Merge origin/main into checkpoint-policy-fixes · Entire

Home

Log in

Merge origin/main into checkpoint-policy-fixes

d958fb7→main·

pfleidi·2w ago·37 files·+3,805 added/-582 removed

Sessions

6f768b8c2fafView transcript

?\ Enforce Checkpoint Policies in CLICodex·GPT-5.5·2 steps

Changes

37

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155

name: E2E Checkpoint Store

on:
  workflow_dispatch:
    inputs:
      agent:
        description: "Agent to run (leave empty to run all real agents; vogon must be selected explicitly)"
        required: false
        type: choice
        options:
          - ""
          - vogon
          - claude-code
          - opencode
          - gemini-cli
          - factoryai-droid
          - cursor-cli
          - copilot-cli
          - roger-roger
          - codex
      checkpoint_store:
        description: "Checkpoint storage backend to run the suite against"
        required: true
        default: "git-refs"
        type: choice
        options:
          - git-branch
          - git-refs

jobs:
  # Build the agent matrix dynamically: a single selected agent, or all of them
  # when the input is left empty.
  matrix-setup:
    runs-on: ubuntu-latest
    outputs:
      agents: ${{ steps.set.outputs.agents }}
    steps:
      - id: set
        # Pass the input through env rather than interpolating it into the script:
        # `type: choice` only constrains the UI, so a REST-API dispatch could inject
        # shell metacharacters here otherwise.
        env:
          AGENT_INPUT: ${{ github.event.inputs.agent }}
        run: |
          input="$AGENT_INPUT"
          if [ -n "$input" ]; then
            echo "agents=[\"$input\"]" >> "$GITHUB_OUTPUT"
          else
            echo 'agents=["claude-code","opencode","gemini-cli","factoryai-droid","cursor-cli","copilot-cli","roger-roger","codex"]' >> "$GITHUB_OUTPUT"
          fi

e2e-checkpoint-store:
    needs: matrix-setup
    runs-on: ubuntu-latest
    timeout-minutes: 40
    permissions:
      actions: read
      contents: read
      # Granted for every leg so copilot-cli works in the matrix; harmless for others.
      copilot-requests: write
    strategy:
      fail-fast: false
      matrix:
        agent: ${{ fromJson(needs.matrix-setup.outputs.agents) }}

steps:
      - name: Checkout repository
        uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
        with:
          ref: ${{ github.event.pull_request.head.sha || github.sha }}

- name: Install system dependencies
        run: |
          sudo apt-get update
          sudo apt-get install -y gnome-keyring tmux
          echo 'somecredstorepass' | gnome-keyring-daemon --unlock

- name: Setup mise
        uses: jdx/mise-action@e6a8b3978addb5a52f2b4cd9d91eafa7f0ab959d # v4

- name: Build entire CLI
        run: go build -o /usr/local/bin/entire ./cmd/entire

- name: Build vogon binary
        if: matrix.agent == 'vogon'
        run: go build -o /usr/local/bin/vogon ./e2e/vogon

- name: Install agent CLI
        if: matrix.agent != 'vogon'
        run: |
          case "${{ matrix.agent }}" in
            claude-code) curl -fsSL https://claude.ai/install.sh | bash ;;
            opencode)    curl -fsSL https://opencode.ai/install | bash ;;
            gemini-cli)  npm install -g @google/gemini-cli ;;
            codex)       npm install -g @openai/codex ;;
            cursor-cli)  curl https://cursor.com/install -fsS | bash ;;
            factoryai-droid) curl -fsSL https://app.factory.ai/cli | sh ;;
            copilot-cli)  npm install -g @github/copilot  ;;
            roger-roger) ;; # installed by mise (see mise.toml)
          esac
          echo "$HOME/.local/bin" >> $GITHUB_PATH

- name: Verify roger-roger agent
        if: matrix.agent == 'roger-roger'
        run: |
          set -euo pipefail
          echo "Verifying roger-roger binaries on PATH..."
          command -v roger-roger
          command -v entire-agent-roger-roger

- name: Bootstrap agent
        if: matrix.agent != 'roger-roger' && matrix.agent != 'vogon'
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
          FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
          COPILOT_GITHUB_TOKEN: ${{ github.token }}
        run: go run ./e2e/bootstrap ${{ matrix.agent }}

- name: E2E Checkpoint Store
        env:
          ANTHROPIC_API_KEY: ${{ secrets.ANTHROPIC_API_KEY }}
          GEMINI_API_KEY: ${{ secrets.GEMINI_API_KEY }}
          OPENAI_API_KEY: ${{ secrets.OPENAI_API_KEY }}
          # Pin per-agent models and concurrency limits to match e2e.yml so this
          # workflow tests the same versions and avoids per-agent rate limits.
          E2E_CODEX_MODEL: ${{ matrix.agent == 'codex' && 'gpt-5.4-mini' || '' }}
          E2E_GEMINI_MODEL: ${{ matrix.agent == 'gemini-cli' && 'gemini-3.1-flash-lite' || '' }}
          CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
          FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
          COPILOT_GITHUB_TOKEN: ${{ github.token }}
          E2E_CONCURRENT_TEST_LIMIT: ${{ matrix.agent == 'gemini-cli' && '6' || matrix.agent == 'factoryai-droid' && '1' || matrix.agent == 'cursor-cli' && '2' || '' }}
          E2E_ARTIFACT_DIR: ${{ github.workspace }}/e2e-artifacts
          E2E_ENTIRE_BIN: /usr/local/bin/entire
          E2E_CHECKPOINT_STORE: ${{ inputs.checkpoint_store }}
        # roger-roger is deterministic, so it runs through its dedicated task,
        # which does NOT enable --rerun-fails. Routing it through the default
        # task (`test:e2e`) would retry a real regression and mask it.
        run: |
          mkdir -p "$E2E_ARTIFACT_DIR"
          if [ "${{ matrix.agent }}" = "roger-roger" ]; then
            mise run test:e2e:roger-roger TestExternalAgent
          else
            mise run test:e2e --agent ${{ matrix.agent }}
          fi

- name: Upload artifacts
        if: always()
        uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7
        with:
          name: e2e-checkpoint-store-${{ matrix.agent }}-${{ inputs.checkpoint_store }}
          path: e2e-artifacts/
          retention-days: 7

A.github/workflows/e2e-checkpoint-store.yml+155

107 unmodified lines

108
109
110
111
112
111
112
113
114
115
116
117
118
119
120
121
122
123
12 unmodified lines

136
137
138
131
132
139
140
141
142
143
144
145
146
147
148
136
137
138
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
2 unmodified lines

202
203
204
147
205
206
149
150
151
152
153
154
155
156
157
207
208
209
159
160
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226

107 unmodified lines

CURSOR_API_KEY: ${{ secrets.CURSOR_API_KEY }}
          FACTORY_API_KEY: ${{ secrets.FACTORY_API_KEY }}
          COPILOT_GITHUB_TOKEN: ${{ github.token }}
          E2E_CONCURRENT_TEST_LIMIT: ${{ matrix.agent == 'gemini-cli' && '6' || matrix.agent == 'factoryai-droid' && '1' || '' }}
        run: mise run test:e2e --agent ${{ matrix.agent }} ${{ matrix.agent == 'roger-roger' && 'TestExternalAgent' || '' }}
          E2E_CONCURRENT_TEST_LIMIT: ${{ matrix.agent == 'gemini-cli' && '6' || matrix.agent == 'factoryai-droid' && '1' || matrix.agent == 'cursor-cli' && '2' || '' }}
        # roger-roger is deterministic, so it runs through its dedicated task,
        # which does NOT enable --rerun-fails. Routing it through the default
        # task (`test:e2e`) would retry a real regression and mask it.
        run: |
          if [ "${{ matrix.agent }}" = "roger-roger" ]; then
            mise run test:e2e:roger-roger TestExternalAgent
          else
            mise run test:e2e --agent ${{ matrix.agent }}
          fi

- name: Upload artifacts
        if: always()
12 unmodified lines

needs: [e2e-tests, e2e-windows]
    if: ${{ always() && (needs.e2e-tests.result == 'failure' || needs.e2e-windows.result == 'failure') && github.event_name == 'push' }}
    steps:
      - name: Get failed agents
        id: failed
      # Classify the failed jobs into two severities:
      #   RED    — a "reliable" agent (or Windows) failed. These pass on every
      #            healthy run, so a failure here is a real regression.
      #   YELLOW — only a known-flaky agent failed. Worth investigating, but not
      #            a `main` regression. Reported so we don't lose visibility.
      - name: Classify failures
        id: classify
        env:
          GH_TOKEN: ${{ github.token }}
        run: |
          failed=$(gh api repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs \
            --jq '[.jobs[] | select(.conclusion == "failure") | .name | if test("\\(") then capture("\\((?<agent>[^)]+)\\)") | .agent else . end] | join(", ")')
          echo "agents=$failed" >> "$GITHUB_OUTPUT"
          set -euo pipefail
          # Reliable tier (RED). The e2e-windows reusable job is also RED but
          # is matched separately below since it has no "(agent)" suffix.
          reliable="claude-code opencode gemini-cli roger-roger"

failed_names=$(gh api --paginate \
            "repos/${{ github.repository }}/actions/runs/${{ github.run_id }}/jobs" \
            --jq '.jobs[] | select(.conclusion == "failure") | .name')

red=""
          yellow=""
          while IFS= read -r name; do
            [ -z "$name" ] && continue
            case "$name" in
              *e2e-windows*) red="$red windows"; continue ;;
            esac
            # "e2e-tests (cursor-cli)" -> "cursor-cli"
            agent="${name#*(}"; agent="${agent%)*}"
            case " $reliable " in
              *" $agent "*) red="$red $agent" ;;
              *)            yellow="$yellow $agent" ;;
            esac
          done <<EOF
          $failed_names
          EOF

# Each list was built as " a b c" (leading space, space-separated).
          # Strip the leading space, then join on ", ". Empty stays empty.
          red="${red# }";       red="${red// /, }"
          yellow="${yellow# }"; yellow="${yellow// /, }"

if [ -n "$red" ]; then
            color="#d50200"
            header=":red_circle: *E2E Tests Failed* on \`main\`"
          else
            color="#daa038"
            header=":large_yellow_circle: *E2E flaky-agent failures* on \`main\`"
          fi

body=""
          [ -n "$red" ]    && body="${body}Reliable agents (regression): *${red}*\\n"
          [ -n "$yellow" ] && body="${body}Flaky agents (investigate): *${yellow}*\\n"

{
            echo "color=$color"
            echo "header=$header"
            echo "body=$body"
          } >> "$GITHUB_OUTPUT"

- name: Notify Slack of E2E failure
        uses: slackapi/slack-github-action@45a88b9581bfab2566dc881e2cd66d334e621e2c # v3.0.3
2 unmodified lines

webhook-type: incoming-webhook
          payload: |
            {
              "blocks": [\
              "attachments": [\
                {\
                  "type": "section",\
                  "text": {\
                    "type": "mrkdwn",\
                    "text": ":red_circle: *E2E Tests Failed* on `main`\n\nFailed agents: *${{ steps.failed.outputs.agents }}*\n<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run details>"\
                  }\
                },\
                {\
                  "type": "context",\
                  "elements": [\
                  "color": "${{ steps.classify.outputs.color }}",\
                  "blocks": [\
                    {\
                      "type": "mrkdwn",\
                      "text": "Commit: <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> by ${{ github.actor }}"\
                      "type": "section",\
                      "text": {\
                        "type": "mrkdwn",\
                        "text": "${{ steps.classify.outputs.header }}\n\n${{ steps.classify.outputs.body }}<${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}|View run details>"\
                      }\
                    },\
                    {\
                      "type": "context",\
                      "elements": [\
                        {\
                          "type": "mrkdwn",\
                          "text": "Commit: <${{ github.server_url }}/${{ github.repository }}/commit/${{ github.sha }}|${{ github.sha }}> by ${{ github.actor }}"\
                        }\
                      ]\
                    }\
                  ]\
                }\
```\
\
M.github/workflows/e2e.yml+82/-19\
\
```\
93 unmodified lines\
\
94\
95\
96\
97\
97\
98\
99\
100\
\
93 unmodified lines\
\
          fi\
\
      - name: Run GoReleaser\
        uses: goreleaser/goreleaser-action@5daf1e915a5f0af01ddbcd89a43b8061ff4f1a89 # v7\
        uses: goreleaser/goreleaser-action@f06c13b6b1a9625abc9e6e439d9c05a8f2190e94 # v7\
        with:\
          distribution: goreleaser-pro\
          version: latest\
```\
\
M.github/workflows/release.yml+1/-1\
\
```\
4 unmodified lines\
\
5\
6\
7\
8\
9\
10\
11\
12\
13\
14\
15\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
31\
32\
33\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
44\
45\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
61\
62\
\
4 unmodified lines\
\
The format is based on [Keep a Changelog](https://keepachangelog.com/),\
and this project adheres to [Semantic Versioning](https://semver.org/).\
\
## [0.7.8] - 2026-06-30\
\
### Added\
\
- `entire import` brings sessions created before Entire was enabled into checkpoints, with support for Cursor, Pi, Factory, Codex, Copilot, and Gemini ([#1527](https://github.com/entireio/cli/pull/1527), [#1540](https://github.com/entireio/cli/pull/1540))\
- Sessions can now be adopted across repos and worktrees, and ACTIVE sessions whose agent has exited are finalized automatically ([#1472](https://github.com/entireio/cli/pull/1472), [#1488](https://github.com/entireio/cli/pull/1488))\
- Multi-agent review profiles for `entire review` ([#1312](https://github.com/entireio/cli/pull/1312))\
- OpenAI Privacy Filter with a pre-push redaction architecture ([#1214](https://github.com/entireio/cli/pull/1214))\
- Codex token-usage diagnostics ([#1393](https://github.com/entireio/cli/pull/1393))\
- A `.worktreeinclude` file for controlling worktree contents ([#1517](https://github.com/entireio/cli/pull/1517))\
- `entire repo clone`, a `visibility` verb, and `entire repo mirror list --show-available` for working with mirrored repositories ([#1529](https://github.com/entireio/cli/pull/1529), [#1531](https://github.com/entireio/cli/pull/1531), [#1490](https://github.com/entireio/cli/pull/1490))\
- Control-plane CLI gained org/project `get`+`delete`, repo grant `list`+`remove`, and friendly-name resolution ([#1499](https://github.com/entireio/cli/pull/1499), [#1498](https://github.com/entireio/cli/pull/1498))\
- Hidden checkpoint policy command plus repo-level checkpoint policy enforcement ([#1508](https://github.com/entireio/cli/pull/1508), [#1509](https://github.com/entireio/cli/pull/1509))\
\
### Changed\
\
- Checkpoints now record storage-version metadata, run format-compatibility checks, and surface a `compact_transcript` path in `metadata.json`; the compact `transcript.jsonl` is stored and pushed in v1 checkpoints ([#1494](https://github.com/entireio/cli/pull/1494), [#1507](https://github.com/entireio/cli/pull/1507), [#1515](https://github.com/entireio/cli/pull/1515), [#1419](https://github.com/entireio/cli/pull/1419))\
- `entire explain` enriches its JSON summary and surfaces list truncation ([#1560](https://github.com/entireio/cli/pull/1560))\
- The CLI sends a versioned `User-Agent` (`entire-cli/{version}`) and collects the installed git version in telemetry ([#1489](https://github.com/entireio/cli/pull/1489), [#1520](https://github.com/entireio/cli/pull/1520))\
- Connect timeouts were loosened for slow links ([#1487](https://github.com/entireio/cli/pull/1487))\
- The Entire search skill is now opt-in ([#1521](https://github.com/entireio/cli/pull/1521))\
- The Pi extension was updated for context injection ([#1469](https://github.com/entireio/cli/pull/1469))\
- Refined checkpoint version-policy error handling ([#1528](https://github.com/entireio/cli/pull/1528))\
\
### Fixed\
\
- Copilot no longer creates phantom sessions for subagent turns ([#1578](https://github.com/entireio/cli/pull/1578))\
- Fixed Cursor hook misattribution and added Cursor token-usage support ([#1263](https://github.com/entireio/cli/pull/1263))\
- `entire review` no longer wedges multi-agent runs on "Finalizing output..." ([#1561](https://github.com/entireio/cli/pull/1561))\
- Attribution `why` prompts are now honest, with unified blame/why line syntax ([#1535](https://github.com/entireio/cli/pull/1535))\
- `entire grant` resolves repos by name, supports `github:` handle grantees, and returns a repo clone URL ([#1549](https://github.com/entireio/cli/pull/1549))\
- Control-plane commands now display the core a request actually dials, and cluster-addressed `repo mirror` commands route to the cluster's core ([#1478](https://github.com/entireio/cli/pull/1478), [#1475](https://github.com/entireio/cli/pull/1475))\
- Stopped shallow-fetching the metadata tip, fixing a false "disconnected" state ([#1443](https://github.com/entireio/cli/pull/1443))\
- ULID checkpoint IDs are now recognized alongside legacy hex ([#1546](https://github.com/entireio/cli/pull/1546))\
- `entire activity` truncates repo names rune-safely in the repo chart ([#1473](https://github.com/entireio/cli/pull/1473))\
- OPF prompt defaults are ordered correctly, and OPF progress is routed to stderr instead of `/dev/tty` ([#1464](https://github.com/entireio/cli/pull/1464), [#1470](https://github.com/entireio/cli/pull/1470))\
- Fixed escaping in help text ([#1553](https://github.com/entireio/cli/pull/1553))\
\
### Housekeeping\
\
- Major checkpoint-storage refactor toward pluggable stores: split into persistent/ephemeral stores with generic read/write, a store registry + topology, an `Open` factory facade, unified committed writes behind `Store.Write`, an extracted persistent contract under `api/checkpoint`, and a `treeWriter` for checkpoint writes; removed the checkpoints v1.1 mirror machinery ([#1451](https://github.com/entireio/cli/pull/1451), [#1480](https://github.com/entireio/cli/pull/1480), [#1481](https://github.com/entireio/cli/pull/1481), [#1495](https://github.com/entireio/cli/pull/1495), [#1504](https://github.com/entireio/cli/pull/1504), [#1533](https://github.com/entireio/cli/pull/1533), [#1556](https://github.com/entireio/cli/pull/1556), [#1454](https://github.com/entireio/cli/pull/1454), [#1482](https://github.com/entireio/cli/pull/1482))\
- Added a manual-dispatch `e2e-checkpoint-store` workflow and tackled failing E2E tests ([#1567](https://github.com/entireio/cli/pull/1567), [#1577](https://github.com/entireio/cli/pull/1577))\
- De-flaked the ColdPathFailover redirect-target test and stopped `resolvePushSettings` tests from fetching github.com ([#1574](https://github.com/entireio/cli/pull/1574), [#1463](https://github.com/entireio/cli/pull/1463))\
- Removed the `ireturn` lint and its directives, and refreshed/regenerated the Core API OpenAPI spec and client ([#1548](https://github.com/entireio/cli/pull/1548), [#1502](https://github.com/entireio/cli/pull/1502), [#1518](https://github.com/entireio/cli/pull/1518), [#1512](https://github.com/entireio/cli/pull/1512))\
- `mise run dev:publish` now always installs to `~/go/bin` (ignoring stray `$GOBIN`) and allows a custom target directory ([#1550](https://github.com/entireio/cli/pull/1550), [#1543](https://github.com/entireio/cli/pull/1543))\
- Docs: README now mentions tap trust, and the Codex hooks feature-flag docs were updated ([#1534](https://github.com/entireio/cli/pull/1534), [#1545](https://github.com/entireio/cli/pull/1545))\
- Dependency bumps ([#1564](https://github.com/entireio/cli/pull/1564), [#1563](https://github.com/entireio/cli/pull/1563), [#1526](https://github.com/entireio/cli/pull/1526), [#1525](https://github.com/entireio/cli/pull/1525), [#1524](https://github.com/entireio/cli/pull/1524), [#1516](https://github.com/entireio/cli/pull/1516), [#1485](https://github.com/entireio/cli/pull/1485), [#1467](https://github.com/entireio/cli/pull/1467), [#1466](https://github.com/entireio/cli/pull/1466), [#1465](https://github.com/entireio/cli/pull/1465))\
\
### Thanks\
\
Thanks to @SnowingFox for fixing Cursor hook misattribution and adding Cursor token-usage support, @suhaanthayyil for honest `why` prompts and unified blame/why line syntax, @Mohit-Katyal for rune-safe repo-name truncation in the activity chart, and @ronaldtebrake for updating the Codex hooks feature-flag docs!\
\
## [0.7.7] - 2026-06-18\
\
### Added\
```\
\
MCHANGELOG.md+52\
\
```\
28 unmodified lines\
\
29\
30\
31\
32\
32\
33\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
\
28 unmodified lines\
\
discoverable through `entire labs` and may remain hidden from root help while\
their canonical paths are still runnable.\
\
- `session` (alias: `sessions`): `list`, `info`, `tokens`, `stop`, `attach`, `resume`, `current`.\
- `session` (alias: `sessions`): `list`, `info`, `tokens`, `stop`, `attach`, `adopt`, `resume`, `current`.\
  `resume` with a branch arg switches to it and resumes its session; with no arg\
  it opens an interactive picker of stopped sessions (across all worktrees),\
  resolving each to its branch and pointing at the owning worktree when the\
  branch is checked out elsewhere. Resume keeps an existing local session log\
  as-is by default (`--force` overwrites it from the checkpoint).\
  `adopt` moves an active session from another repo or worktree into the current\
  worktree and resets target-local checkpoint bookkeeping so future commits link\
  to the adopted session from the new location.\
- `checkpoint` (aliases: `cp`, `checkpoints`): `list`, `explain`, `tokens`, `search`, plus\
  the deprecated `rewind` (functional, prints a cobra deprecation message, will\
  be removed in a future release)\
```\
\
MCLAUDE.md+4/-1\
\
```\
3 unmodified lines\
\
4\
5\
6\
7\
8\
9\
10\
11\
12\
13\
14\
15\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
26\
27\
53 unmodified lines\
\
81\
82\
83\
84\
85\
86\
87\
88\
89\
90\
91\
92\
93\
94\
95\
96\
97\
98\
99\
100\
\
3 unmodified lines\
\
    "context"\
    "fmt"\
    "io"\
    "strings"\
\
    "github.com/entireio/cli/cmd/entire/cli/agent"\
    "github.com/entireio/cli/cmd/entire/cli/logging"\
)\
\
// subagentSessionIDPrefix is the prefix Copilot uses when it reuses a Task\
// tool-use id as the sessionId on lifecycle hooks fired for a subagent turn.\
// Real Copilot session ids are UUIDs, so this prefix unambiguously marks a\
// subagent context (e.g. "toolu_bdrk_01K…" on Bedrock-backed models).\
const subagentSessionIDPrefix = "toolu_"\
\
// isSubagentSessionID reports whether a Copilot sessionId is actually a\
// subagent's tool-use id rather than a real interactive session id.\
func isSubagentSessionID(sessionID string) bool {\
    return strings.HasPrefix(sessionID, subagentSessionIDPrefix)\
}\
\
// Ensure CopilotCLIAgent implements HookSupport at compile time.\
var _ agent.HookSupport = (*CopilotCLIAgent)(nil)\
\
53 unmodified lines\
\
        }\
    }\
\
    // Copilot fires the per-turn/session lifecycle hooks for subagent turns too,\
    // using the subagent's Task tool-use id (e.g. "toolu_…") as the sessionId.\
    // Those must NOT spin up a top-level Entire session: the subagent never gets\
    // a matching stop for that id, so the phantom session would stay "active"\
    // forever and pin its shadow branch open after the user commits. The\
    // subagent's work is still captured via the main session's subagentStop →\
    // task checkpoint path, so we drop only the session-lifecycle hooks here and\
    // leave subagentStop itself to run.\
    if hookName != HookNameSubagentStop && isSubagentSessionID(env.SessionID) {\
        logging.Debug(ctx, "copilot-cli: skipping lifecycle event for subagent session",\
            "sessionID", env.SessionID, "hook", hookName)\
        return nil, nil //nolint:nilnil // Subagent lifecycle hook — no top-level session action.\
    }\
\
    switch hookName {\
    case HookNameUserPromptSubmitted:\
        return c.buildUserPromptSubmitted(ctx, env), nil\
```\
\
Mcmd/entire/cli/agent/copilotcli/lifecycle.go+27\
\
```\
357 unmodified lines\
\
358\
359\
360\
361\
362\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
375\
376\
377\
378\
379\
380\
381\
382\
383\
384\
385\
386\
387\
388\
389\
390\
391\
392\
393\
394\
395\
396\
397\
398\
399\
400\
401\
402\
403\
404\
405\
406\
407\
408\
409\
410\
\
357 unmodified lines\
\
    }\
}\
\
// TestParseHookEvent_SubagentSession_LifecycleHooksReturnNil verifies that the\
// per-turn/session lifecycle hooks are dropped when Copilot fires them for a\
// subagent turn (sessionId is a Task tool-use id, e.g. "toolu_…"). Otherwise a\
// phantom top-level session is created that never ends and pins its shadow\
// branch open after the user commits.\
func TestParseHookEvent_SubagentSession_LifecycleHooksReturnNil(t *testing.T) {\
    t.Parallel()\
\
    const subagentSessionID = "toolu_bdrk_01KTyZvJLaUtkjgvdA355rMX"\
    ag := &CopilotCLIAgent{}\
\
    lifecycleHooks := []string{\
        HookNameUserPromptSubmitted,\
        HookNameSessionStart,\
        HookNameAgentStop,\
        HookNameSessionEnd,\
    }\
\
    for _, hookName := range lifecycleHooks {\
        t.Run(hookName, func(t *testing.T) {\
            t.Parallel()\
            input := `{"timestamp":1771480081360,"cwd":"/path/to/repo","sessionId":"` + subagentSessionID + `","prompt":"hi"}`\
\
            event, err := ag.ParseHookEvent(context.Background(), hookName, strings.NewReader(input))\
\
            require.NoError(t, err)\
            require.Nil(t, event, "expected nil event for subagent-session lifecycle hook %s", hookName)\
        })\
    }\
}\
\
// TestParseHookEvent_SubagentSession_SubagentStopStillFires verifies the\
// subagent-stop hook is NOT dropped — it always carries the main session id and\
// drives the task-checkpoint path.\
func TestParseHookEvent_SubagentSession_SubagentStopStillFires(t *testing.T) {\
    t.Parallel()\
\
    ag := &CopilotCLIAgent{}\
    input := `{"timestamp":1771480085412,"cwd":"/path/to/repo","sessionId":"` + testSessionID + `"}`\
\
    event, err := ag.ParseHookEvent(context.Background(), HookNameSubagentStop, strings.NewReader(input))\
\
    require.NoError(t, err)\
    require.NotNil(t, event, "subagent-stop must still produce an event")\
    require.Equal(t, agent.SubagentEnd, event.Type)\
}\
\
func TestParseHookEvent_PassthroughHooks_ReturnNil(t *testing.T) {\
    t.Parallel()\
```\
\
Mcmd/entire/cli/agent/copilotcli/lifecycle\_test.go+47\
\
```\
81 unmodified lines\
\
82\
83\
84\
85\
86\
87\
88\
89\
90\
85\
86\
87\
88\
89\
90\
91\
94\
95\
96\
97\
98\
99\
100\
101\
102\
103\
92\
93\
94\
95\
105\
106\
107\
96\
97\
98\
99\
100\
111\
112\
101\
102\
103\
104\
11 unmodified lines\
\
116\
117\
118\
130\
131\
132\
133\
134\
119\
120\
121\
122\
123\
124\
125\
136\
126\
127\
138\
128\
129\
130\
142\
131\
132\
133\
134\
146\
147\
135\
136\
137\
138\
139\
149\
140\
141\
142\
143\
144\
152\
153\
145\
146\
147\
148\
149\
150\
151\
152\
153\
154\
155\
156\
157\
158\
159\
160\
159\
160\
161\
162\
163\
164\
165\
166\
167\
168\
169\
161\
162\
163\
164\
165\
166\
167\
168\
169\
170\
171\
172\
173\
174\
171\
175\
173\
174\
175\
176\
177\
178\
179\
180\
181\
182\
180\
181\
183\
184\
185\
186\
187\
188\
189\
185\
190\
191\
192\
193\
194\
195\
196\
197\
198\
199\
200\
201\
202\
203\
204\
205\
206\
207\
208\
209\
210\
211\
212\
213\
214\
215\
216\
217\
218\
219\
220\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
231\
232\
233\
234\
235\
236\
237\
238\
239\
240\
241\
242\
243\
244\
245\
246\
247\
248\
249\
250\
251\
252\
253\
254\
255\
256\
257\
258\
259\
260\
261\
262\
263\
264\
265\
266\
267\
268\
269\
270\
271\
272\
273\
274\
275\
276\
277\
278\
279\
280\
281\
282\
283\
284\
285\
286\
287\
288\
289\
290\
291\
292\
293\
294\
295\
296\
297\
298\
299\
300\
301\
302\
303\
304\
305\
306\
307\
308\
309\
310\
311\
312\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
324\
325\
326\
327\
328\
329\
330\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
350\
351\
352\
353\
354\
355\
356\
357\
358\
359\
360\
361\
362\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
375\
376\
377\
378\
379\
380\
381\
382\
383\
384\
385\
386\
387\
388\
389\
390\
391\
392\
393\
394\
395\
396\
397\
398\
399\
400\
401\
402\
403\
404\
405\
406\
407\
408\
409\
410\
411\
412\
413\
414\
415\
416\
417\
418\
419\
420\
421\
422\
423\
424\
425\
426\
427\
428\
429\
430\
431\
432\
433\
434\
435\
436\
437\
190\
438\
439\
440\
441\
3 unmodified lines\
\
445\
446\
447\
200\
448\
449\
450\
451\
24 unmodified lines\
\
476\
477\
478\
231\
479\
480\
481\
482\
64 unmodified lines\
\
547\
548\
549\
302\
550\
551\
552\
553\
82 unmodified lines\
\
636\
637\
638\
391\
639\
640\
641\
642\
643\
396\
644\
645\
646\
647\
90 unmodified lines\
\
738\
739\
740\
493\
741\
742\
743\
744\
745\
746\
747\
500\
501\
502\
503\
748\
749\
750\
68 unmodified lines\
\
819\
820\
821\
578\
579\
580\
822\
823\
824\
825\
584\
585\
586\
587\
588\
589\
590\
591\
826\
827\
593\
828\
829\
595\
830\
597\
598\
599\
600\
601\
602\
603\
604\
605\
606\
607\
608\
609\
610\
611\
831\
832\
833\
834\
10 unmodified lines\
\
845\
846\
847\
628\
848\
849\
850\
851\
21 unmodified lines\
\
873\
874\
875\
656\
876\
877\
878\
879\
52 unmodified lines\
\
932\
933\
934\
715\
935\
936\
937\
938\
30 unmodified lines\
\
969\
970\
971\
752\
972\
973\
974\
975\
107 unmodified lines\
\
1083\
1084\
1085\
866\
1086\
1087\
1088\
1089\
117 unmodified lines\
\
1207\
1208\
1209\
990\
1210\
1211\
1212\
1213\
1214\
1215\
1216\
997\
1217\
1218\
1219\
1220\
433 unmodified lines\
\
1654\
1655\
1656\
1437\
1438\
1439\
1440\
1657\
1658\
1659\
1660\
1444\
1445\
1446\
1447\
1448\
1449\
1450\
1451\
1452\
1661\
1662\
1454\
1663\
1664\
1665\
1457\
1458\
1459\
1460\
1461\
1462\
1463\
1464\
1465\
1466\
1467\
1468\
1469\
1470\
1471\
1472\
1473\
1474\
1475\
1476\
1477\
1478\
1479\
1480\
1481\
1482\
1483\
1484\
1485\
1486\
1487\
1488\
1489\
1490\
1666\
1667\
1668\
1669\
1670\
1671\
1496\
1672\
1673\
1674\
1675\
26 unmodified lines\
\
1702\
1703\
1704\
1529\
1530\
1531\
1532\
1705\
1706\
1707\
1708\
1536\
1537\
1538\
1539\
1540\
1541\
1542\
1543\
1544\
1709\
1710\
1546\
1711\
1712\
1548\
1549\
1550\
1713\
1552\
1553\
1554\
1555\
1556\
1557\
1558\
1559\
1560\
1561\
1562\
1563\
1564\
1565\
1566\
1567\
1568\
1569\
1570\
1571\
1572\
1573\
1574\
1575\
1576\
1577\
1578\
1579\
1580\
1581\
1582\
1583\
1584\
1585\
1586\
1587\
1588\
1589\
1590\
1591\
1592\
1593\
1594\
1595\
1596\
1597\
1598\
1599\
1600\
1601\
1602\
1603\
1604\
1605\
1606\
1607\
1608\
1609\
1610\
1611\
1612\
1613\
1614\
1615\
1616\
1617\
1618\
1619\
1620\
1621\
1622\
1623\
1624\
1625\
1626\
1627\
1628\
1629\
1630\
1631\
1632\
1633\
1634\
1635\
1636\
1637\
1638\
1639\
1640\
1641\
1642\
1643\
1644\
1645\
1714\
1715\
1716\
1717\
12 unmodified lines\
\
1730\
1731\
1732\
1664\
1733\
1734\
1735\
1736\
35 unmodified lines\
\
1772\
1773\
1774\
1706\
1775\
1776\
1777\
1778\
236 unmodified lines\
\
2015\
2016\
2017\
1949\
2018\
2019\
2020\
2021\
\
81 unmodified lines\
\
        return err\
    }\
\
    // Use sharded path: <id[:2]>/<id[2:]>/\
    basePath := opts.CheckpointID.Path() + "/"\
    checkpointPath := opts.CheckpointID.Path()\
\
    // Flatten only the checkpoint subtree (O(files in checkpoint))\
    entries, err := s.flattenCheckpointEntries(rootTreeHash, checkpointPath)\
    // Build the new checkpoint subtree from its current state on the v1 branch,\
    // then splice it back at the shard path. basePath keeps the v1 sharded layout\
    // so stored session-file pointers stay /<shard>/<id>/<n>/... as before.\
    existing, err := s.subtreeObjAt(rootTreeHash, opts.CheckpointID.Path())\
    if err != nil {\
        return err\
    }\
\
    // Track task metadata path for commit trailer\
    var taskMetadataPath string\
\
    // Handle task checkpoints\
    if opts.IsTask && opts.ToolUseID != "" {\
        taskMetadataPath, err = s.writeTaskCheckpointEntries(ctx, opts, basePath, entries)\
        if err != nil {\
            return err\
        }\
    checkpointVersion := CheckpointVersionBranchV1\
    if opts.CheckpointVersion != "" {\
        checkpointVersion = opts.CheckpointVersion\
    }\
\
    // Write standard checkpoint entries (transcript, prompts, context, metadata)\
    if err := s.writeStandardCheckpointEntries(ctx, opts, basePath, entries); err != nil {\
    checkpointSubtree, taskMetadataPath, err := s.applySessionWrite(ctx, opts, existing, opts.CheckpointID.Path()+"/", checkpointVersion)\
    if err != nil {\
        return err\
    }\
\
    // Build checkpoint subtree and splice into root (O(depth) tree surgery)\
    newTreeHash, err := s.spliceCheckpointSubtree(ctx, rootTreeHash, opts.CheckpointID, basePath, entries)\
    newTreeHash, err := s.spliceCheckpointSubtree(rootTreeHash, opts.CheckpointID, checkpointSubtree)\
    if err != nil {\
        return err\
    }\
11 unmodified lines\
\
    return s.setPrimaryRef(newCommitHash)\
}\
\
// flattenCheckpointEntries reads only the entries under a specific checkpoint path\
// from the sessions branch tree. This is O(files in checkpoint) instead of O(all checkpoints).\
// Returns an empty map if the checkpoint doesn't exist yet.\
func (s *GitStore) flattenCheckpointEntries(rootTreeHash plumbing.Hash, checkpointPath string) (map[string]object.TreeEntry, error) {\
    entries := make(map[string]object.TreeEntry)\
// subtreeObjAt returns the tree object for one checkpoint's subtree within a root\
// tree, or (nil, nil) when the root or the checkpoint path does not exist yet.\
// path is the in-tree checkpoint path (e.g. "a3/b2c4d5e6f7"); pass "" to return\
// the root tree itself (the per-checkpoint-ref layout, where the whole tree is\
// the checkpoint subtree).\
func (s *treeWriter) subtreeObjAt(rootTreeHash plumbing.Hash, path string) (*object.Tree, error) {\
    if rootTreeHash == plumbing.ZeroHash {\
        return entries, nil\
        return nil, nil //nolint:nilnil // absent checkpoint (no tree yet), not an error\
    }\
\
    rootTree, err := s.repo.TreeObject(rootTreeHash)\
    if err != nil {\
        if errors.Is(err, plumbing.ErrObjectNotFound) {\
            return entries, nil // Tree doesn't exist yet\
            return nil, nil //nolint:nilnil // tree doesn't exist yet, not an error\
        }\
        return nil, fmt.Errorf("failed to read root tree %s: %w", rootTreeHash, err)\
    }\
\
    subtree, err := rootTree.Tree(checkpointPath)\
    if path == "" {\
        return rootTree, nil\
    }\
    subtree, err := rootTree.Tree(path)\
    if err != nil {\
        return entries, nil //nolint:nilerr // Checkpoint doesn't exist yet\
        return nil, nil //nolint:nilnil,nilerr // checkpoint doesn't exist yet, not an error\
    }\
    return subtree, nil\
}\
\
    // Flatten just this subtree with the full path prefix\
    if err := FlattenTree(s.repo, subtree, checkpointPath, entries); err != nil {\
// flattenExisting flattens a checkpoint's current subtree into a path->entry map\
// keyed under basePath, so the per-checkpoint write helpers (which build paths as\
// basePath+"<n>/<file>") see the existing files. basePath is "" for the\
// per-checkpoint-ref layout or "<shard>/<id>/" for the v1 branch layout. A nil\
// subtree (new checkpoint) yields an empty map.\
func (s *treeWriter) flattenExisting(existing *object.Tree, basePath string) (map[string]object.TreeEntry, error) {\
    entries := make(map[string]object.TreeEntry)\
    if existing == nil {\
        return entries, nil\
    }\
    if err := FlattenTree(s.repo, existing, strings.TrimSuffix(basePath, "/"), entries); err != nil {\
        return nil, err\
    }\
    return entries, nil\
}\
\
// spliceCheckpointSubtree builds a tree from checkpoint-local entries and installs it\
// at the correct shard location in the root tree using O(depth) tree surgery.\
// basePath is like "a3/b2c4d5e6f7/" (with trailing slash).\
// Returns the new root tree hash.\
func (s *GitStore) spliceCheckpointSubtree(ctx context.Context, rootTreeHash plumbing.Hash, checkpointID id.CheckpointID, basePath string, entries map[string]object.TreeEntry) (plumbing.Hash, error) {\
    // Convert entries to relative paths (strip basePath prefix)\
    relEntries := make(map[string]object.TreeEntry, len(entries))\
    for path, entry := range entries {\
        relPath := strings.TrimPrefix(path, basePath)\
        if relPath == path {\
            continue // Entry doesn't have the expected prefix\
// buildCheckpointSubtree builds the checkpoint subtree object from entries keyed\
// under basePath, stripping the prefix so the subtree is rooted at the checkpoint\
// directory. With basePath "" the entries are already root-relative.\
func (s *treeWriter) buildCheckpointSubtree(ctx context.Context, entries map[string]object.TreeEntry, basePath string) (plumbing.Hash, error) {\
    relEntries := entries\
    if basePath != "" {\
        relEntries = make(map[string]object.TreeEntry, len(entries))\
        for path, entry := range entries {\
            relPath := strings.TrimPrefix(path, basePath)\
            if relPath == path {\
                continue // Entry doesn't have the expected prefix\
            }\
            relEntries[relPath] = entry\
        }\
        relEntries[relPath] = entry\
    }\
\
    // Build the checkpoint subtree from relative entries\
    checkpointTreeHash, err := BuildTreeFromEntries(ctx, s.repo, relEntries)\
    subtree, err := BuildTreeFromEntries(ctx, s.repo, relEntries)\
    if err != nil {\
        return plumbing.ZeroHash, fmt.Errorf("failed to build checkpoint subtree: %w", err)\
    }\
    return subtree, nil\
}\
\
    // Splice into root tree at the shard path using tree surgery\
    // Path: ["a3"] with entry "b2c4d5e6f7" pointing to the checkpoint tree\
// spliceCheckpointSubtree installs a prebuilt checkpoint subtree at the shard\
// location in the v1 root tree using O(depth) tree surgery, returning the new\
// root tree hash. The v1 branch always shards on the first two ID characters.\
func (s *GitStore) spliceCheckpointSubtree(rootTreeHash plumbing.Hash, checkpointID id.CheckpointID, checkpointSubtree plumbing.Hash) (plumbing.Hash, error) {\
    shardPrefix := string(checkpointID[:2])\
    shardSuffix := string(checkpointID[2:])\
    return UpdateSubtree(s.repo, rootTreeHash, []string{shardPrefix}, []object.TreeEntry{\
        {Name: shardSuffix, Mode: filemode.Dir, Hash: checkpointTreeHash},\
        {Name: shardSuffix, Mode: filemode.Dir, Hash: checkpointSubtree},\
    }, UpdateSubtreeOptions{MergeMode: MergeKeepExisting})\
}\
\
// applySessionWrite applies a Session write to a checkpoint's current subtree and\
// returns the new checkpoint subtree hash plus the task metadata path (for the\
// commit trailer). It is backing-independent: the v1-branch store passes the\
// sharded basePath and the per-checkpoint-ref store passes "". checkpointVersion\
// is stamped into a freshly written root summary.\
func (s *treeWriter) applySessionWrite(ctx context.Context, opts WriteOptions, existing *object.Tree, basePath, checkpointVersion string) (plumbing.Hash, string, error) {\
    entries, err := s.flattenExisting(existing, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, "", err\
    }\
\
    var taskMetadataPath string\
    if opts.IsTask && opts.ToolUseID != "" {\
        taskMetadataPath, err = s.writeTaskCheckpointEntries(ctx, opts, basePath, entries)\
        if err != nil {\
            return plumbing.ZeroHash, "", err\
        }\
    }\
\
    if err := s.writeStandardCheckpointEntries(ctx, opts, basePath, entries, checkpointVersion); err != nil {\
        return plumbing.ZeroHash, "", err\
    }\
\
    subtree, err := s.buildCheckpointSubtree(ctx, entries, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, "", err\
    }\
    return subtree, taskMetadataPath, nil\
}\
\
// applyAttributionBackfill rewrites the checkpoint root summary's combined\
// attribution on the checkpoint's current subtree, returning the new subtree\
// hash. Returns ErrCheckpointNotFound when the checkpoint has no root summary.\
func (s *treeWriter) applyAttributionBackfill(ctx context.Context, existing *object.Tree, basePath string, combinedAttribution *Attribution) (plumbing.Hash, error) {\
    entries, err := s.flattenExisting(existing, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, err\
    }\
\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return plumbing.ZeroHash, ErrCheckpointNotFound\
    }\
\
    summary, err := s.readSummaryFromBlob(entry.Hash)\
    if err != nil {\
        return plumbing.ZeroHash, fmt.Errorf("failed to read checkpoint summary: %w", err)\
    }\
    summary.CombinedAttribution = combinedAttribution\
\
    metadataJSON, err := jsonutil.MarshalIndentWithNewline(summary, "", "  ")\
    if err != nil {\
        return plumbing.ZeroHash, fmt.Errorf("failed to marshal checkpoint summary: %w", err)\
    }\
    metadataHash, err := CreateBlobFromContent(s.repo, metadataJSON)\
    if err != nil {\
        return plumbing.ZeroHash, fmt.Errorf("failed to create checkpoint summary blob: %w", err)\
    }\
    entries[rootMetadataPath] = object.TreeEntry{\
        Name: rootMetadataPath,\
        Mode: filemode.Regular,\
        Hash: metadataHash,\
    }\
\
    return s.buildCheckpointSubtree(ctx, entries, basePath)\
}\
\
// applySummaryBackfill rewrites the latest session's summary on the checkpoint's\
// current subtree, returning the new subtree hash and that session's ID (for the\
// commit message). Returns ErrCheckpointNotFound when the checkpoint has no root\
// summary.\
func (s *treeWriter) applySummaryBackfill(ctx context.Context, existing *object.Tree, basePath string, summary *Summary) (plumbing.Hash, string, error) {\
    entries, err := s.flattenExisting(existing, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, "", err\
    }\
\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return plumbing.ZeroHash, "", ErrCheckpointNotFound\
    }\
\
    checkpointSummary, err := s.readSummaryFromBlob(entry.Hash)\
    if err != nil {\
        return plumbing.ZeroHash, "", fmt.Errorf("failed to read checkpoint summary: %w", err)\
    }\
\
    // Find the latest session's metadata path (0-based indexing)\
    latestIndex := len(checkpointSummary.Sessions) - 1\
    sessionMetadataPath := fmt.Sprintf("%s%d/%s", basePath, latestIndex, paths.MetadataFileName)\
    sessionEntry, exists := entries[sessionMetadataPath]\
    if !exists {\
        return plumbing.ZeroHash, "", fmt.Errorf("session metadata not found at %s", sessionMetadataPath)\
    }\
\
    existingMetadata, err := s.readMetadataFromBlob(sessionEntry.Hash)\
    if err != nil {\
        return plumbing.ZeroHash, "", fmt.Errorf("failed to read session metadata: %w", err)\
    }\
\
    existingMetadata.Summary = RedactSummary(summary)\
\
    metadataJSON, err := jsonutil.MarshalIndentWithNewline(existingMetadata, "", "  ")\
    if err != nil {\
        return plumbing.ZeroHash, "", fmt.Errorf("failed to marshal metadata: %w", err)\
    }\
    metadataHash, err := CreateBlobFromContent(s.repo, metadataJSON)\
    if err != nil {\
        return plumbing.ZeroHash, "", fmt.Errorf("failed to create metadata blob: %w", err)\
    }\
    entries[sessionMetadataPath] = object.TreeEntry{\
        Name: sessionMetadataPath,\
        Mode: filemode.Regular,\
        Hash: metadataHash,\
    }\
\
    subtree, err := s.buildCheckpointSubtree(ctx, entries, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, "", err\
    }\
    return subtree, existingMetadata.SessionID, nil\
}\
\
// applyTranscriptBackfill replaces a session's transcript, prompts, and skill\
// events on the checkpoint's current subtree, returning the new subtree hash.\
// Returns ErrCheckpointNotFound when the checkpoint has no sessions yet.\
func (s *treeWriter) applyTranscriptBackfill(ctx context.Context, opts UpdateOptions, existing *object.Tree, basePath string) (plumbing.Hash, error) {\
    entries, err := s.flattenExisting(existing, basePath)\
    if err != nil {\
        return plumbing.ZeroHash, err\
    }\
\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return plumbing.ZeroHash, ErrCheckpointNotFound\
    }\
\
    checkpointSummary, err := s.readSummaryFromBlob(entry.Hash)\
    if err != nil {\
        return plumbing.ZeroHash, fmt.Errorf("failed to read checkpoint summary: %w", err)\
    }\
    if len(checkpointSummary.Sessions) == 0 {\
        return plumbing.ZeroHash, ErrCheckpointNotFound\
    }\
\
    // Find session index matching opts.SessionID\
    sessionIndex := -1\
    var sessionMeta *Metadata\
    for i := range len(checkpointSummary.Sessions) {\
        metaPath := fmt.Sprintf("%s%d/%s", basePath, i, paths.MetadataFileName)\
        if metaEntry, metaExists := entries[metaPath]; metaExists {\
            meta, metaErr := s.readMetadataFromBlob(metaEntry.Hash)\
            if metaErr == nil && meta.SessionID == opts.SessionID {\
                sessionIndex = i\
                sessionMeta = meta\
                break\
            }\
        }\
    }\
    if sessionIndex == -1 {\
        // Fall back to latest session; log so mismatches are diagnosable.\
        sessionIndex = len(checkpointSummary.Sessions) - 1\
        logging.Debug(ctx, "backfillTranscript: session ID not found, falling back to latest",\
            slog.String("session_id", opts.SessionID),\
            slog.String("checkpoint_id", string(opts.CheckpointID)),\
            slog.Int("fallback_index", sessionIndex),\
        )\
        metaPath := fmt.Sprintf("%s%d/%s", basePath, sessionIndex, paths.MetadataFileName)\
        if metaEntry, metaExists := entries[metaPath]; metaExists {\
            sessionMeta, _ = s.readMetadataFromBlob(metaEntry.Hash) //nolint:errcheck // best-effort; nil meta means start 0\
        }\
    }\
\
    sessionPath := fmt.Sprintf("%s%d/", basePath, sessionIndex)\
\
    // Replace transcript (full replace, not append).\
    // Transcript is pre-redacted by the caller (enforced by RedactedBytes type).\
    if opts.Transcript.Len() > 0 {\
        agentType := opts.Agent\
        startLine := 0\
        if sessionMeta != nil {\
            startLine = sessionMeta.GetTranscriptStart()\
            if agentType == "" {\
                agentType = sessionMeta.Agent\
            }\
        }\
        if err := s.replaceTranscript(ctx, opts.Transcript, agentType, startLine, opts.PrecomputedBlobs, sessionPath, entries); err != nil {\
            return plumbing.ZeroHash, fmt.Errorf("failed to replace transcript: %w", err)\
        }\
\
        // Keep the root metadata.json compact_transcript pointer consistent with\
        // the finalized tree. replaceTranscript may have written transcript.jsonl\
        // that the initial write lacked (e.g. compaction was skipped then and\
        // succeeds now), so re-derive the pointer from the tree entry and rewrite\
        // the root summary when it changed.\
        compactPath := ""\
        if _, ok := entries[sessionPath+paths.CompactTranscriptFileName]; ok {\
            compactPath = "/" + sessionPath + paths.CompactTranscriptFileName\
        }\
        if checkpointSummary.Sessions[sessionIndex].CompactTranscript != compactPath {\
            checkpointSummary.Sessions[sessionIndex].CompactTranscript = compactPath\
            summaryJSON, err := jsonutil.MarshalIndentWithNewline(checkpointSummary, "", "  ")\
            if err != nil {\
                return plumbing.ZeroHash, fmt.Errorf("failed to marshal checkpoint summary: %w", err)\
            }\
            summaryHash, err := CreateBlobFromContent(s.repo, summaryJSON)\
            if err != nil {\
                return plumbing.ZeroHash, fmt.Errorf("failed to create checkpoint summary blob: %w", err)\
            }\
            entries[rootMetadataPath] = object.TreeEntry{\
                Name: rootMetadataPath,\
                Mode: filemode.Regular,\
                Hash: summaryHash,\
            }\
        }\
    }\
\
    // Replace prompts with 7-layer-redacted content.\
    if len(opts.Prompts) > 0 {\
        promptContent := RedactedJoinedPrompts(opts.Prompts)\
        blobHash, err := CreateBlobFromContent(s.repo, []byte(promptContent))\
        if err != nil {\
            return plumbing.ZeroHash, fmt.Errorf("failed to create prompt blob: %w", err)\
        }\
        entries[sessionPath+paths.PromptFileName] = object.TreeEntry{\
            Name: sessionPath + paths.PromptFileName,\
            Mode: filemode.Regular,\
            Hash: blobHash,\
        }\
    }\
\
    if len(opts.SkillEvents) > 0 {\
        if err := s.replaceSkillEvents(opts.SkillEvents, sessionPath, entries); err != nil {\
            return plumbing.ZeroHash, fmt.Errorf("failed to replace skill events: %w", err)\
        }\
    }\
\
    return s.buildCheckpointSubtree(ctx, entries, basePath)\
}\
\
// writeTaskCheckpointEntries writes task-specific checkpoint entries and returns the task metadata path.\
func (s *GitStore) writeTaskCheckpointEntries(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry) (string, error) {\
func (s *treeWriter) writeTaskCheckpointEntries(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry) (string, error) {\
    taskPath := basePath + "tasks/" + opts.ToolUseID + "/"\
\
    if opts.IsIncremental {\
3 unmodified lines\
\
}\
\
// writeIncrementalTaskCheckpoint writes an incremental checkpoint file during task execution.\
func (s *GitStore) writeIncrementalTaskCheckpoint(opts WriteOptions, taskPath string, entries map[string]object.TreeEntry) (string, error) {\
func (s *treeWriter) writeIncrementalTaskCheckpoint(opts WriteOptions, taskPath string, entries map[string]object.TreeEntry) (string, error) {\
    incData, err := redact.JSONLBytes(opts.IncrementalData)\
    if err != nil {\
        return "", fmt.Errorf("failed to redact incremental checkpoint: %w", err)\
24 unmodified lines\
\
}\
\
// writeFinalTaskCheckpoint writes the final checkpoint.json and subagent transcript.\
func (s *GitStore) writeFinalTaskCheckpoint(ctx context.Context, opts WriteOptions, taskPath string, entries map[string]object.TreeEntry) (string, error) {\
func (s *treeWriter) writeFinalTaskCheckpoint(ctx context.Context, opts WriteOptions, taskPath string, entries map[string]object.TreeEntry) (string, error) {\
    checkpoint := taskCheckpointData{\
        SessionID:      opts.SessionID,\
        ToolUseID:      opts.ToolUseID,\
64 unmodified lines\
\
//	│   └── content_hash.txt\
//	├── 2/                    # Second session\
//	└── ...\
func (s *GitStore) writeStandardCheckpointEntries(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry) error {\
func (s *treeWriter) writeStandardCheckpointEntries(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry, checkpointVersion string) error {\
    // Read existing summary to get current session count\
    var existingSummary *CheckpointSummary\
    metadataPath := basePath + paths.MetadataFileName\
82 unmodified lines\
\
    }\
\
    // Update root metadata.json with CheckpointSummary\
    return s.writeCheckpointSummary(opts, basePath, entries, sessions)\
    return s.writeCheckpointSummary(opts, basePath, entries, sessions, checkpointVersion)\
}\
\
// writeSessionToSubdirectory writes a single session's files to a numbered subdirectory.\
// Returns the absolute file paths from the git tree root for the sessions map.\
func (s *GitStore) writeSessionToSubdirectory(ctx context.Context, opts WriteOptions, sessionPath string, entries map[string]object.TreeEntry) (SessionFilePaths, error) {\
func (s *treeWriter) writeSessionToSubdirectory(ctx context.Context, opts WriteOptions, sessionPath string, entries map[string]object.TreeEntry) (SessionFilePaths, error) {\
    filePaths := SessionFilePaths{}\
\
    // Clear any existing entries at this path so stale files from a previous\
90 unmodified lines\
\
// writeCheckpointSummary writes the root-level CheckpointSummary with aggregated statistics.\
// sessions is the complete sessions array (already built by the caller).\
func (s *GitStore) writeCheckpointSummary(opts WriteOptions, basePath string, entries map[string]object.TreeEntry, sessions []SessionFilePaths) error {\
func (s *treeWriter) writeCheckpointSummary(opts WriteOptions, basePath string, entries map[string]object.TreeEntry, sessions []SessionFilePaths, checkpointVersion string) error {\
    checkpointsCount, filesTouched, tokenUsage, err := s.reaggregateFromEntries(basePath, len(sessions), entries)\
    if err != nil {\
        return fmt.Errorf("failed to aggregate session stats: %w", err)\
    }\
\
    combinedAttribution := opts.CombinedAttribution\
    checkpointVersion := CheckpointVersionBranchV1\
    if opts.CheckpointVersion != "" {\
        checkpointVersion = opts.CheckpointVersion\
    }\
    hasReview := opts.HasReview\
    hasInvestigation := opts.HasInvestigation\
    // imported is the umbrella flag: true when any session in this checkpoint\
68 unmodified lines\
\
        return err\
    }\
\
    basePath := checkpointID.Path() + "/"\
    checkpointPath := checkpointID.Path()\
    entries, err := s.flattenCheckpointEntries(rootTreeHash, checkpointPath)\
    existing, err := s.subtreeObjAt(rootTreeHash, checkpointID.Path())\
    if err != nil {\
        return err\
    }\
\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return ErrCheckpointNotFound\
    }\
\
    summary, err := s.readSummaryFromBlob(entry.Hash)\
    checkpointSubtree, err := s.applyAttributionBackfill(ctx, existing, checkpointID.Path()+"/", combinedAttribution)\
    if err != nil {\
        return fmt.Errorf("failed to read checkpoint summary: %w", err)\
        return err\
    }\
    summary.CombinedAttribution = combinedAttribution\
\
    metadataJSON, err := jsonutil.MarshalIndentWithNewline(summary, "", "  ")\
    if err != nil {\
        return fmt.Errorf("failed to marshal checkpoint summary: %w", err)\
    }\
    metadataHash, err := CreateBlobFromContent(s.repo, metadataJSON)\
    if err != nil {\
        return fmt.Errorf("failed to create checkpoint summary blob: %w", err)\
    }\
    entries[rootMetadataPath] = object.TreeEntry{\
        Name: rootMetadataPath,\
        Mode: filemode.Regular,\
        Hash: metadataHash,\
    }\
\
    newTreeHash, err := s.spliceCheckpointSubtree(ctx, rootTreeHash, checkpointID, basePath, entries)\
    newTreeHash, err := s.spliceCheckpointSubtree(rootTreeHash, checkpointID, checkpointSubtree)\
    if err != nil {\
        return err\
    }\
10 unmodified lines\
\
// findSessionIndex returns the index of an existing session with the given ID,\
// or the next available index if not found. This prevents duplicate session entries.\
func (s *GitStore) findSessionIndex(ctx context.Context, basePath string, existingSummary *CheckpointSummary, entries map[string]object.TreeEntry, sessionID string) int {\
func (s *treeWriter) findSessionIndex(ctx context.Context, basePath string, existingSummary *CheckpointSummary, entries map[string]object.TreeEntry, sessionID string) int {\
    if existingSummary == nil {\
        return 0\
    }\
21 unmodified lines\
\
// reaggregateFromEntries reads all session metadata from the entries map and\
// reaggregates CheckpointsCount, FilesTouched, and TokenUsage.\
func (s *GitStore) reaggregateFromEntries(basePath string, sessionCount int, entries map[string]object.TreeEntry) (int, []string, *agent.TokenUsage, error) {\
func (s *treeWriter) reaggregateFromEntries(basePath string, sessionCount int, entries map[string]object.TreeEntry) (int, []string, *agent.TokenUsage, error) {\
    var totalCount int\
    var allFiles []string\
    var totalTokens *agent.TokenUsage\
52 unmodified lines\
\
}\
\
// readSummaryFromBlob reads CheckpointSummary from a blob hash.\
func (s *GitStore) readSummaryFromBlob(hash plumbing.Hash) (*CheckpointSummary, error) {\
func (s *treeWriter) readSummaryFromBlob(hash plumbing.Hash) (*CheckpointSummary, error) {\
    summary, err := readJSONFromBlob[CheckpointSummary](s.repo, hash)\
    if err != nil {\
        return nil, err\
30 unmodified lines\
\
// tree (so it is pushed alongside full.jsonl) but is not yet referenced by\
// metadata. Returns true when a transcript was written, false when it was\
// empty and nothing was written.\
func (s *GitStore) writeTranscript(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry) (bool, error) {\
func (s *treeWriter) writeTranscript(ctx context.Context, opts WriteOptions, basePath string, entries map[string]object.TreeEntry) (bool, error) {\
    logCtx := logging.WithComponent(ctx, "checkpoint")\
    transcriptBytes := opts.Transcript.Bytes()\
\
107 unmodified lines\
\
// checkpoint write. transcriptBytes must already be sanitized for the agent\
// (e.g. Codex portable-transcript sanitization); callers sanitize before\
// calling so the expensive pass runs exactly once.\
func (s *GitStore) writeCompactTranscript(ctx context.Context, agentType types.AgentType, startLine int, transcriptBytes []byte, sessionPath string, entries map[string]object.TreeEntry) {\
func (s *treeWriter) writeCompactTranscript(ctx context.Context, agentType types.AgentType, startLine int, transcriptBytes []byte, sessionPath string, entries map[string]object.TreeEntry) {\
    compactCtx, compactSpan := perf.Start(ctx, "write_compact_transcript")\
    defer compactSpan.End()\
\
117 unmodified lines\
\
}\
\
// readMetadataFromBlob reads Metadata from a blob hash.\
func (s *GitStore) readMetadataFromBlob(hash plumbing.Hash) (*Metadata, error) {\
func (s *treeWriter) readMetadataFromBlob(hash plumbing.Hash) (*Metadata, error) {\
    return readJSONFromBlob[Metadata](s.repo, hash)\
}\
\
// buildCommitMessage constructs the commit message with proper trailers.\
// The commit subject is always "Checkpoint: <id>" for consistency.\
// If CommitSubject is provided (e.g., for task checkpoints), it's included in the body.\
func (s *GitStore) buildCommitMessage(opts WriteOptions, taskMetadataPath string) string {\
func (s *treeWriter) buildCommitMessage(opts WriteOptions, taskMetadataPath string) string {\
    var commitMsg strings.Builder\
\
    // Subject line is always the checkpoint ID for consistent formatting\
433 unmodified lines\
\
        return err\
    }\
\
    // Flatten only the checkpoint subtree\
    basePath := checkpointID.Path() + "/"\
    checkpointPath := checkpointID.Path()\
    entries, err := s.flattenCheckpointEntries(rootTreeHash, checkpointPath)\
    existing, err := s.subtreeObjAt(rootTreeHash, checkpointID.Path())\
    if err != nil {\
        return err\
    }\
\
    // Read root CheckpointSummary to find the latest session\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return ErrCheckpointNotFound\
    }\
\
    checkpointSummary, err := s.readSummaryFromBlob(entry.Hash)\
    checkpointSubtree, sessionID, err := s.applySummaryBackfill(ctx, existing, checkpointID.Path()+"/", summary)\
    if err != nil {\
        return fmt.Errorf("failed to read checkpoint summary: %w", err)\
        return err\
    }\
\
    // Find the latest session's metadata path (0-based indexing)\
    latestIndex := len(checkpointSummary.Sessions) - 1\
    sessionMetadataPath := fmt.Sprintf("%s%d/%s", basePath, latestIndex, paths.MetadataFileName)\
    sessionEntry, exists := entries[sessionMetadataPath]\
    if !exists {\
        return fmt.Errorf("session metadata not found at %s", sessionMetadataPath)\
    }\
\
    // Read and update session metadata\
    existingMetadata, err := s.readMetadataFromBlob(sessionEntry.Hash)\
    if err != nil {\
        return fmt.Errorf("failed to read session metadata: %w", err)\
    }\
\
    // Update the summary\
    existingMetadata.Summary = RedactSummary(summary)\
\
    // Write updated session metadata\
    metadataJSON, err := jsonutil.MarshalIndentWithNewline(existingMetadata, "", "  ")\
    if err != nil {\
        return fmt.Errorf("failed to marshal metadata: %w", err)\
    }\
    metadataHash, err := CreateBlobFromContent(s.repo, metadataJSON)\
    if err != nil {\
        return fmt.Errorf("failed to create metadata blob: %w", err)\
    }\
    entries[sessionMetadataPath] = object.TreeEntry{\
        Name: sessionMetadataPath,\
        Mode: filemode.Regular,\
        Hash: metadataHash,\
    }\
\
    // Build checkpoint subtree and splice into root (O(depth) tree surgery)\
    newTreeHash, err := s.spliceCheckpointSubtree(ctx, rootTreeHash, checkpointID, basePath, entries)\
    newTreeHash, err := s.spliceCheckpointSubtree(rootTreeHash, checkpointID, checkpointSubtree)\
    if err != nil {\
        return err\
    }\
\
    authorName, authorEmail := GetGitAuthorFromRepo(s.repo)\
    commitMsg := fmt.Sprintf("Update summary for checkpoint %s (session: %s)", checkpointID, existingMetadata.SessionID)\
    commitMsg := fmt.Sprintf("Update summary for checkpoint %s (session: %s)", checkpointID, sessionID)\
    newCommitHash, err := CreateCommit(ctx, s.repo, newTreeHash, parentHash, commitMsg, authorName, authorEmail)\
    if err != nil {\
        return err\
26 unmodified lines\
\
        return err\
    }\
\
    // Flatten only the checkpoint subtree\
    basePath := opts.CheckpointID.Path() + "/"\
    checkpointPath := opts.CheckpointID.Path()\
    entries, err := s.flattenCheckpointEntries(rootTreeHash, checkpointPath)\
    existing, err := s.subtreeObjAt(rootTreeHash, opts.CheckpointID.Path())\
    if err != nil {\
        return err\
    }\
\
    // Read root CheckpointSummary to find the session slot\
    rootMetadataPath := basePath + paths.MetadataFileName\
    entry, exists := entries[rootMetadataPath]\
    if !exists {\
        return ErrCheckpointNotFound\
    }\
\
    checkpointSummary, err := s.readSummaryFromBlob(entry.Hash)\
    checkpointSubtree, err := s.applyTranscriptBackfill(ctx, opts, existing, opts.CheckpointID.Path()+"/")\
    if err != nil {\
        return fmt.Errorf("failed to read checkpoint summary: %w", err)\
        return err\
    }\
    if len(checkpointSummary.Sessions) == 0 {\
        return ErrCheckpointNotFound\
    }\
\
    // Find session index matching opts.SessionID\
    sessionIndex := -1\
    var sessionMeta *Metadata\
    for i := range len(checkpointSummary.Sessions) {\
        metaPath := fmt.Sprintf("%s%d/%s", basePath, i, paths.MetadataFileName)\
        if metaEntry, metaExists := entries[metaPath]; metaExists {\
            meta, metaErr := s.readMetadataFromBlob(metaEntry.Hash)\
            if metaErr == nil && meta.SessionID == opts.SessionID {\
                sessionIndex = i\
                sessionMeta = meta\
                break\
            }\
        }\
    }\
    if sessionIndex == -1 {\
        // Fall back to latest session; log so mismatches are diagnosable.\
        sessionIndex = len(checkpointSummary.Sessions) - 1\
        logging.Debug(ctx, "backfillTranscript: session ID not found, falling back to latest",\
            slog.String("session_id", opts.SessionID),\
            slog.String("checkpoint_id", string(opts.CheckpointID)),\
            slog.Int("fallback_index", sessionIndex),\
        )\
        metaPath := fmt.Sprintf("%s%d/%s", basePath, sessionIndex, paths.MetadataFileName)\
        if metaEntry, metaExists := entries[metaPath]; metaExists {\
            sessionMeta, _ = s.readMetadataFromBlob(metaEntry.Hash) //nolint:errcheck // best-effort; nil meta means start 0\
        }\
    }\
\
    sessionPath := fmt.Sprintf("%s%d/", basePath, sessionIndex)\
\
    // Replace transcript (full replace, not append).\
    // Transcript is pre-redacted by the caller (enforced by RedactedBytes type).\
    if opts.Transcript.Len() > 0 {\
        agentType := opts.Agent\
        startLine := 0\
        if sessionMeta != nil {\
            startLine = sessionMeta.GetTranscriptStart()\
            if agentType == "" {\
                agentType = sessionMeta.Agent\
            }\
        }\
        if err := s.replaceTranscript(ctx, opts.Transcript, agentType, startLine, opts.PrecomputedBlobs, sessionPath, entries); err != nil {\
            return fmt.Errorf("failed to replace transcript: %w", err)\
        }\
\
        // Keep the root metadata.json compact_transcript pointer consistent with\
        // the finalized tree. replaceTranscript may have written transcript.jsonl\
        // that the initial write lacked (e.g. compaction was skipped then and\
        // succeeds now), so re-derive the pointer from the tree entry and rewrite\
        // the root summary when it changed.\
        compactPath := ""\
        if _, ok := entries[sessionPath+paths.CompactTranscriptFileName]; ok {\
            compactPath = "/" + sessionPath + paths.CompactTranscriptFileName\
        }\
        if checkpointSummary.Sessions[sessionIndex].CompactTranscript != compactPath {\
            checkpointSummary.Sessions[sessionIndex].CompactTranscript = compactPath\
            summaryJSON, err := jsonutil.MarshalIndentWithNewline(checkpointSummary, "", "  ")\
            if err != nil {\
                return fmt.Errorf("failed to marshal checkpoint summary: %w", err)\
            }\
            summaryHash, err := CreateBlobFromContent(s.repo, summaryJSON)\
            if err != nil {\
                return fmt.Errorf("failed to create checkpoint summary blob: %w", err)\
            }\
            entries[rootMetadataPath] = object.TreeEntry{\
                Name: rootMetadataPath,\
                Mode: filemode.Regular,\
                Hash: summaryHash,\
            }\
        }\
    }\
\
    // Replace prompts with 7-layer-redacted content.\
    if len(opts.Prompts) > 0 {\
        promptContent := RedactedJoinedPrompts(opts.Prompts)\
        blobHash, err := CreateBlobFromContent(s.repo, []byte(promptContent))\
        if err != nil {\
            return fmt.Errorf("failed to create prompt blob: %w", err)\
        }\
        entries[sessionPath+paths.PromptFileName] = object.TreeEntry{\
            Name: sessionPath + paths.PromptFileName,\
            Mode: filemode.Regular,\
            Hash: blobHash,\
        }\
    }\
\
    if len(opts.SkillEvents) > 0 {\
        if err := s.replaceSkillEvents(opts.SkillEvents, sessionPath, entries); err != nil {\
            return fmt.Errorf("failed to replace skill events: %w", err)\
        }\
    }\
\
    // Build checkpoint subtree and splice into root (O(depth) tree surgery)\
    newTreeHash, err := s.spliceCheckpointSubtree(ctx, rootTreeHash, opts.CheckpointID, basePath, entries)\
    newTreeHash, err := s.spliceCheckpointSubtree(rootTreeHash, opts.CheckpointID, checkpointSubtree)\
    if err != nil {\
        return err\
    }\
12 unmodified lines\
\
    return s.setPrimaryRef(newCommitHash)\
}\
\
func (s *GitStore) replaceSkillEvents(skillEvents []agent.SkillEvent, sessionPath string, entries map[string]object.TreeEntry) error {\
func (s *treeWriter) replaceSkillEvents(skillEvents []agent.SkillEvent, sessionPath string, entries map[string]object.TreeEntry) error {\
    metadataPath := sessionPath + paths.MetadataFileName\
    entry, exists := entries[metadataPath]\
    if !exists {\
35 unmodified lines\
\
// reuse precomputed blobs: each checkpoint in a turn shares the full\
// transcript but has its own start offset, so the compact content differs per\
// checkpoint.\
func (s *GitStore) replaceTranscript(ctx context.Context, transcript redact.RedactedBytes, agentType types.AgentType, startLine int, precomputed *PrecomputedTranscriptBlobs, sessionPath string, entries map[string]object.TreeEntry) error {\
func (s *treeWriter) replaceTranscript(ctx context.Context, transcript redact.RedactedBytes, agentType types.AgentType, startLine int, precomputed *PrecomputedTranscriptBlobs, sessionPath string, entries map[string]object.TreeEntry) error {\
    // Ignore precompute if invariants are violated — fall back to fresh chunking.\
    if precomputed != nil && !precomputed.IsUsable() {\
        precomputed = nil\
236 unmodified lines\
\
// copyMetadataDir copies all files from a directory to the checkpoint path.\
// Used to include additional metadata files like task checkpoints, subagent transcripts, etc.\
func (s *GitStore) copyMetadataDir(ctx context.Context, metadataDir, basePath string, entries map[string]object.TreeEntry) error {\
func (s *treeWriter) copyMetadataDir(ctx context.Context, metadataDir, basePath string, entries map[string]object.TreeEntry) error {\
    err := filepath.Walk(metadataDir, func(path string, info os.FileInfo, err error) error {\
        if err != nil {\
            return err\
```\
\
Mcmd/entire/cli/checkpoint/persistent.go+330/-261\
\
```\
71 unmodified lines\
\
72\
73\
74\
75\
75\
76\
77\
78\
\
71 unmodified lines\
\
        Prompts:      []string{"hi"},\
    }\
\
    err = store.writeStandardCheckpointEntries(context.Background(), opts, basePath, entries)\
    err = store.writeStandardCheckpointEntries(context.Background(), opts, basePath, entries, CheckpointVersionBranchV1)\
    if err == nil {\
        t.Fatal("expected writeStandardCheckpointEntries to refuse, got nil error")\
    }\
```\
\
Mcmd/entire/cli/checkpoint/persistent\_tripwire\_test.go+1/-1\
\
```\
76 unmodified lines\
\
77\
78\
79\
80\
80\
81\
82\
82\
83\
84\
85\
86\
87\
88\
89\
15 unmodified lines\
\
105\
106\
107\
104\
108\
109\
110\
111\
112\
113\
114\
115\
\
76 unmodified lines\
\
    }\
\
    basePath := cpID.Path() + "/"\
    entries, err := store.flattenCheckpointEntries(rootTreeHash, cpID.Path())\
    existing, err := store.subtreeObjAt(rootTreeHash, cpID.Path())\
    if err != nil {\
        t.Fatalf("flattenCheckpointEntries(): %v", err)\
        t.Fatalf("subtreeObjAt(): %v", err)\
    }\
    entries, err := store.flattenExisting(existing, basePath)\
    if err != nil {\
        t.Fatalf("flattenExisting(): %v", err)\
    }\
\
    summary := readSummaryFromBranch(t, store.repo, cpID)\
15 unmodified lines\
\
        Hash: metadataHash,\
    }\
\
    newTreeHash, err := store.spliceCheckpointSubtree(ctx, rootTreeHash, cpID, basePath, entries)\
    checkpointSubtree, err := store.buildCheckpointSubtree(ctx, entries, basePath)\
    if err != nil {\
        t.Fatalf("buildCheckpointSubtree(): %v", err)\
    }\
    newTreeHash, err := store.spliceCheckpointSubtree(rootTreeHash, cpID, checkpointSubtree)\
    if err != nil {\
        t.Fatalf("spliceCheckpointSubtree(): %v", err)\
    }\
```\
\
Mcmd/entire/cli/checkpoint/persistent\_update\_test.go+11/-3\
\
```\
13 unmodified lines\
\
14\
15\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
19\
31\
32\
33\
21\
34\
35\
36\
37\
38\
24 unmodified lines\
\
63\
64\
65\
52\
66\
67\
68\
69\
\
13 unmodified lines\
\
    _ EphemeralStore  = (*ephemeralStore)(nil)\
)\
\
// treeWriter holds the repo-only machinery for building a single checkpoint's\
// subtree from write requests: entry builders, transcript/session writers, and\
// the per-request appliers (applySessionWrite / applyTranscriptBackfill /\
// applySummaryBackfill / applyAttributionBackfill). It is independent of where\
// the resulting subtree is committed, so both the git-branch store (which nests\
// the subtree under <shard>/<id>/ on the v1 branch) and the git-refs store\
// (which keeps it at the root of a per-checkpoint ref) embed it and share this\
// code.\
type treeWriter struct {\
    repo *git.Repository\
}\
\
// GitStore is the committed (persistent) checkpoint store. Writes target\
// refs.Primary; committed reads resolve against refs.Read. The temporary\
// shadow-branch surface lives in ephemeralStore.\
// shadow-branch surface lives in ephemeralStore. It embeds *treeWriter for the\
// shared subtree-building machinery.\
type GitStore struct {\
    repo        *git.Repository\
    *treeWriter\
\
    refs        PersistentRefs\
    blobFetcher BlobFetchFunc\
}\
24 unmodified lines\
\
// and committed-metadata topology. Pass DefaultV1Refs() for the v1-only default\
// or ResolveRefs(ctx) in code paths that honor settings.\
func NewGitStore(repo *git.Repository, refs PersistentRefs) *GitStore {\
    return &GitStore{repo: repo, refs: refs}\
    return &GitStore{treeWriter: &treeWriter{repo: repo}, refs: refs}\
}\
\
// SetBlobFetcher configures the store to automatically fetch missing blobs\
```\
\
Mcmd/entire/cli/checkpoint/store.go+17/-3\
\
```\
1 unmodified line\
\
2\
3\
4\
5\
5\
6\
7\
34 unmodified lines\
\
42\
43\
44\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
45\
46\
47\
48\
65\
66\
67\
49\
50\
51\
52\
53\
54\
55\
7 unmodified lines\
\
63\
64\
65\
81\
82\
66\
67\
68\
69\
70\
84\
85\
71\
72\
73\
74\
75\
1 unmodified line\
\
77\
78\
79\
93\
80\
81\
82\
83\
97\
84\
85\
99\
86\
87\
88\
89\
90\
91\
92\
93\
94\
95\
96\
97\
98\
10 unmodified lines\
\
109\
110\
111\
116\
112\
113\
118\
119\
120\
114\
115\
116\
117\
118\
119\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
120\
121\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
134\
135\
136\
137\
138\
139\
140\
141\
142\
143\
143\
144\
145\
146\
26 unmodified lines\
\
173\
174\
175\
176\
176\
178\
179\
180\
177\
178\
179\
180\
181\
182\
183\
184\
185\
186\
187\
187\
188\
189\
190\
191\
192\
193\
194\
195\
3 unmodified lines\
\
199\
200\
201\
197\
202\
203\
204\
11 unmodified lines\
\
216\
217\
218\
215\
219\
220\
217\
218\
219\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
225\
226\
227\
228\
231\
232\
233\
234\
235\
236\
237\
238\
239\
240\
241\
242\
243\
244\
239\
240\
241\
245\
246\
247\
248\
246\
247\
248\
249\
250\
251\
252\
4 unmodified lines\
\
257\
258\
259\
259\
260\
261\
262\
263\
15 unmodified lines\
\
279\
280\
281\
281\
282\
283\
283\
284\
285\
286\
287\
288\
289\
286\
287\
288\
289\
290\
291\
292\
293\
294\
295\
291\
292\
293\
294\
295\
301\
302\
303\
304\
305\
306\
307\
308\
309\
310\
311\
312\
313\
314\
315\
316\
296\
297\
298\
299\
320\
321\
322\
323\
300\
301\
302\
327\
328\
329\
303\
304\
305\
306\
307\
308\
309\
310\
311\
312\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
350\
351\
352\
353\
354\
355\
356\
357\
358\
324\
325\
326\
327\
328\
329\
330\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
10 unmodified lines\
\
360\
361\
362\
376\
363\
364\
378\
379\
380\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
386\
387\
388\
389\
375\
376\
377\
378\
379\
380\
381\
382\
383\
384\
385\
386\
387\
388\
400\
401\
402\
389\
390\
391\
392\
407\
408\
409\
393\
394\
395\
396\
6 unmodified lines\
\
403\
404\
405\
422\
406\
407\
408\
409\
17 unmodified lines\
\
427\
428\
429\
446\
430\
431\
448\
432\
433\
450\
451\
452\
453\
434\
435\
436\
437\
438\
439\
455\
456\
457\
458\
459\
440\
441\
442\
443\
444\
465\
466\
467\
468\
469\
470\
471\
472\
473\
474\
475\
476\
477\
478\
479\
480\
445\
446\
447\
448\
484\
485\
486\
487\
449\
450\
451\
452\
492\
493\
494\
495\
496\
497\
498\
453\
454\
455\
456\
457\
458\
459\
460\
461\
462\
463\
464\
465\
466\
467\
468\
469\
470\
471\
472\
473\
474\
475\
476\
505\
477\
478\
479\
480\
\
1 unmodified line\
\
import (\
    "context"\
    "errors"\
    "fmt"\
\
    "github.com/spf13/cobra"\
34 unmodified lines\
\
    }\
}\
\
// validateGrantGranteeType rejects grantee kinds the control plane no longer\
// accepts when granting. A grant resolves to an account (from the provider\
// identity), so "account" is the only valid kind ("" means the default,\
// account). org/team granting was dropped server-side (COR-561) and the\
// generated client enum is account-only, so catch it here with a clear message\
// instead of an opaque enum-encoding error.\
func validateGrantGranteeType(granteeType string) error {\
    switch granteeType {\
    case "", "account":\
        return nil\
    default:\
        return fmt.Errorf("invalid --grantee-type %q: only \"account\" is supported", granteeType)\
    }\
}\
\
// newGrantCmd is the hidden `entire grant` command group: manage access\
// grants and org membership on the Entire control plane. Org, project, and\
// repo each support add / list / remove. Surfaced via `entire labs`.\
//\
// Grantees are addressed by their identity provider + provider user id\
// (e.g. --provider github --provider-user-id 12345), matching the control\
// plane's grant model. Handle-based addressing is a follow-up.\
// Grantees are addressed by a provider-qualified handle (e.g. github:alice),\
// which the CLI resolves to the provider account behind the scenes. `remove`\
// also accepts an account ULID to revoke a grant by id. Targets (org, project,\
// repo) are addressed by name or ULID.\
func newGrantCmd() *cobra.Command {\
    cmd := &cobra.Command{\
        Use:    "grant",\
7 unmodified lines\
\
    return cmd\
}\
\
// orgMemberColumns / projectGrantColumns are the human table views of the\
// two membership/grant listings.\
// orgMemberColumns / grantColumns are the human table views of the\
// membership/grant listings. Grant listings now include inherited and owner\
// grants, so GRANTEE shows a friendly name (handle/org name) with SOURCE\
// saying where the grant comes from; ID keeps the ULID for revoke.\
var (\
    orgMemberColumns    = []string{"ACCOUNT", "ROLE", "STATUS"}\
    projectGrantColumns = []string{"GRANTEE-TYPE", "GRANTEE", "ROLE"}\
    orgMemberColumns = []string{"ACCOUNT", "ROLE", "STATUS"}\
    grantColumns     = []string{"GRANTEE-TYPE", "GRANTEE", "ID", "ROLE", "SOURCE"}\
)\
\
func orgMemberRow(m coreapi.Membership) []string {\
1 unmodified line\
\
}\
\
func projectGrantRow(g coreapi.ProjectGrant) []string {\
    return []string{g.GranteeType, g.GranteeId, g.Role}\
    return []string{g.GranteeType, granteeName(g.GranteeName, g.GranteeId), g.GranteeId, g.Role, g.Source}\
}\
\
// repoGrantRow mirrors projectGrantRow; RepoGrant and ProjectGrant share the\
// grantee-type/grantee/role shape, so both reuse projectGrantColumns.\
// grantee/role/source shape, so both reuse grantColumns.\
func repoGrantRow(g coreapi.RepoGrant) []string {\
    return []string{g.GranteeType, g.GranteeId, g.Role}\
    return []string{g.GranteeType, granteeName(g.GranteeName, g.GranteeId), g.GranteeId, g.Role, g.Source}\
}\
\
// granteeName returns the friendly name when the server resolved one, falling\
// back to the ULID for grantees it couldn't label (e.g. teams).\
func granteeName(name coreapi.OptString, granteeID string) string {\
    if n := name.Or(""); n != "" {\
        return n\
    }\
    return granteeID\
}\
\
// --- org membership -------------------------------------------------------\
10 unmodified lines\
\
}\
\
func newGrantOrgAddCmd() *cobra.Command {\
    var provider, providerUserID, role string\
    var role string\
    cmd := &cobra.Command{\
        Use:   "add <org>",\
        Short: "Add a member to an org",\
        Args:  cobra.ExactArgs(1),\
        Use:     "add <org> <grantee>",\
        Short:   "Add a member to an org",\
        Long:    "Add a member (addressed as provider:handle, e.g. github:alice) to an org (name or ULID).",\
        Example: "  entire grant org add acme github:alice --role admin",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            body := &coreapi.AddOrgMemberInputBody{\
                Provider:       provider,\
                ProviderUserId: providerUserID,\
            }\
            if role != "" {\
                r, err := parseOrgRole(role)\
                if err != nil {\
                    cmd.SilenceUsage = true\
                    return err\
                }\
                body.Role = coreapi.NewOptAddOrgMemberInputBodyRole(r)\
            }\
            return runCoreJSON(cmd, func(ctx context.Context, c *coreapi.Client) (any, error) {\
                orgID, err := resolveOrgRef(ctx, c, args[0])\
                if err != nil {\
                    return nil, err\
                }\
                provider, providerUserID, err := resolveGranteeProvider(ctx, c, args[1])\
                if err != nil {\
                    return nil, err\
                }\
                body := &coreapi.AddOrgMemberInputBody{\
                    Provider:       provider,\
                    ProviderUserId: providerUserID,\
                }\
                if role != "" {\
                    r, err := parseOrgRole(role)\
                    if err != nil {\
                        return nil, err\
                    }\
                    body.Role = coreapi.NewOptAddOrgMemberInputBodyRole(r)\
                }\
                return c.AddOrgMember(ctx, body, coreapi.AddOrgMemberParams{OrgId: orgID})\
            })\
        },\
    }\
    bindGranteeFlags(cmd, &provider, &providerUserID)\
    cmd.Flags().StringVar(&role, "role", "", "org role: owner, admin, or member (default member)")\
    return cmd\
}\
26 unmodified lines\
\
}\
\
func newGrantOrgRemoveCmd() *cobra.Command {\
    var provider, providerUserID string\
    cmd := &cobra.Command{\
        Use:   "remove <org>",\
        Short: "Remove a member from an org",\
        Args:  cobra.ExactArgs(1),\
        Use:     "remove <org> <grantee>",\
        Short:   "Remove a member from an org",\
        Long:    "Remove a member (addressed as provider:handle, e.g. github:alice) from an org (name or ULID).",\
        Example: "  entire grant org remove acme github:alice",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            return runCore(cmd, func(ctx context.Context, c *coreapi.Client) error {\
                orgID, err := resolveOrgRef(ctx, c, args[0])\
                if err != nil {\
                    return err\
                }\
                return revokeGrant(cmd, "Removed", fmt.Sprintf("%s/%s from org %s", provider, providerUserID, args[0]), func() error {\
                provider, providerUserID, err := resolveGranteeProvider(ctx, c, args[1])\
                if err != nil {\
                    return err\
                }\
                return revokeGrant(cmd, "Removed", fmt.Sprintf("%s from org %s", args[1], args[0]), func() error {\
                    return c.RemoveOrgMember(ctx, coreapi.RemoveOrgMemberParams{\
                        OrgId:          orgID,\
                        Provider:       provider,\
3 unmodified lines\
\
            })\
        },\
    }\
    bindGranteeFlags(cmd, &provider, &providerUserID)\
    return cmd\
}\
\
11 unmodified lines\
\
}\
\
func newGrantProjectAddCmd() *cobra.Command {\
    var provider, providerUserID, role, granteeType string\
    var role string\
    cmd := &cobra.Command{\
        Use:   "add <project>",\
        Short: "Grant access to a project",\
        Args:  cobra.ExactArgs(1),\
        Use:     "add <project> <grantee>",\
        Short:   "Grant a user access to a project",\
        Long:    "Grant a user (addressed as provider:handle, e.g. github:alice) access to a project (name or ULID).",\
        Example: "  entire grant project add widgets github:alice --role writer",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            if err := validateGrantRole(role); err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            if err := validateGrantGranteeType(granteeType); err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            return runCoreJSON(cmd, func(ctx context.Context, c *coreapi.Client) (any, error) {\
                projID, err := resolveProjectRef(ctx, c, args[0])\
                if err != nil {\
                    return nil, err\
                }\
                provider, providerUserID, err := resolveGranteeProvider(ctx, c, args[1])\
                if err != nil {\
                    return nil, err\
                }\
                body := &coreapi.GrantProjectAccessInputBody{\
                    Provider:       provider,\
                    ProviderUserId: providerUserID,\
                    Role:           coreapi.GrantProjectAccessInputBodyRole(role),\
                }\
                if granteeType != "" {\
                    body.GranteeType = coreapi.NewOptGrantProjectAccessInputBodyGranteeType(coreapi.GrantProjectAccessInputBodyGranteeType(granteeType))\
                }\
                return c.GrantProjectAccess(ctx, body, coreapi.GrantProjectAccessParams{ProjectId: projID})\
            })\
        },\
    }\
    bindGranteeFlags(cmd, &provider, &providerUserID)\
    cmd.Flags().StringVar(&role, "role", "", "project role (required)")\
    cmd.Flags().StringVar(&granteeType, "grantee-type", "", "grantee kind: account (the only supported kind; default)")\
    cmd.Flags().StringVar(&role, "role", "", "project role: reader, writer, or admin (required)")\
    markRequired(cmd, "role")\
    return cmd\
}\
4 unmodified lines\
\
        Short: "List project members",\
        Args:  cobra.ExactArgs(1),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            return runCoreList(cmd, projectGrantColumns, projectGrantRow, func(ctx context.Context, c *coreapi.Client) ([]coreapi.ProjectGrant, error) {\
            return runCoreList(cmd, grantColumns, projectGrantRow, func(ctx context.Context, c *coreapi.Client) ([]coreapi.ProjectGrant, error) {\
                projID, err := resolveProjectRef(ctx, c, args[0])\
                if err != nil {\
                    return nil, err\
15 unmodified lines\
\
}\
\
func newGrantProjectRemoveCmd() *cobra.Command {\
    var granteeType, granteeID, provider, providerUserID string\
    cmd := &cobra.Command{\
        Use:   "remove <project>",\
        Use:   "remove <project> <grantee>",\
        Short: "Revoke project access from a grantee",\
        Long: "Revoke a grantee's access to a project (addressed by name or ULID). " +\
            "Identify the grantee either by --provider/--provider-user-id (an " +\
            "account, e.g. github + user id) or by --grantee-type account " +\
            "--grantee-id <ULID>.",\
        Args: cobra.ExactArgs(1),\
            "The grantee is a provider-qualified handle (e.g. github:alice) or an " +\
            "account ULID.",\
        Example: "  entire grant project remove widgets github:alice",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            mode, err := parseGranteeMode(provider, providerUserID, granteeType, granteeID)\
            if err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            return runCore(cmd, func(ctx context.Context, c *coreapi.Client) error {\
                projID, err := resolveProjectRef(ctx, c, args[0])\
                if err != nil {\
                    return err\
                }\
                if mode == granteeModeProvider {\
                    return revokeGrant(cmd, "Revoked", fmt.Sprintf("%s/%s from project %s", provider, providerUserID, args[0]), func() error {\
                        return c.RevokeProjectAccessByProvider(ctx, coreapi.RevokeProjectAccessByProviderParams{\
                            ProjectId:      projID,\
                            Provider:       provider,\
                            ProviderUserId: providerUserID,\
                        })\
                    })\
                }\
                return revokeGrant(cmd, "Revoked", fmt.Sprintf("%s %s from project %s", granteeType, granteeID, args[0]), func() error {\
                    return c.RevokeProjectAccess(ctx, coreapi.RevokeProjectAccessParams{\
                        ProjectId:   projID,\
                        GranteeType: granteeType,\
                        GranteeId:   granteeID,\
                    })\
                })\
                return revokeProjectGrantee(ctx, cmd, c, projID, args[0], args[1])\
            })\
        },\
    }\
    cmd.Flags().StringVar(&granteeType, "grantee-type", "", "grantee kind: account (with --grantee-id)")\
    cmd.Flags().StringVar(&granteeID, "grantee-id", "", "grantee ULID (with --grantee-type)")\
    cmd.Flags().StringVar(&provider, "provider", "", "identity provider, e.g. github (with --provider-user-id)")\
    cmd.Flags().StringVar(&providerUserID, "provider-user-id", "", "provider-specific user id (with --provider)")\
    return cmd\
}\
\
// granteeMode names the two ways `grant project remove` / `grant repo remove`\
// can address a grantee.\
type granteeMode int\
// revokeProjectGrantee revokes a grantee (provider:handle or account ULID) from\
// a resolved project. projectRef is the user's original (pre-resolution) project\
// ref, used only for the success message.\
func revokeProjectGrantee(ctx context.Context, cmd *cobra.Command, c *coreapi.Client, projID, projectRef, grantee string) error {\
    return revokeGrantee(ctx, cmd, c, "project", projectRef, grantee,\
        func() error {\
            return c.RevokeProjectAccess(ctx, coreapi.RevokeProjectAccessParams{\
                ProjectId:   projID,\
                GranteeType: "account",\
                GranteeId:   grantee,\
            })\
        },\
        func(provider, providerUserID string) error {\
            return c.RevokeProjectAccessByProvider(ctx, coreapi.RevokeProjectAccessByProviderParams{\
                ProjectId:      projID,\
                Provider:       provider,\
                ProviderUserId: providerUserID,\
            })\
        })\
}\
\
const (\
    granteeModeProvider granteeMode = iota // --provider + --provider-user-id\
    granteeModeID                          // --grantee-type + --grantee-id\
)\
\
// parseGranteeMode validates that exactly one addressing mode was supplied\
// and fully specified, returning which one. The two modes are mutually\
// exclusive: a provider account (github + user id) hits the by-provider revoke\
// route, while a ULID grantee hits the typed-id route that also covers org and\
// team grantees.\
func parseGranteeMode(provider, providerUserID, granteeType, granteeID string) (granteeMode, error) {\
    byProvider := provider != "" || providerUserID != ""\
    byID := granteeType != "" || granteeID != ""\
    switch {\
    case byProvider && byID:\
        return 0, errors.New("specify either --provider/--provider-user-id or --grantee-type/--grantee-id, not both")\
    case byProvider:\
        if provider == "" || providerUserID == "" {\
            return 0, errors.New("both --provider and --provider-user-id are required")\
        }\
        return granteeModeProvider, nil\
    case byID:\
        if granteeType == "" || granteeID == "" {\
            return 0, errors.New("both --grantee-type and --grantee-id are required")\
        }\
        return granteeModeID, nil\
    default:\
        return 0, errors.New("identify the grantee with --provider/--provider-user-id or --grantee-type/--grantee-id")\
// revokeGrantee performs the shared grantee-revocation routing for projects and\
// repos: a ULID grantee takes the typed-id route (revokeByID); a provider:handle\
// is resolved to its provider account first and takes the by-provider route\
// (revokeByProvider). target ("project"/"repo") and ref name the grant in the\
// success message.\
func revokeGrantee(\
    ctx context.Context,\
    cmd *cobra.Command,\
    c *coreapi.Client,\
    target, ref, grantee string,\
    revokeByID func() error,\
    revokeByProvider func(provider, providerUserID string) error,\
) error {\
    if looksLikeULID(grantee) {\
        return revokeGrant(cmd, "Revoked", fmt.Sprintf("account %s from %s %s", grantee, target, ref), revokeByID)\
    }\
    provider, providerUserID, err := resolveGranteeProvider(ctx, c, grantee)\
    if err != nil {\
        return err\
    }\
    return revokeGrant(cmd, "Revoked", fmt.Sprintf("%s from %s %s", grantee, target, ref), func() error {\
        return revokeByProvider(provider, providerUserID)\
    })\
}\
\
// --- repo grants ----------------------------------------------------------\
10 unmodified lines\
\
}\
\
func newGrantRepoAddCmd() *cobra.Command {\
    var provider, providerUserID, role, granteeType, project string\
    var role, project string\
    cmd := &cobra.Command{\
        Use:   "add <repo>",\
        Short: "Grant access to a repo",\
        Args:  cobra.ExactArgs(1),\
        Use:     "add <repo> <grantee>",\
        Short:   "Grant a user access to a repo",\
        Long:    "Grant a user (addressed as provider:handle, e.g. github:alice) access to a repo (name or ULID).",\
        Example: "  entire grant repo add web github:alice --project acme --role writer",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            if err := validateGrantRole(role); err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            if err := validateGrantGranteeType(granteeType); err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            return runCoreJSON(cmd, func(ctx context.Context, c *coreapi.Client) (any, error) {\
                repoID, err := resolveRepoRef(ctx, c, args[0], project)\
                if err != nil {\
                    return nil, err\
                }\
                provider, providerUserID, err := resolveGranteeProvider(ctx, c, args[1])\
                if err != nil {\
                    return nil, err\
                }\
                body := &coreapi.GrantRepoAccessInputBody{\
                    Provider:       provider,\
                    ProviderUserId: providerUserID,\
                    Role:           coreapi.GrantRepoAccessInputBodyRole(role),\
                }\
                if granteeType != "" {\
                    body.GranteeType = coreapi.NewOptGrantRepoAccessInputBodyGranteeType(coreapi.GrantRepoAccessInputBodyGranteeType(granteeType))\
                }\
                return c.GrantRepoAccess(ctx, body, coreapi.GrantRepoAccessParams{RepoId: repoID})\
            })\
        },\
    }\
    bindGranteeFlags(cmd, &provider, &providerUserID)\
    cmd.Flags().StringVar(&role, "role", "", "repo role (required)")\
    cmd.Flags().StringVar(&granteeType, "grantee-type", "", "grantee kind: account (the only supported kind; default)")\
    cmd.Flags().StringVar(&role, "role", "", "repo role: reader, writer, or admin (required)")\
    bindRepoProjectFlag(cmd, &project)\
    markRequired(cmd, "role")\
    return cmd\
6 unmodified lines\
\
        Short: "List repo grants",\
        Args:  cobra.ExactArgs(1),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            return runCoreList(cmd, projectGrantColumns, repoGrantRow, func(ctx context.Context, c *coreapi.Client) ([]coreapi.RepoGrant, error) {\
            return runCoreList(cmd, grantColumns, repoGrantRow, func(ctx context.Context, c *coreapi.Client) ([]coreapi.RepoGrant, error) {\
                repoID, err := resolveRepoRef(ctx, c, args[0], project)\
                if err != nil {\
                    return nil, err\
17 unmodified lines\
\
}\
\
func newGrantRepoRemoveCmd() *cobra.Command {\
    var granteeType, granteeID, provider, providerUserID, project string\
    var project string\
    cmd := &cobra.Command{\
        Use:   "remove <repo>",\
        Use:   "remove <repo> <grantee>",\
        Short: "Revoke repo access from a grantee",\
        Long: "Revoke a grantee's access to a repo. Identify the grantee either by " +\
            "--provider/--provider-user-id (an account, e.g. github + user id) or by " +\
            "--grantee-type account --grantee-id <ULID>.",\
        Args: cobra.ExactArgs(1),\
        Long: "Revoke a grantee's access to a repo (addressed by name or ULID). " +\
            "The grantee is a provider-qualified handle (e.g. github:alice) or an " +\
            "account ULID.",\
        Example: "  entire grant repo remove web github:alice --project acme",\
        Args:    cobra.ExactArgs(2),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            mode, err := parseGranteeMode(provider, providerUserID, granteeType, granteeID)\
            if err != nil {\
                cmd.SilenceUsage = true\
                return err\
            }\
            return runCore(cmd, func(ctx context.Context, c *coreapi.Client) error {\
                repoID, err := resolveRepoRef(ctx, c, args[0], project)\
                if err != nil {\
                    return err\
                }\
                if mode == granteeModeProvider {\
                    return revokeGrant(cmd, "Revoked", fmt.Sprintf("%s/%s from repo %s", provider, providerUserID, args[0]), func() error {\
                        return c.RevokeRepoAccessByProvider(ctx, coreapi.RevokeRepoAccessByProviderParams{\
                            RepoId:         repoID,\
                            Provider:       provider,\
                            ProviderUserId: providerUserID,\
                        })\
                    })\
                }\
                return revokeGrant(cmd, "Revoked", fmt.Sprintf("%s %s from repo %s", granteeType, granteeID, args[0]), func() error {\
                    return c.RevokeRepoAccess(ctx, coreapi.RevokeRepoAccessParams{\
                        RepoId:      repoID,\
                        GranteeType: granteeType,\
                        GranteeId:   granteeID,\
                    })\
                })\
                return revokeRepoGrantee(ctx, cmd, c, repoID, args[0], args[1])\
            })\
        },\
    }\
    cmd.Flags().StringVar(&granteeType, "grantee-type", "", "grantee kind: account (with --grantee-id)")\
    cmd.Flags().StringVar(&granteeID, "grantee-id", "", "grantee ULID (with --grantee-type)")\
    cmd.Flags().StringVar(&provider, "provider", "", "identity provider, e.g. github (with --provider-user-id)")\
    cmd.Flags().StringVar(&providerUserID, "provider-user-id", "", "provider-specific user id (with --provider)")\
    bindRepoProjectFlag(cmd, &project)\
    return cmd\
}\
\
// bindGranteeFlags wires the shared --provider / --provider-user-id pair\
// that identifies a grantee across the org/project/repo add+remove verbs,\
// marking both required.\
func bindGranteeFlags(cmd *cobra.Command, provider, providerUserID *string) {\
    cmd.Flags().StringVar(provider, "provider", "", "identity provider (e.g. github) (required)")\
    cmd.Flags().StringVar(providerUserID, "provider-user-id", "", "provider-specific user id (required)")\
    markRequired(cmd, "provider", "provider-user-id")\
// revokeRepoGrantee mirrors revokeProjectGrantee for repos. repoRef is the\
// user's original repo ref, for messaging.\
func revokeRepoGrantee(ctx context.Context, cmd *cobra.Command, c *coreapi.Client, repoID, repoRef, grantee string) error {\
    return revokeGrantee(ctx, cmd, c, "repo", repoRef, grantee,\
        func() error {\
            return c.RevokeRepoAccess(ctx, coreapi.RevokeRepoAccessParams{\
                RepoId:      repoID,\
                GranteeType: "account",\
                GranteeId:   grantee,\
            })\
        },\
        func(provider, providerUserID string) error {\
            return c.RevokeRepoAccessByProvider(ctx, coreapi.RevokeRepoAccessByProviderParams{\
                RepoId:         repoID,\
                Provider:       provider,\
                ProviderUserId: providerUserID,\
            })\
        })\
}\
\
// revokeGrant runs a grant-removal API call idempotently. A 404 means the\
// grantee already has no such grant — the desired end state — so it's reported\
// as a no-op rather than surfaced as a raw error, matching runControlPlaneDelete.\
// verb is the success word ("Revoked"/"Removed"); subject describes the grant,\
// e.g. "github/12345 from repo acme".\
// e.g. "github:alice from repo acme".\
func revokeGrant(cmd *cobra.Command, verb, subject string, revoke func() error) error {\
    if err := revoke(); err != nil {\
        if isCoreNotFound(err) {\
```\
\
Mcmd/entire/cli/grant.go+152/-180\
\
```\
1\
2\
3\
4\
5\
6\
7\
13 unmodified lines\
\
21\
22\
23\
23\
24\
25\
26\
27\
26\
27\
28\
29\
28\
29\
30\
31\
32\
31\
32\
33\
34\
35\
36\
33\
34\
35\
36\
37\
38\
39\
41\
42\
43\
44\
45\
46\
40\
41\
42\
48\
49\
50\
51\
52\
53\
43\
44\
45\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
61\
62\
63\
64\
65\
66\
67\
68\
69\
70\
71\
72\
73\
74\
75\
76\
77\
78\
79\
80\
81\
82\
83\
84\
85\
86\
87\
88\
89\
90\
\
package cli\
\
import (\
    "slices"\
    "testing"\
\
    "github.com/entireio/cli/internal/coreapi"\
13 unmodified lines\
\
    }\
}\
\
func TestParseGranteeMode(t *testing.T) {\
func TestGranteeName(t *testing.T) {\
    t.Parallel()\
    const ulid = "01HZX0000000000000000000AB"\
    tests := []struct {\
        name                                 string\
        provider, providerUserID, gType, gID string\
        want                                 granteeMode\
        wantErr                              bool\
        name string\
        in   coreapi.OptString\
        id   string\
        want string\
    }{\
        {name: "provider mode", provider: "github", providerUserID: "123", want: granteeModeProvider},\
        {name: "id mode", gType: "org", gID: "01J0", want: granteeModeID},\
        {name: "both modes rejected", provider: "github", providerUserID: "123", gType: "org", gID: "01J0", wantErr: true},\
        {name: "partial provider", provider: "github", wantErr: true},\
        {name: "partial id", gType: "org", wantErr: true},\
        {name: "nothing", wantErr: true},\
        {name: "friendly name wins", in: coreapi.NewOptString("github:alice"), id: ulid, want: "github:alice"},\
        {name: "unset falls back to ULID", in: coreapi.OptString{}, id: ulid, want: ulid},\
        {name: "empty string falls back to ULID", in: coreapi.NewOptString(""), id: ulid, want: ulid},\
    }\
    for _, tt := range tests {\
        t.Run(tt.name, func(t *testing.T) {\
            t.Parallel()\
            got, err := parseGranteeMode(tt.provider, tt.providerUserID, tt.gType, tt.gID)\
            if tt.wantErr {\
                if err == nil {\
                    t.Errorf("parseGranteeMode(%q,%q,%q,%q) expected error", tt.provider, tt.providerUserID, tt.gType, tt.gID)\
                }\
                return\
            if got := granteeName(tt.in, tt.id); got != tt.want {\
                t.Errorf("granteeName(%v, %q) = %q, want %q", tt.in, tt.id, got, tt.want)\
            }\
            if err != nil {\
                t.Fatalf("parseGranteeMode: %v", err)\
            }\
            if got != tt.want {\
                t.Errorf("got mode %d, want %d", got, tt.want)\
            }\
        })\
    }\
}\
\
func TestGrantRows(t *testing.T) {\
    t.Parallel()\
    const ulid = "01HZX0000000000000000000AB"\
\
    // grantColumns and the row builders must stay in lockstep — same width,\
    // same column order — or the table header and cells misalign.\
    if got, want := len(grantColumns), 5; got != want {\
        t.Fatalf("grantColumns has %d columns, want %d", got, want)\
    }\
\
    t.Run("project resolved name", func(t *testing.T) {\
        t.Parallel()\
        row := projectGrantRow(coreapi.ProjectGrant{\
            GranteeId:   ulid,\
            GranteeName: coreapi.NewOptString("github:alice"),\
            GranteeType: "account",\
            Role:        "writer",\
            Source:      "direct",\
        })\
        want := []string{"account", "github:alice", ulid, "writer", "direct"}\
        if !slices.Equal(row, want) {\
            t.Errorf("projectGrantRow = %v, want %v", row, want)\
        }\
    })\
\
    t.Run("repo unresolved name falls back to ULID", func(t *testing.T) {\
        t.Parallel()\
        row := repoGrantRow(coreapi.RepoGrant{\
            GranteeId:   ulid,\
            GranteeName: coreapi.OptString{},\
            GranteeType: "team",\
            Role:        "reader",\
            Source:      "inherited",\
        })\
        want := []string{"team", ulid, ulid, "reader", "inherited"}\
        if !slices.Equal(row, want) {\
            t.Errorf("repoGrantRow = %v, want %v", row, want)\
        }\
    })\
}\
\
func TestParseOrgRole(t *testing.T) {\
    t.Parallel()\
    tests := []struct {\
```\
\
Mcmd/entire/cli/grant\_test.go+53/-23\
\
```\
2 unmodified lines\
\
3\
4\
5\
6\
7\
8\
9\
10\
11\
12\
13\
14\
15\
5 unmodified lines\
\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
31\
32\
33\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
44\
45\
46\
47\
48\
22\
23\
24\
25\
49\
50\
51\
52\
53\
54\
6 unmodified lines\
\
61\
62\
63\
38\
64\
65\
66\
67\
68\
69\
44\
70\
71\
72\
73\
74\
75\
50\
76\
77\
78\
79\
80\
81\
56\
82\
83\
84\
85\
86\
87\
62\
88\
89\
90\
91\
1 unmodified line\
\
93\
94\
95\
70\
71\
72\
73\
96\
97\
98\
99\
100\
101\
102\
15 unmodified lines\
\
118\
119\
120\
95\
96\
97\
98\
121\
122\
123\
124\
125\
126\
127\
128\
103\
104\
105\
129\
130\
131\
132\
133\
134\
135\
\
2 unmodified lines\
\
import (\
    "net/http"\
    "net/http/httptest"\
    "strings"\
    "testing"\
\
    "github.com/spf13/cobra"\
    "github.com/stretchr/testify/require"\
\
    "github.com/entireio/cli/internal/coreapi"\
)\
\
// Valid ULID-shaped refs (26 Crockford base32 chars, no I/L/O/U) so the remove\
5 unmodified lines\
\
    wiringGranteeULID = "01HZX7QABCDEFGHJKMNPQRSTVZ"\
)\
\
// grantWiringHandler serves the handle-resolution GET (so a provider:handle\
// grantee resolves to a numeric provider user id) and records the subsequent\
// revoke DELETE. record is called with the DELETE's method and path; deleteFn\
// writes the DELETE response (e.g. 204 or a 404 problem).\
func grantWiringHandler(t *testing.T, record func(method, path string), deleteFn func(w http.ResponseWriter)) http.HandlerFunc {\
    t.Helper()\
    return func(w http.ResponseWriter, r *http.Request) {\
        if r.Method == http.MethodGet && strings.Contains(r.URL.Path, "/identity/handles/") {\
            w.Header().Set("Content-Type", "application/json")\
            if err := writeJSON(w, &coreapi.ResolvedIdentity{\
                AccountId:      wiringGranteeULID,\
                Provider:       providerGitHub,\
                Handle:         "alice",\
                ProviderUserId: "12345",\
            }); err != nil {\
                t.Errorf("encode identity: %v", err)\
            }\
            return\
        }\
        record(r.Method, r.URL.Path)\
        deleteFn(w)\
    }\
}\
\
// TestGrantRemove_RouteWiring drives the grant remove commands through cobra and\
// asserts the grantee-mode → route selection: --provider/--provider-user-id must\
// hit the by-provider revoke route, while --grantee-type/--grantee-id must hit\
// the typed-id route. This locks in the mode→route mapping that grant_test.go's\
// pure-helper tests (parseGranteeMode) can't observe.\
// asserts the grantee-form → route selection: a provider:handle grantee resolves\
// then hits the by-provider revoke route, while an account ULID hits the\
// typed-id route directly. This locks in the grantee→route mapping.\
//\
// Not parallel: runDeleteCmd swaps the package-level activeCoreClient seam.\
func TestGrantRemove_RouteWiring(t *testing.T) {\
6 unmodified lines\
\
        {\
            "repo/by-provider",\
            newGrantRepoRemoveCmd,\
            []string{wiringRepoULID, "--provider", "github", "--provider-user-id", "12345"},\
            []string{wiringRepoULID, "github:alice"},\
            "/api/v1/repos/" + wiringRepoULID + "/grants/account/github/12345",\
        },\
        {\
            "repo/by-grantee-id",\
            newGrantRepoRemoveCmd,\
            []string{wiringRepoULID, "--grantee-type", "account", "--grantee-id", wiringGranteeULID},\
            []string{wiringRepoULID, wiringGranteeULID},\
            "/api/v1/repos/" + wiringRepoULID + "/grants/account/" + wiringGranteeULID,\
        },\
        {\
            "project/by-provider",\
            newGrantProjectRemoveCmd,\
            []string{wiringProjULID, "--provider", "github", "--provider-user-id", "12345"},\
            []string{wiringProjULID, "github:alice"},\
            "/api/v1/projects/" + wiringProjULID + "/grants/account/github/12345",\
        },\
        {\
            "project/by-grantee-id",\
            newGrantProjectRemoveCmd,\
            []string{wiringProjULID, "--grantee-type", "account", "--grantee-id", wiringGranteeULID},\
            []string{wiringProjULID, wiringGranteeULID},\
            "/api/v1/projects/" + wiringProjULID + "/grants/account/" + wiringGranteeULID,\
        },\
        {\
            "org/by-provider",\
            newGrantOrgRemoveCmd,\
            []string{wiringOrgULID, "--provider", "github", "--provider-user-id", "12345"},\
            []string{wiringOrgULID, "github:alice"},\
            "/api/v1/orgs/" + wiringOrgULID + "/members/github/12345",\
        },\
    }\
1 unmodified line\
\
    for _, tc := range cases {\
        t.Run(tc.name, func(t *testing.T) {\
            var gotMethod, gotPath string\
            srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {\
                gotMethod, gotPath = r.Method, r.URL.Path\
                w.WriteHeader(http.StatusNoContent)\
            }))\
            srv := httptest.NewServer(grantWiringHandler(t,\
                func(method, path string) { gotMethod, gotPath = method, path },\
                func(w http.ResponseWriter) { w.WriteHeader(http.StatusNoContent) },\
            ))\
            t.Cleanup(srv.Close)\
\
            _, err := runDeleteCmd(t, tc.newCmd, srv.URL, tc.args...)\
15 unmodified lines\
\
        newCmd func() *cobra.Command\
        args   []string\
    }{\
        {"repo/by-provider", newGrantRepoRemoveCmd, []string{wiringRepoULID, "--provider", "github", "--provider-user-id", "12345"}},\
        {"repo/by-grantee-id", newGrantRepoRemoveCmd, []string{wiringRepoULID, "--grantee-type", "account", "--grantee-id", wiringGranteeULID}},\
        {"project/by-provider", newGrantProjectRemoveCmd, []string{wiringProjULID, "--provider", "github", "--provider-user-id", "12345"}},\
        {"org/by-provider", newGrantOrgRemoveCmd, []string{wiringOrgULID, "--provider", "github", "--provider-user-id", "12345"}},\
        {"repo/by-provider", newGrantRepoRemoveCmd, []string{wiringRepoULID, "github:alice"}},\
        {"repo/by-grantee-id", newGrantRepoRemoveCmd, []string{wiringRepoULID, wiringGranteeULID}},\
        {"project/by-provider", newGrantProjectRemoveCmd, []string{wiringProjULID, "github:alice"}},\
        {"org/by-provider", newGrantOrgRemoveCmd, []string{wiringOrgULID, "github:alice"}},\
    }\
\
    for _, tc := range cases {\
        t.Run(tc.name, func(t *testing.T) {\
            srv := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, _ *http.Request) {\
                writeNotFoundProblem(t, w)\
            }))\
            srv := httptest.NewServer(grantWiringHandler(t,\
                func(_, _ string) {},\
                func(w http.ResponseWriter) { writeNotFoundProblem(t, w) },\
            ))\
            t.Cleanup(srv.Close)\
\
            out, err := runDeleteCmd(t, tc.newCmd, srv.URL, tc.args...)\
```\
\
Mcmd/entire/cli/grant\_wiring\_test.go+47/-20\
\
```\
241 unmodified lines\
\
242\
243\
244\
245\
246\
247\
248\
249\
250\
251\
252\
\
241 unmodified lines\
\
    // so ErrStateNotFound is the normal first-session path — only warn on\
    // genuinely unexpected errors, matching the rest of this file.\
    mutErr := strategy.MutateSessionState(ctx, event.SessionID, func(state *strategy.SessionState) error {\
        if state.AdoptedIntoWorktreePath != "" {\
            logging.Info(logCtx, "skipping adopted-away source session start",\
                slog.String("adopted_into_worktree", state.AdoptedIntoWorktreePath))\
            return strategy.ErrMutationSkip\
        }\
        persistEventMetadataToState(event, state)\
        if transErr := strategy.TransitionAndLog(ctx, state, session.EventSessionStart, session.TransitionContext{}, session.NoOpActionHandler{}); transErr != nil {\
            logging.Warn(logCtx, "session start transition failed",\
```\
\
Mcmd/entire/cli/lifecycle.go+5\
\
```\
45 unmodified lines\
\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
61\
62\
63\
64\
65\
66\
67\
114 unmodified lines\
\
182\
183\
184\
169\
185\
186\
187\
188\
\
45 unmodified lines\
\
    return []string{r.ID, r.Name, r.OwningProjectId, r.ClusterHost.Or("-"), state}\
}\
\
// repoDetailColumns / repoDetailRow extend the shared repo view with the\
// entire:// clone URL for the single-repo `get` output. The list view stays on\
// the lean repoColumns — a full clone URL per row would bloat the table — but a\
// person inspecting one repo wants the URL they can paste into `git clone`\
// (COR-699). REMOTE is "-" until the repo is provisioned enough to have a\
// resolvable cluster host + path.\
var repoDetailColumns = []string{"ID", "NAME", "PROJECT", "CLUSTER", "STATE", "REMOTE"}\
\
func repoDetailRow(r coreapi.Repo) []string {\
    remote := repoRemoteURL(r)\
    if remote == "" {\
        remote = "-"\
    }\
    return append(repoRow(r), remote)\
}\
\
// repoRemoteURL synthesizes the entire:// clone/remote URL for a repo from\
// its resolved cluster host and path — the form `git clone` and\
// `git remote add` accept, which git-remote-entire reads back as the repo\
114 unmodified lines\
\
        Short: "Show a repository by name or ULID",\
        Args:  cobra.ExactArgs(1),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            return runCoreObject(cmd, repoColumns, repoRow, func(ctx context.Context, c *coreapi.Client) (*coreapi.Repo, error) {\
            return runCoreObject(cmd, repoDetailColumns, repoDetailRow, func(ctx context.Context, c *coreapi.Client) (*coreapi.Repo, error) {\
                repoID, err := resolveRepoRef(ctx, c, args[0], project)\
                if err != nil {\
                    return nil, err\
```\
\
Mcmd/entire/cli/repo.go+17/-1\
\
```\
93 unmodified lines\
\
94\
95\
96\
97\
98\
99\
100\
101\
102\
103\
104\
105\
106\
107\
108\
109\
110\
111\
112\
113\
114\
115\
116\
117\
118\
119\
120\
121\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
134\
\
93 unmodified lines\
\
    }\
}\
\
func TestRepoDetailRow(t *testing.T) {\
    t.Parallel()\
\
    t.Run("includes the entire:// remote", func(t *testing.T) {\
        t.Parallel()\
        row := repoDetailRow(coreapi.Repo{\
            ID:              "01KS6KFJR2XS6PZ188MVYE07AN",\
            Name:            "web",\
            OwningProjectId: "01KS6KFJR2XS6PZ188MVYE07AP",\
            ClusterHost:     coreapi.NewOptString("aws-us-east-2.entire.io"),\
            Path:            coreapi.NewOptString("acme/web"),\
            State:           coreapi.NewOptRepoState(coreapi.RepoStateActive),\
        })\
        if len(row) != len(repoDetailColumns) {\
            t.Fatalf("row has %d cells, want %d (one per column)", len(row), len(repoDetailColumns))\
        }\
        if want := "entire://aws-us-east-2.entire.io/acme/web"; row[len(row)-1] != want {\
            t.Errorf("REMOTE cell = %q, want %q", row[len(row)-1], want)\
        }\
    })\
\
    t.Run("shows - when the remote is not yet resolvable", func(t *testing.T) {\
        t.Parallel()\
        row := repoDetailRow(coreapi.Repo{\
            ID:              "01KS6KFJR2XS6PZ188MVYE07AN",\
            Name:            "web",\
            OwningProjectId: "01KS6KFJR2XS6PZ188MVYE07AP",\
            ClusterHost:     coreapi.NewOptString("aws-us-east-2.entire.io"),\
        })\
        if row[len(row)-1] != "-" {\
            t.Errorf("REMOTE cell = %q, want %q", row[len(row)-1], "-")\
        }\
    })\
}\
\
func TestRepoCreateOutput_StampsRemote(t *testing.T) {\
    t.Parallel()\
    repo := &coreapi.Repo{\
```\
\
Mcmd/entire/cli/repo\_test.go+35\
\
```\
19 unmodified lines\
\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
31\
32\
69 unmodified lines\
\
102\
103\
104\
105\
106\
107\
108\
109\
110\
111\
112\
113\
114\
115\
116\
117\
118\
119\
120\
121\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
134\
135\
136\
137\
138\
139\
140\
141\
142\
143\
144\
145\
146\
147\
31 unmodified lines\
\
179\
180\
181\
135\
136\
137\
138\
182\
183\
184\
185\
186\
187\
188\
\
19 unmodified lines\
\
// under the response's singular `org`/`project` field, or 404) — the CLI never\
// lists everything and filters client-side.\
\
// providerGitHub is the identity-provider slug for GitHub-backed accounts, the\
// provider half of a qualified grantee handle like "github:alice". GitHub is the\
// only provider with backing accounts today; other slugs resolve once they exist\
// server-side. (Distinct from setup.go's checkpointProviderGitHub, which names\
// the checkpoint hosting provider — same string, unrelated concern.)\
const providerGitHub = "github"\
\
// looksLikeULID reports whether s has the shape of a ULID: 26 characters drawn\
// from Crockford base32 (digits plus uppercase letters, excluding I, L, O, U).\
// The check is shape-only and case-insensitive on the alphabet; it never hits\
69 unmodified lines\
\
    return id.AccountId, nil\
}\
\
// resolveGranteeProvider turns a grantee reference into the (provider,\
// providerUserId) pair the grant/membership "by provider" routes key on. The\
// reference is a provider-qualified handle (e.g. "github:alice"); it is\
// resolved through the control plane to the provider's stable numeric user id.\
// The friendly handle alone is not what the grant routes accept — passing it as\
// --provider-user-id was the COR-699 footgun ("provider identity not found") —\
// so the CLI always resolves it first. A bare account ULID is rejected here:\
// the by-provider routes can't be addressed by ULID, and there is no reverse\
// account→provider-id lookup; callers that accept a ULID grantee (project/repo\
// remove) handle it via the typed-id route before reaching this helper.\
func resolveGranteeProvider(ctx context.Context, c *coreapi.Client, ref string) (provider, providerUserID string, err error) {\
    // A ULID is a tempting paste from `grant … list` (which prints the grantee\
    // ID), but the by-provider routes can't be addressed by ULID. Reject it with\
    // a message that points at the form this command actually wants, rather than\
    // letting parseQualifiedHandle dangle a "(or a ULID)" hint that doesn't apply.\
    if looksLikeULID(ref) {\
        return "", "", fmt.Errorf("grantee %q is an account ULID; this command needs a provider-qualified handle like \"github:alice\"", ref)\
    }\
    p, handle, err := parseQualifiedHandle(ref)\
    if err != nil {\
        return "", "", err\
    }\
    id, err := c.ResolveHandle(ctx, coreapi.ResolveHandleParams{Provider: p, Handle: handle})\
    if err != nil {\
        if isCoreNotFound(err) {\
            return "", "", fmt.Errorf("no %s identity for handle %q", p, handle)\
        }\
        return "", "", err\
    }\
    if id.ProviderUserId == "" {\
        return "", "", fmt.Errorf("handle %q resolved to no provider user id", ref)\
    }\
    // Prefer the server-normalized provider over the raw prefix, falling back to\
    // the input when the response omits it.\
    if id.Provider != "" {\
        p = id.Provider\
    }\
    return p, id.ProviderUserId, nil\
}\
\
// parseQualifiedHandle splits a provider-qualified handle like "github:alice"\
// into its provider ("github") and handle ("alice"). Accounts are addressed by\
// this friendly form; a value with no "provider:" prefix is rejected so the\
31 unmodified lines\
\
// resolveRepoRef turns a repo reference into its ULID. A ULID passes through.\
// A name requires a project scope (projectRef, itself a name or ULID) because\
// repo names are unique only within a project: the repo is resolved via the\
// server's case-insensitive by-name lookup, scoped to that project. A\
// name-filtered query returns the single match in the response's `repo`\
// field (empty when there's no match) — the `repos` array is only populated\
// for unfiltered list pages.\
// server's case-insensitive by-name lookup, scoped to that project. Like the\
// org/project endpoints, a name-filtered list returns the single match under the\
// response's singular `repo` field (the plural `repos` is only populated for an\
// unfiltered page) — reading `repos` here was the COR-699 bug.\
func resolveRepoRef(ctx context.Context, c *coreapi.Client, ref, projectRef string) (string, error) {\
    if looksLikeULID(ref) {\
        return ref, nil\
```\
\
Mcmd/entire/cli/resolveref.go+51/-4\
\
```\
203 unmodified lines\
\
204\
205\
206\
207\
208\
207\
208\
209\
210\
211\
212\
213\
214\
96 unmodified lines\
\
311\
312\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
324\
325\
326\
327\
328\
329\
330\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
350\
351\
352\
353\
354\
355\
356\
357\
358\
359\
360\
361\
362\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
375\
376\
377\
378\
379\
380\
381\
382\
383\
\
203 unmodified lines\
\
        var gotName string\
        c, calls := resolveTestClient(t, func(w http.ResponseWriter, r *http.Request) {\
            gotName = r.URL.Query().Get("name")\
            // A name-filtered query returns the match in the singular `repo`\
            // field, not the `repos` page array — mirror the real server.\
            // A name-filtered list returns the single match under the singular\
            // `repo` field (like org/project) — NOT the plural `repos` array,\
            // which is only populated for an unfiltered page. Reading `repos`\
            // here was the COR-699 bug, so the fixture must mirror the real\
            // server's singular field to keep that regression covered.\
            if err := writeJSON(w, &coreapi.ListProjectReposOutputBody{Repo: coreapi.NewOptRepo(coreapi.Repo{ID: ulidRepoWeb, Name: "web"})}); err != nil {\
                t.Errorf("encode repo: %v", err)\
            }\
96 unmodified lines\
\
    })\
}\
\
func TestResolveGranteeProvider(t *testing.T) {\
    t.Parallel()\
\
    t.Run("handle resolves to the provider user id in one call", func(t *testing.T) {\
        t.Parallel()\
        c, calls := resolveTestClient(t, func(w http.ResponseWriter, _ *http.Request) {\
            if err := writeJSON(w, &coreapi.ResolvedIdentity{AccountId: ulidResolvedAcct, Provider: providerGitHub, Handle: "alice", ProviderUserId: "12345"}); err != nil {\
                t.Errorf("encode identity: %v", err)\
            }\
        })\
        provider, puid, err := resolveGranteeProvider(context.Background(), c, "github:alice")\
        if err != nil {\
            t.Fatalf("resolveGranteeProvider: %v", err)\
        }\
        if provider != providerGitHub || puid != "12345" {\
            t.Errorf("resolveGranteeProvider = (%q, %q), want (github, 12345)", provider, puid)\
        }\
        if n := calls.Load(); n != 1 {\
            t.Errorf("handle ref made %d HTTP calls, want 1", n)\
        }\
    })\
\
    t.Run("non-qualified handle fails before any network call", func(t *testing.T) {\
        t.Parallel()\
        c, calls := resolveTestClient(t, func(w http.ResponseWriter, _ *http.Request) {\
            t.Error("unexpected HTTP call for an invalid handle")\
            w.WriteHeader(http.StatusInternalServerError)\
        })\
        if _, _, err := resolveGranteeProvider(context.Background(), c, "alice"); err == nil {\
            t.Error("resolveGranteeProvider expected error for non-qualified handle")\
        }\
        if n := calls.Load(); n != 0 {\
            t.Errorf("invalid handle made %d HTTP calls, want 0", n)\
        }\
    })\
\
    t.Run("account ULID is rejected before any network call", func(t *testing.T) {\
        t.Parallel()\
        c, calls := resolveTestClient(t, func(w http.ResponseWriter, _ *http.Request) {\
            t.Error("unexpected HTTP call for a ULID grantee")\
            w.WriteHeader(http.StatusInternalServerError)\
        })\
        _, _, err := resolveGranteeProvider(context.Background(), c, wiringGranteeULID)\
        if err == nil {\
            t.Fatal("resolveGranteeProvider expected error for a ULID grantee")\
        }\
        if !strings.Contains(err.Error(), "provider-qualified handle") {\
            t.Errorf("error %q should point at the provider-qualified handle form", err)\
        }\
        if n := calls.Load(); n != 0 {\
            t.Errorf("ULID grantee made %d HTTP calls, want 0", n)\
        }\
    })\
\
    t.Run("empty provider user id is an error", func(t *testing.T) {\
        t.Parallel()\
        c, _ := resolveTestClient(t, func(w http.ResponseWriter, _ *http.Request) {\
            if err := writeJSON(w, &coreapi.ResolvedIdentity{AccountId: ulidResolvedAcct, Provider: providerGitHub, Handle: "alice", ProviderUserId: ""}); err != nil {\
                t.Errorf("encode identity: %v", err)\
            }\
        })\
        if _, _, err := resolveGranteeProvider(context.Background(), c, "github:alice"); err == nil {\
            t.Error("resolveGranteeProvider expected error for empty provider user id")\
        }\
    })\
}\
\
func TestLooksLikeULID(t *testing.T) {\
    t.Parallel()\
    tests := []struct {\
```\
\
Mcmd/entire/cli/resolveref\_test.go+72/-2\
\
```\
112 unmodified lines\
\
113\
114\
115\
116\
117\
118\
119\
120\
121\
122\
123\
124\
125\
126\
127\
128\
265 unmodified lines\
\
394\
395\
396\
387\
388\
397\
398\
399\
400\
10 unmodified lines\
\
411\
412\
413\
414\
415\
416\
417\
418\
419\
420\
421\
422\
423\
424\
\
112 unmodified lines\
\
    // Derived from .git/worktrees/<name>/, stable across git worktree move\
    WorktreeID string `json:"worktree_id,omitempty"`\
\
    // AdoptedIntoWorktreePath marks a source-side tombstone left behind after\
    // `entire session adopt` moves this session into another repository/worktree.\
    // Hook TurnStart must not reactivate tombstoned source records, otherwise the\
    // same session ID can diverge in two session stores.\
    AdoptedIntoWorktreePath string `json:"adopted_into_worktree_path,omitempty"`\
\
    // AdoptedIntoWorktreeID is the target worktree ID paired with\
    // AdoptedIntoWorktreePath when available.\
    AdoptedIntoWorktreeID string `json:"adopted_into_worktree_id,omitempty"`\
\
    // Branch is the git branch HEAD pointed at the last time this session took a\
    // turn. Captured on each turn start so it tracks branches created or renamed\
    // after the session began. Empty when HEAD was detached or for sessions\
265 unmodified lines\
\
    // will see 0 for these fields and fall back to scoping from the transcript start.\
    // This is acceptable since CLI upgrades are monotonic and the worst case is\
    // redundant transcript content in a condensation, not data loss.\
    s.CondensedTranscriptLines = 0\
    s.TranscriptLinesAtStart = 0\
    s.ClearLegacyTranscriptOffsets()\
\
    // Backfill AttributionBaseCommit for sessions created before this field existed.\
    // Without this, a mid-turn commit would migrate BaseCommit and the fallback in\
10 unmodified lines\
\
    }\
}\
\
// ClearLegacyTranscriptOffsets clears deprecated transcript offset fields so\
// callers that intentionally reset CheckpointTranscriptStart do not re-persist\
// stale legacy state.\
func (s *State) ClearLegacyTranscriptOffsets() {\
    s.CondensedTranscriptLines = 0\
    s.TranscriptLinesAtStart = 0\
}\
\
// RealignAttributionBase sets AttributionBaseCommit to newBase and clears any\
// bookkeeping whose meaning depends on attribution being diverged from the\
// shadow-branch base. Call this every time a code path intentionally brings\
```\
\
Mcmd/entire/cli/session/state.go+19/-2\
\
```\
1\
2\
3\
4\
5\
6\
7\
8\
9\
10\
11\
12\
13\
14\
15\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
31\
32\
33\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
44\
45\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
61\
62\
63\
64\
65\
66\
67\
68\
69\
70\
71\
72\
73\
74\
75\
76\
77\
78\
79\
80\
81\
82\
83\
84\
85\
86\
87\
88\
89\
90\
91\
92\
93\
94\
95\
96\
97\
98\
99\
100\
101\
102\
103\
104\
105\
106\
107\
108\
109\
110\
111\
112\
113\
114\
115\
116\
117\
118\
119\
120\
121\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
134\
135\
136\
137\
138\
139\
140\
141\
142\
143\
144\
145\
146\
147\
148\
149\
150\
151\
152\
153\
154\
155\
156\
157\
158\
159\
160\
161\
162\
163\
164\
165\
166\
167\
168\
169\
170\
171\
172\
173\
174\
175\
176\
177\
178\
179\
180\
181\
182\
183\
184\
185\
186\
187\
188\
189\
190\
191\
192\
193\
194\
195\
196\
197\
198\
199\
200\
201\
202\
203\
204\
205\
206\
207\
208\
209\
210\
211\
212\
213\
214\
215\
216\
217\
218\
219\
220\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
231\
232\
233\
234\
235\
236\
237\
238\
239\
240\
241\
242\
243\
244\
245\
246\
247\
248\
249\
250\
251\
252\
253\
254\
255\
256\
257\
258\
259\
260\
261\
262\
263\
264\
265\
266\
267\
268\
269\
270\
271\
272\
273\
274\
275\
276\
277\
278\
279\
280\
281\
282\
283\
284\
285\
286\
287\
288\
289\
290\
291\
292\
293\
294\
295\
296\
297\
298\
299\
300\
301\
302\
303\
304\
305\
306\
307\
308\
309\
310\
311\
312\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
324\
325\
326\
327\
328\
329\
330\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
350\
351\
352\
353\
354\
355\
356\
357\
358\
359\
360\
361\
362\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
375\
376\
377\
378\
379\
380\
381\
382\
383\
384\
385\
386\
387\
388\
389\
390\
391\
392\
393\
394\
395\
396\
397\
398\
399\
400\
401\
402\
403\
404\
405\
406\
407\
408\
409\
410\
411\
412\
413\
414\
415\
416\
417\
418\
419\
420\
421\
422\
423\
424\
425\
426\
427\
428\
429\
430\
431\
432\
433\
434\
435\
436\
437\
438\
439\
440\
441\
442\
443\
444\
445\
446\
447\
448\
449\
450\
451\
452\
453\
454\
455\
456\
457\
458\
459\
460\
461\
462\
463\
464\
465\
466\
467\
468\
469\
470\
471\
472\
473\
474\
475\
476\
477\
478\
479\
480\
481\
482\
483\
484\
485\
486\
487\
488\
489\
490\
491\
492\
493\
494\
495\
496\
497\
498\
499\
500\
501\
502\
503\
504\
505\
506\
507\
508\
509\
510\
511\
512\
513\
514\
515\
516\
517\
518\
519\
520\
521\
522\
523\
524\
525\
526\
527\
528\
529\
530\
531\
532\
533\
534\
535\
536\
537\
538\
539\
540\
541\
542\
543\
544\
545\
546\
547\
548\
549\
550\
551\
552\
553\
554\
555\
556\
557\
558\
559\
560\
561\
562\
563\
564\
565\
566\
567\
568\
569\
570\
571\
572\
573\
574\
575\
576\
577\
578\
579\
580\
581\
582\
583\
584\
585\
586\
587\
588\
589\
590\
591\
592\
593\
594\
595\
596\
\
package cli\
\
import (\
    "bytes"\
    "context"\
    "errors"\
    "fmt"\
    "io"\
    "maps"\
    "os/exec"\
    "path/filepath"\
    "slices"\
    "sort"\
    "strings"\
    "time"\
\
    "github.com/entireio/cli/cmd/entire/cli/agent"\
    "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"\
    "github.com/entireio/cli/cmd/entire/cli/paths"\
    "github.com/entireio/cli/cmd/entire/cli/session"\
    "github.com/entireio/cli/cmd/entire/cli/strategy"\
    "github.com/entireio/cli/cmd/entire/cli/versioninfo"\
    "github.com/spf13/cobra"\
)\
\
type adoptOptions struct {\
    FromWorktree string\
    Force        bool\
}\
\
const adoptRecentWindow = 12 * time.Hour\
\
func newAdoptCmd() *cobra.Command {\
    var opts adoptOptions\
\
    cmd := &cobra.Command{\
        Use:   "adopt [session-id]",\
        Short: "Adopt an active session from another worktree",\
        Long: `Adopt an active session from another worktree into the current repository.\
\
This is useful when an agent starts in one repository or worktree, then moves\
and makes changes in another. Adoption moves the live session state into the\
current repo and seeds it with the current repo's uncommitted file changes so\
the next commit can be linked normally.\
\
When the source and target share a Git session store, adoption moves the same\
session state file to the current worktree and requires --force or --yes.`,\
        Example: `  entire session adopt 019ed5fe-ec49-7a72-89fd-f38e323f5448 --from ../cli\
  entire session adopt --from /path/to/source/worktree\
  entire session adopt --from ../source-worktree --yes`,\
        Args: cobra.MaximumNArgs(1),\
        RunE: func(cmd *cobra.Command, args []string) error {\
            sessionID := ""\
            if len(args) > 0 {\
                sessionID = args[0]\
            }\
            return runAdopt(cmd.Context(), cmd.OutOrStdout(), sessionID, opts)\
        },\
    }\
\
    cmd.Flags().StringVar(&opts.FromWorktree, "from", "", "source worktree that already tracks the session")\
    cmd.Flags().BoolVar(&opts.Force, "force", false, "replace an existing local state file for the same session")\
    cmd.Flags().BoolVar(&opts.Force, "yes", false, "confirm same-store adoption and replacement without prompting")\
\
    return cmd\
}\
\
func runAdopt(ctx context.Context, w io.Writer, sessionID string, opts adoptOptions) error {\
    if strings.TrimSpace(opts.FromWorktree) == "" {\
        return errors.New("source worktree is required; pass --from <path>")\
    }\
\
    sourceStore, sourceWorktree, sourceCommonDir, err := stateStoreForWorktree(ctx, opts.FromWorktree)\
    if err != nil {\
        return err\
    }\
\
    targetStore, targetWorktree, targetCommonDir, err := stateStoreForWorktree(ctx, ".")\
    if err != nil {\
        return fmt.Errorf("open current session store: %w", err)\
    }\
    sameSessionStore := sameAdoptStore(sourceCommonDir, targetCommonDir)\
    if sameSessionStore && sameAdoptPath(sourceWorktree, targetWorktree) {\
        return errors.New("source and target are the same worktree; no session adoption is needed")\
    }\
\
    sourceState, err := selectAdoptSourceSession(ctx, sourceStore, sourceWorktree, sessionID)\
    if err != nil {\
        return err\
    }\
    if err := validateAdoptSourceTranscript(sourceState, sourceWorktree); err != nil {\
        return err\
    }\
\
    var adopted *session.State\
    var filesTouched []string\
    if sameSessionStore {\
        adopted, filesTouched, err = adoptFromSameSessionStore(ctx, sourceWorktree, sourceState, opts)\
    } else {\
        adopted, filesTouched, err = adoptFromExternalSessionStore(\
            ctx,\
            sourceStore,\
            sourceWorktree,\
            sourceCommonDir,\
            targetStore,\
            targetCommonDir,\
            sourceState.SessionID,\
            opts,\
        )\
    }\
    if err != nil {\
        return err\
    }\
\
    fmt.Fprintf(w, "Adopted session %s from %s\n", shortSessionID(adopted.SessionID), sourceWorktree)\
    if len(filesTouched) == 0 {\
        fmt.Fprintln(w, "No current file changes were detected, so the next commit may not link until hooks record changes.")\
        return nil\
    }\
    fmt.Fprintf(w, "Tracking %d file(s): %s\n", len(filesTouched), strings.Join(filesTouched, ", "))\
    fmt.Fprintln(w, "Review tracked files before committing; adoption attributes current changes in this repo to the adopted session.")\
    return nil\
}\
\
func adoptFromExternalSessionStore(\
    ctx context.Context,\
    sourceStore *session.StateStore,\
    sourceWorktree string,\
    sourceCommonDir string,\
    targetStore *session.StateStore,\
    targetCommonDir string,\
    sessionID string,\
    opts adoptOptions,\
) (*session.State, []string, error) {\
    sourceWorktreeID, worktreeIDErr := paths.GetWorktreeID(sourceWorktree)\
    if worktreeIDErr != nil {\
        sourceWorktreeID = ""\
    }\
\
    var adopted *session.State\
    var filesTouched []string\
    err := strategy.WithSessionStateLocks(ctx, sessionID, []string{sourceCommonDir, targetCommonDir}, func() error {\
        sourceState, err := sourceStore.Load(ctx, sessionID)\
        if err != nil {\
            return fmt.Errorf("load source session state: %w", err)\
        }\
        if sourceState == nil {\
            return fmt.Errorf("session %s was not found in %s", sessionID, sourceWorktree)\
        }\
        if !isAdoptableSourceSession(sourceState) {\
            return fmt.Errorf("session %s is ended or fully condensed and cannot be adopted", sessionID)\
        }\
        if !sessionBelongsToSourceWorktree(sourceState, sourceWorktree, sourceWorktreeID) {\
            return fmt.Errorf("session %s belongs to %s, not %s",\
                sessionID, adoptSessionWorktreeLabel(sourceState), sourceWorktree)\
        }\
        if err := validateAdoptSourceTranscript(sourceState, sourceWorktree); err != nil {\
            return err\
        }\
\
        next, touched, err := buildAdoptedSessionState(ctx, sourceState)\
        if err != nil {\
            return err\
        }\
        existing, err := targetStore.Load(ctx, next.SessionID)\
        if err != nil {\
            return fmt.Errorf("load current session state: %w", err)\
        }\
        if existing != nil && !opts.Force {\
            return fmt.Errorf("session %s is already tracked in this repo; rerun with --force to replace it", next.SessionID)\
        }\
        if err := targetStore.Save(ctx, next); err != nil {\
            return fmt.Errorf("save adopted session state: %w", err)\
        }\
        retired := retireAdoptedSourceSession(sourceState, next)\
        if err := sourceStore.Save(ctx, &retired); err != nil {\
            if rollbackErr := rollbackExternalAdoptTarget(ctx, targetStore, next.SessionID, existing); rollbackErr != nil {\
                return fmt.Errorf("retire source session state: %w; rollback adopted target session state: %w", err, rollbackErr)\
            }\
            return fmt.Errorf("retire source session state: %w", err)\
        }\
        adopted = next\
        filesTouched = touched\
        return nil\
    })\
    if err != nil {\
        return nil, nil, fmt.Errorf("adopt external session state: %w", err)\
    }\
    return adopted, filesTouched, nil\
}\
\
func rollbackExternalAdoptTarget(ctx context.Context, targetStore *session.StateStore, sessionID string, previous *session.State) error {\
    if previous == nil {\
        if err := targetStore.Clear(ctx, sessionID); err != nil {\
            return fmt.Errorf("clear adopted target session state: %w", err)\
        }\
        return nil\
    }\
    if err := targetStore.Save(ctx, previous); err != nil {\
        return fmt.Errorf("restore previous target session state: %w", err)\
    }\
    return nil\
}\
\
func retireAdoptedSourceSession(source, target *session.State) session.State {\
    now := time.Now()\
    retired := cloneAdoptSourceState(source)\
    retired.Phase = session.PhaseEnded\
    retired.EndedAt = &now\
    retired.FullyCondensed = true\
    retired.Owner = nil\
    retired.FilesTouched = nil\
    retired.TurnID = ""\
    retired.TurnCheckpointIDs = nil\
    retired.AdoptedIntoWorktreePath = target.WorktreePath\
    retired.AdoptedIntoWorktreeID = target.WorktreeID\
    return retired\
}\
\
func adoptFromSameSessionStore(ctx context.Context, sourceWorktree string, sourceState *session.State, opts adoptOptions) (*session.State, []string, error) {\
    if !opts.Force {\
        return nil, nil, fmt.Errorf("session %s is already tracked in this repo; rerun with --force to replace it", sourceState.SessionID)\
    }\
\
    sourceWorktreeID, worktreeIDErr := paths.GetWorktreeID(sourceWorktree)\
    if worktreeIDErr != nil {\
        sourceWorktreeID = ""\
    }\
\
    var adopted *session.State\
    var filesTouched []string\
    err := strategy.MutateSessionState(ctx, sourceState.SessionID, func(current *strategy.SessionState) error {\
        if !isAdoptableSourceSession(current) {\
            return fmt.Errorf("session %s is ended or fully condensed and cannot be adopted", sourceState.SessionID)\
        }\
        if !sessionBelongsToSourceWorktree(current, sourceWorktree, sourceWorktreeID) {\
            return fmt.Errorf("session %s belongs to %s, not %s",\
                sourceState.SessionID, adoptSessionWorktreeLabel(current), sourceWorktree)\
        }\
        if err := validateAdoptSourceTranscript(current, sourceWorktree); err != nil {\
            return err\
        }\
\
        next, touched, err := buildAdoptedSessionState(ctx, current)\
        if err != nil {\
            return err\
        }\
        *current = *next\
        snapshot := cloneAdoptSourceState(next)\
        adopted = &snapshot\
        filesTouched = touched\
        return nil\
    })\
    if errors.Is(err, strategy.ErrStateNotFound) {\
        return nil, nil, fmt.Errorf("session %s was not found in %s", sourceState.SessionID, sourceWorktree)\
    }\
    if err != nil {\
        return nil, nil, fmt.Errorf("adopt same-store session state: %w", err)\
    }\
    return adopted, filesTouched, nil\
}\
\
func validateAdoptSourceTranscript(source *session.State, sourceWorktree string) error {\
    if source == nil || strings.TrimSpace(source.TranscriptPath) == "" {\
        return nil\
    }\
\
    owner, ok := agent.AgentForTranscriptPath(source.TranscriptPath, sourceWorktree)\
    if !ok {\
        return fmt.Errorf("unexpected transcript path for session %s: %s is not owned by a registered agent for %s",\
            source.SessionID, source.TranscriptPath, sourceWorktree)\
    }\
    if source.AgentType != "" && owner.Type() != source.AgentType {\
        return fmt.Errorf("unexpected transcript path for session %s: %s belongs to %s, but source state says %s",\
            source.SessionID, source.TranscriptPath, owner.Type(), source.AgentType)\
    }\
    return nil\
}\
\
func stateStoreForWorktree(ctx context.Context, worktreePath string) (*session.StateStore, string, string, error) {\
    absWorktree, err := filepath.Abs(worktreePath)\
    if err != nil {\
        return nil, "", "", fmt.Errorf("resolve source worktree: %w", err)\
    }\
\
    cmd := exec.CommandContext(ctx, "git", "-C", absWorktree, "rev-parse", "--show-toplevel", "--git-common-dir")\
    var stderr bytes.Buffer\
    cmd.Stderr = &stderr\
    output, err := cmd.Output()\
    if err != nil {\
        msg := strings.TrimSpace(stderr.String())\
        if msg != "" {\
            return nil, "", "", fmt.Errorf("resolve source git directory: %s: %w", msg, err)\
        }\
        return nil, "", "", fmt.Errorf("resolve source git directory: %w", err)\
    }\
\
    lines := strings.Split(strings.TrimSpace(string(output)), "\n")\
    if len(lines) < 2 {\
        return nil, "", "", fmt.Errorf("resolve source git directory: unexpected git output %q", strings.TrimSpace(string(output)))\
    }\
    sourceRoot := strings.TrimSpace(lines[0])\
    commonDir := strings.TrimSpace(lines[1])\
    if !filepath.IsAbs(commonDir) {\
        commonDir = filepath.Join(absWorktree, commonDir)\
    }\
    commonDir = filepath.Clean(commonDir)\
\
    return session.NewStateStoreWithDir(filepath.Join(commonDir, session.SessionStateDirName)), sourceRoot, commonDir, nil\
}\
\
func selectAdoptSourceSession(ctx context.Context, store *session.StateStore, sourceWorktree, sessionID string) (*session.State, error) {\
    sourceWorktreeID, worktreeIDErr := paths.GetWorktreeID(sourceWorktree)\
    if worktreeIDErr != nil {\
        sourceWorktreeID = ""\
    }\
    if sessionID != "" {\
        sourceState, err := store.Load(ctx, sessionID)\
        if err != nil {\
            return nil, fmt.Errorf("load source session state: %w", err)\
        }\
        if sourceState == nil {\
            return nil, fmt.Errorf("session %s was not found in %s", sessionID, sourceWorktree)\
        }\
        if !isAdoptableSourceSession(sourceState) {\
            return nil, fmt.Errorf("session %s is ended or fully condensed and cannot be adopted", sessionID)\
        }\
        if !sessionBelongsToSourceWorktree(sourceState, sourceWorktree, sourceWorktreeID) {\
            return nil, fmt.Errorf("session %s belongs to %s, not %s",\
                sessionID, adoptSessionWorktreeLabel(sourceState), sourceWorktree)\
        }\
        return sourceState, nil\
    }\
\
    states, err := store.List(ctx)\
    if err != nil {\
        return nil, fmt.Errorf("list source sessions: %w", err)\
    }\
    candidates := make([]*session.State, 0, len(states))\
    for _, state := range states {\
        if isRecentAdoptCandidate(state) && sessionBelongsToSourceWorktree(state, sourceWorktree, sourceWorktreeID) {\
            candidates = append(candidates, state)\
        }\
    }\
    sort.Slice(candidates, func(i, j int) bool {\
        return sessionLastSeen(candidates[i]).After(sessionLastSeen(candidates[j]))\
    })\
\
    switch len(candidates) {\
    case 0:\
        return nil, fmt.Errorf("no recent active sessions found in %s", sourceWorktree)\
    case 1:\
        return candidates[0], nil\
    default:\
        ids := make([]string, 0, len(candidates))\
        for _, candidate := range candidates {\
            ids = append(ids, candidate.SessionID)\
        }\
        return nil, fmt.Errorf("multiple recent active sessions found in %s; pass one of: %s",\
            sourceWorktree, strings.Join(ids, ", "))\
    }\
}\
\
func sessionBelongsToSourceWorktree(state *session.State, sourceWorktree, sourceWorktreeID string) bool {\
    if state == nil {\
        return false\
    }\
    if state.WorktreeID != "" && sourceWorktreeID != "" {\
        return state.WorktreeID == sourceWorktreeID\
    }\
    if state.WorktreePath != "" {\
        return sameAdoptPath(state.WorktreePath, sourceWorktree)\
    }\
    return false\
}\
\
func adoptSessionWorktreeLabel(state *session.State) string {\
    if state == nil {\
        return unknownPlaceholder\
    }\
    if state.WorktreePath != "" {\
        return state.WorktreePath\
    }\
    if state.WorktreeID != "" {\
        return state.WorktreeID\
    }\
    return unknownPlaceholder\
}\
\
func isRecentAdoptCandidate(state *session.State) bool {\
    if !isAdoptableSourceSession(state) {\
        return false\
    }\
    lastSeen := sessionLastSeen(state)\
    if lastSeen.IsZero() {\
        return false\
    }\
    return time.Since(lastSeen) <= adoptRecentWindow\
}\
\
func isAdoptableSourceSession(state *session.State) bool {\
    return state != nil &&\
        state.Phase != session.PhaseEnded &&\
        state.EndedAt == nil &&\
        !state.FullyCondensed\
}\
\
func sessionLastSeen(state *session.State) time.Time {\
    if state.LastInteractionTime != nil {\
        return *state.LastInteractionTime\
    }\
    return state.StartedAt\
}\
\
func buildAdoptedSessionState(ctx context.Context, source *session.State) (*session.State, []string, error) {\
    repo, err := openRepository(ctx)\
    if err != nil {\
        return nil, nil, fmt.Errorf("open current repository: %w", err)\
    }\
    defer repo.Close()\
\
    head, err := repo.Head()\
    if err != nil {\
        return nil, nil, fmt.Errorf("resolve current HEAD: %w", err)\
    }\
\
    worktreeRoot, err := paths.WorktreeRoot(ctx)\
    if err != nil {\
        return nil, nil, fmt.Errorf("resolve current worktree root: %w", err)\
    }\
    worktreeID, err := paths.GetWorktreeID(worktreeRoot)\
    if err != nil {\
        return nil, nil, fmt.Errorf("resolve current worktree ID: %w", err)\
    }\
\
    branch, branchErr := GetCurrentBranch(ctx)\
    if branchErr != nil {\
        branch = ""\
    }\
    filesTouched, err := currentFilesTouched(ctx)\
    if err != nil {\
        return nil, nil, err\
    }\
    untrackedFiles, err := strategy.CollectUntrackedFiles(ctx)\
    if err != nil {\
        untrackedFiles = nil\
    }\
\
    now := time.Now()\
    adopted := cloneAdoptSourceState(source)\
\
    // Keep the source live transcript path. In cross-repo adoption the transcript\
    // belongs to the continuing agent session, not the target repository; clearing\
    // or recomputing it from the target repo would drop live transcript capture.\
    adopted.CLIVersion = versioninfo.Version\
    adopted.TranscriptPath = source.TranscriptPath\
    adopted.BaseCommit = head.Hash().String()\
    adopted.RealignAttributionBase(head.Hash().String())\
    adopted.WorktreePath = worktreeRoot\
    adopted.WorktreeID = worktreeID\
    adopted.AdoptedIntoWorktreePath = ""\
    adopted.AdoptedIntoWorktreeID = ""\
    adopted.Branch = branch\
    adopted.LastInteractionTime = &now\
    adopted.Phase = session.PhaseActive\
    adopted.EndedAt = nil\
    adopted.FilesTouched = filesTouched\
\
    // Reset target-local checkpoint bookkeeping. Source checkpoint IDs can point\
    // at metadata in another repository or checkpoint branch; carrying them into\
    // this repo would let amend and turn-finalization paths operate on unrelated\
    // checkpoints.\
    adopted.StepCount = 0\
    adopted.CheckpointTranscriptStart = 0\
    adopted.CheckpointTranscriptSize = 0\
    adopted.TranscriptIdentifierAtStart = ""\
    adopted.ClearLegacyTranscriptOffsets()\
    adopted.TurnID = ""\
    adopted.TurnCheckpointIDs = nil\
    adopted.LastCheckpointID = id.EmptyCheckpointID\
    adopted.LastCheckpointCommitHash = ""\
    adopted.CheckpointTokenUsage = nil\
\
    adopted.FullyCondensed = false\
    adopted.UntrackedFilesAtStart = untrackedFiles\
    adopted.PromptAttributions = nil\
    adopted.PendingPromptAttribution = nil\
    // Preserve cumulative turn/context metrics for the continuing agent session,\
    // but start the target checkpoint prompt window at the current turn count so\
    // the first adopted checkpoint only counts target-side turns.\
    adopted.PromptWindowBase = adopted.SessionTurnCount\
    adopted.PromptWindowResetPending = false\
    adopted.AttachedManually = false\
    // The source process owner may already be gone; a new turn will capture the\
    // current owner, and until then liveness should fall back to the timeout.\
    adopted.Owner = nil\
\
    return &adopted, filesTouched, nil\
}\
\
func cloneAdoptSourceState(source *session.State) session.State {\
    adopted := *source\
    adopted.EndedAt = cloneTimePtr(source.EndedAt)\
    adopted.LastInteractionTime = cloneTimePtr(source.LastInteractionTime)\
    adopted.ReviewSkills = slices.Clone(source.ReviewSkills)\
    adopted.TurnCheckpointIDs = slices.Clone(source.TurnCheckpointIDs)\
    adopted.UntrackedFilesAtStart = slices.Clone(source.UntrackedFilesAtStart)\
    adopted.FilesTouched = slices.Clone(source.FilesTouched)\
    adopted.TokenUsage = cloneTokenUsage(source.TokenUsage)\
    adopted.SkillEvents = cloneSkillEvents(source.SkillEvents)\
    adopted.PromptAttributions = clonePromptAttributions(source.PromptAttributions)\
    if source.PendingPromptAttribution != nil {\
        pending := clonePromptAttribution(*source.PendingPromptAttribution)\
        adopted.PendingPromptAttribution = &pending\
    }\
    return adopted\
}\
\
func cloneTimePtr(t *time.Time) *time.Time {\
    if t == nil {\
        return nil\
    }\
    cloned := *t\
    return &cloned\
}\
\
func cloneTokenUsage(usage *agent.TokenUsage) *agent.TokenUsage {\
    if usage == nil {\
        return nil\
    }\
    cloned := *usage\
    cloned.SubagentTokens = cloneTokenUsage(usage.SubagentTokens)\
    return &cloned\
}\
\
func cloneSkillEvents(events []agent.SkillEvent) []agent.SkillEvent {\
    cloned := slices.Clone(events)\
    for i := range cloned {\
        if events[i].TranscriptAnchor != nil {\
            anchor := *events[i].TranscriptAnchor\
            anchor.EntryIDs = slices.Clone(events[i].TranscriptAnchor.EntryIDs)\
            cloned[i].TranscriptAnchor = &anchor\
        }\
        cloned[i].Native = maps.Clone(events[i].Native)\
    }\
    return cloned\
}\
\
func clonePromptAttributions(attrs []session.PromptAttribution) []session.PromptAttribution {\
    cloned := slices.Clone(attrs)\
    for i := range cloned {\
        cloned[i] = clonePromptAttribution(attrs[i])\
    }\
    return cloned\
}\
\
func clonePromptAttribution(attr session.PromptAttribution) session.PromptAttribution {\
    attr.UserAddedPerFile = maps.Clone(attr.UserAddedPerFile)\
    attr.UserRemovedPerFile = maps.Clone(attr.UserRemovedPerFile)\
    return attr\
}\
\
func sameAdoptPath(a, b string) bool {\
    return canonicalAdoptPath(a) == canonicalAdoptPath(b)\
}\
\
func sameAdoptStore(a, b string) bool {\
    return canonicalAdoptPath(a) == canonicalAdoptPath(b)\
}\
\
func canonicalAdoptPath(path string) string {\
    if path == "" {\
        return ""\
    }\
    abs, err := filepath.Abs(path)\
    if err == nil {\
        path = abs\
    }\
    path = filepath.Clean(path)\
    if resolved, err := filepath.EvalSymlinks(path); err == nil {\
        path = resolved\
    }\
    return path\
}\
\
func currentFilesTouched(ctx context.Context) ([]string, error) {\
    changes, err := DetectFileChanges(ctx, nil)\
    if err != nil {\
        return nil, fmt.Errorf("detect current file changes: %w", err)\
    }\
    files := mergeUnique(nil, changes.Modified)\
    files = mergeUnique(files, changes.New)\
    files = mergeUnique(files, changes.Deleted)\
    sort.Strings(files)\
    return files, nil\
}\
```\
\
Acmd/entire/cli/session\_adopt.go+596\
\
```\
1\
2\
3\
4\
5\
6\
7\
8\
9\
10\
11\
12\
13\
14\
15\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
30\
31\
32\
33\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
44\
45\
46\
47\
48\
49\
50\
51\
52\
53\
54\
55\
56\
57\
58\
59\
60\
61\
62\
63\
64\
65\
66\
67\
68\
69\
70\
71\
72\
73\
74\
75\
76\
77\
78\
79\
80\
81\
82\
83\
84\
85\
86\
87\
88\
89\
90\
91\
92\
93\
94\
95\
96\
97\
98\
99\
100\
101\
102\
103\
104\
105\
106\
107\
108\
109\
110\
111\
112\
113\
114\
115\
116\
117\
118\
119\
120\
121\
122\
123\
124\
125\
126\
127\
128\
129\
130\
131\
132\
133\
134\
135\
136\
137\
138\
139\
140\
141\
142\
143\
144\
145\
146\
147\
148\
149\
150\
151\
152\
153\
154\
155\
156\
157\
158\
159\
160\
161\
162\
163\
164\
165\
166\
167\
168\
169\
170\
171\
172\
173\
174\
175\
176\
177\
178\
179\
180\
181\
182\
183\
184\
185\
186\
187\
188\
189\
190\
191\
192\
193\
194\
195\
196\
197\
198\
199\
200\
201\
202\
203\
204\
205\
206\
207\
208\
209\
210\
211\
212\
213\
214\
215\
216\
217\
218\
219\
220\
221\
222\
223\
224\
225\
226\
227\
228\
229\
230\
231\
232\
233\
234\
235\
236\
237\
238\
239\
240\
241\
242\
243\
244\
245\
246\
247\
248\
249\
250\
251\
252\
253\
254\
255\
256\
257\
258\
259\
260\
261\
262\
263\
264\
265\
266\
267\
268\
269\
270\
271\
272\
273\
274\
275\
276\
277\
278\
279\
280\
281\
282\
283\
284\
285\
286\
287\
288\
289\
290\
291\
292\
293\
294\
295\
296\
297\
298\
299\
300\
301\
302\
303\
304\
305\
306\
307\
308\
309\
310\
311\
312\
313\
314\
315\
316\
317\
318\
319\
320\
321\
322\
323\
324\
325\
326\
327\
328\
329\
330\
331\
332\
333\
334\
335\
336\
337\
338\
339\
340\
341\
342\
343\
344\
345\
346\
347\
348\
349\
350\
351\
352\
353\
354\
355\
356\
357\
358\
359\
360\
361\
362\
363\
364\
365\
366\
367\
368\
369\
370\
371\
372\
373\
374\
375\
376\
377\
378\
379\
380\
381\
382\
383\
384\
385\
386\
387\
388\
389\
390\
391\
392\
393\
394\
395\
396\
397\
398\
399\
400\
401\
402\
403\
404\
405\
406\
407\
408\
409\
410\
411\
412\
413\
414\
415\
416\
417\
418\
419\
420\
421\
422\
423\
424\
425\
426\
427\
428\
429\
430\
431\
432\
433\
434\
435\
436\
437\
438\
439\
440\
441\
442\
443\
444\
445\
446\
447\
448\
449\
450\
451\
452\
453\
454\
455\
456\
457\
458\
459\
460\
461\
462\
463\
464\
465\
466\
467\
468\
469\
470\
471\
472\
473\
474\
475\
476\
477\
478\
479\
480\
481\
482\
483\
484\
485\
486\
487\
488\
489\
490\
491\
492\
493\
494\
495\
496\
497\
498\
499\
500\
501\
502\
503\
504\
505\
506\
507\
508\
509\
510\
511\
512\
513\
514\
515\
516\
517\
518\
519\
520\
521\
522\
523\
524\
525\
526\
527\
528\
529\
530\
531\
532\
533\
534\
535\
536\
537\
538\
539\
540\
541\
542\
543\
544\
545\
546\
547\
548\
549\
550\
551\
552\
553\
554\
555\
556\
557\
558\
559\
560\
561\
562\
563\
564\
565\
566\
567\
568\
569\
570\
571\
572\
573\
574\
575\
576\
577\
578\
579\
580\
581\
582\
583\
584\
585\
586\
587\
588\
589\
590\
591\
592\
593\
594\
595\
596\
597\
598\
599\
600\
601\
602\
603\
604\
605\
606\
607\
608\
609\
610\
611\
612\
613\
614\
615\
616\
617\
618\
619\
620\
621\
622\
623\
624\
625\
626\
627\
628\
629\
630\
631\
632\
633\
634\
635\
636\
637\
638\
639\
640\
641\
642\
643\
644\
645\
646\
647\
648\
649\
650\
651\
652\
653\
654\
655\
656\
657\
658\
659\
660\
661\
662\
663\
664\
665\
666\
667\
668\
669\
670\
671\
672\
673\
674\
675\
676\
677\
678\
679\
680\
681\
682\
683\
684\
685\
686\
687\
688\
689\
690\
691\
692\
693\
694\
695\
696\
697\
698\
699\
700\
701\
702\
703\
704\
705\
706\
707\
708\
709\
710\
711\
712\
713\
714\
715\
716\
717\
718\
719\
720\
721\
722\
723\
724\
725\
726\
727\
728\
729\
730\
731\
732\
733\
734\
735\
736\
737\
738\
739\
740\
741\
742\
743\
744\
745\
746\
747\
748\
749\
750\
751\
752\
753\
754\
755\
756\
757\
758\
759\
760\
761\
762\
763\
764\
765\
766\
767\
768\
769\
770\
771\
772\
773\
774\
775\
776\
777\
778\
779\
780\
781\
782\
783\
784\
785\
786\
787\
788\
789\
790\
791\
792\
793\
794\
795\
796\
797\
798\
799\
800\
801\
802\
803\
804\
805\
806\
807\
808\
809\
810\
811\
812\
813\
814\
815\
816\
817\
818\
819\
820\
821\
822\
823\
824\
825\
826\
827\
828\
829\
830\
831\
832\
833\
834\
835\
836\
837\
838\
839\
840\
841\
842\
843\
844\
845\
846\
847\
848\
849\
850\
851\
852\
853\
854\
855\
856\
857\
858\
859\
860\
861\
862\
863\
864\
865\
866\
867\
868\
869\
870\
871\
872\
873\
874\
875\
876\
877\
878\
879\
880\
881\
882\
883\
884\
885\
886\
887\
888\
889\
890\
891\
892\
893\
894\
895\
896\
897\
898\
899\
900\
901\
902\
903\
904\
905\
906\
907\
908\
909\
910\
911\
912\
913\
914\
915\
916\
917\
918\
919\
920\
921\
922\
923\
924\
925\
926\
927\
928\
929\
930\
931\
932\
933\
934\
935\
936\
937\
938\
939\
940\
941\
942\
943\
944\
945\
946\
947\
948\
949\
950\
951\
952\
953\
954\
955\
956\
957\
958\
959\
960\
961\
962\
963\
964\
965\
966\
967\
968\
969\
970\
971\
972\
973\
974\
975\
976\
977\
978\
979\
980\
981\
982\
983\
984\
985\
986\
987\
988\
989\
990\
991\
992\
993\
994\
995\
996\
997\
998\
999\
1000\
1001\
1002\
1003\
1004\
1005\
1006\
1007\
1008\
1009\
1010\
1011\
1012\
1013\
1014\
1015\
1016\
1017\
1018\
1019\
1020\
1021\
1022\
1023\
1024\
1025\
1026\
1027\
1028\
1029\
1030\
1031\
1032\
1033\
1034\
1035\
1036\
1037\
1038\
1039\
1040\
1041\
1042\
1043\
1044\
1045\
1046\
1047\
1048\
1049\
1050\
1051\
1052\
1053\
1054\
1055\
1056\
1057\
1058\
1059\
1060\
1061\
1062\
1063\
1064\
1065\
1066\
1067\
1068\
1069\
1070\
1071\
1072\
1073\
1074\
1075\
1076\
1077\
1078\
1079\
1080\
1081\
1082\
1083\
1084\
1085\
1086\
1087\
1088\
1089\
1090\
1091\
1092\
1093\
1094\
1095\
1096\
1097\
1098\
1099\
1100\
1101\
1102\
1103\
1104\
1105\
1106\
1107\
1108\
1109\
1110\
1111\
1112\
1113\
1114\
1115\
1116\
1117\
1118\
1119\
1120\
1121\
1122\
1123\
1124\
1125\
1126\
1127\
1128\
1129\
1130\
1131\
1132\
1133\
1134\
1135\
1136\
1137\
1138\
1139\
1140\
1141\
1142\
1143\
1144\
1145\
1146\
1147\
1148\
1149\
1150\
1151\
1152\
1153\
1154\
1155\
1156\
1157\
1158\
1159\
1160\
1161\
1162\
1163\
1164\
1165\
1166\
1167\
1168\
1169\
1170\
1171\
1172\
1173\
1174\
1175\
1176\
1177\
1178\
1179\
1180\
1181\
1182\
1183\
1184\
1185\
1186\
1187\
1188\
1189\
1190\
1191\
1192\
1193\
1194\
1195\
1196\
1197\
1198\
1199\
1200\
1201\
1202\
1203\
1204\
1205\
1206\
1207\
1208\
1209\
1210\
1211\
1212\
1213\
1214\
1215\
1216\
1217\
1218\
1219\
1220\
1221\
1222\
1223\
1224\
1225\
1226\
1227\
1228\
1229\
1230\
1231\
1232\
1233\
1234\
1235\
1236\
1237\
1238\
1239\
1240\
1241\
1242\
1243\
1244\
1245\
1246\
1247\
1248\
1249\
1250\
1251\
1252\
1253\
1254\
1255\
1256\
1257\
1258\
1259\
1260\
1261\
1262\
1263\
1264\
1265\
1266\
1267\
1268\
1269\
1270\
1271\
1272\
1273\
1274\
1275\
1276\
1277\
1278\
1279\
1280\
1281\
1282\
1283\
1284\
1285\
1286\
1287\
1288\
1289\
1290\
1291\
1292\
1293\
1294\
1295\
1296\
1297\
1298\
1299\
1300\
1301\
1302\
1303\
1304\
1305\
1306\
1307\
1308\
1309\
1310\
1311\
1312\
1313\
1314\
1315\
1316\
1317\
1318\
1319\
1320\
1321\
1322\
1323\
1324\
1325\
1326\
1327\
1328\
1329\
1330\
1331\
1332\
1333\
1334\
1335\
1336\
1337\
1338\
1339\
1340\
1341\
1342\
1343\
1344\
1345\
1346\
1347\
1348\
1349\
1350\
1351\
1352\
1353\
1354\
1355\
1356\
1357\
1358\
1359\
1360\
1361\
1362\
1363\
1364\
1365\
1366\
1367\
1368\
1369\
1370\
1371\
1372\
1373\
1374\
1375\
1376\
1377\
1378\
1379\
1380\
1381\
1382\
1383\
1384\
1385\
1386\
1387\
1388\
1389\
1390\
1391\
1392\
1393\
1394\
1395\
1396\
1397\
1398\
1399\
1400\
1401\
1402\
1403\
1404\
1405\
1406\
1407\
1408\
1409\
1410\
1411\
1412\
1413\
1414\
1415\
1416\
1417\
1418\
1419\
1420\
1421\
1422\
1423\
1424\
1425\
1426\
1427\
1428\
1429\
1430\
1431\
1432\
1433\
1434\
1435\
1436\
1437\
1438\
1439\
1440\
1441\
1442\
1443\
1444\
1445\
1446\
1447\
1448\
1449\
1450\
1451\
1452\
1453\
1454\
1455\
1456\
1457\
1458\
1459\
1460\
1461\
1462\
1463\
1464\
1465\
1466\
1467\
1468\
1469\
1470\
1471\
1472\
1473\
1474\
1475\
1476\
1477\
1478\
1479\
1480\
1481\
1482\
1483\
1484\
1485\
1486\
1487\
1488\
1489\
1490\
1491\
1492\
1493\
1494\
1495\
1496\
1497\
1498\
1499\
1500\
1501\
1502\
1503\
1504\
1505\
1506\
1507\
1508\
1509\
1510\
1511\
1512\
1513\
1514\
1515\
1516\
1517\
1518\
1519\
1520\
1521\
1522\
1523\
1524\
1525\
1526\
1527\
1528\
1529\
1530\
1531\
1532\
1533\
1534\
1535\
1536\
1537\
1538\
1539\
1540\
1541\
1542\
1543\
1544\
1545\
1546\
1547\
1548\
1549\
1550\
1551\
1552\
1553\
1554\
1555\
1556\
1557\
1558\
1559\
1560\
1561\
1562\
1563\
1564\
1565\
1566\
1567\
1568\
1569\
1570\
1571\
1572\
1573\
1574\
1575\
1576\
1577\
1578\
1579\
1580\
1581\
1582\
1583\
1584\
1585\
1586\
1587\
1588\
1589\
1590\
1591\
1592\
1593\
1594\
1595\
1596\
1597\
1598\
1599\
1600\
1601\
1602\
1603\
1604\
1605\
1606\
1607\
1608\
1609\
1610\
1611\
1612\
1613\
1614\
1615\
1616\
1617\
1618\
1619\
1620\
1621\
1622\
1623\
1624\
1625\
1626\
1627\
1628\
1629\
1630\
1631\
1632\
1633\
1634\
1635\
1636\
1637\
1638\
1639\
1640\
1641\
1642\
1643\
1644\
1645\
1646\
1647\
1648\
1649\
1650\
1651\
1652\
1653\
1654\
1655\
1656\
1657\
1658\
1659\
1660\
1661\
1662\
1663\
1664\
1665\
1666\
1667\
1668\
1669\
1670\
1671\
1672\
1673\
1674\
1675\
1676\
1677\
1678\
1679\
1680\
1681\
1682\
1683\
1684\
1685\
1686\
1687\
1688\
1689\
1690\
1691\
1692\
1693\
1694\
1695\
1696\
1697\
1698\
1699\
1700\
1701\
1702\
1703\
1704\
1705\
1706\
1707\
1708\
1709\
1710\
1711\
1712\
1713\
1714\
1715\
1716\
1717\
1718\
1719\
1720\
1721\
1722\
1723\
1724\
1725\
1726\
1727\
1728\
1729\
1730\
1731\
1732\
\
package cli\
\
import (\
    "bytes"\
    "context"\
    "encoding/json"\
    "os"\
    "os/exec"\
    "path/filepath"\
    "runtime"\
    "strings"\
    "testing"\
    "time"\
\
    "github.com/entireio/cli/cmd/entire/cli/agent"\
    "github.com/entireio/cli/cmd/entire/cli/checkpoint/id"\
    "github.com/entireio/cli/cmd/entire/cli/internal/flock"\
    "github.com/entireio/cli/cmd/entire/cli/paths"\
    "github.com/entireio/cli/cmd/entire/cli/proclive"\
    "github.com/entireio/cli/cmd/entire/cli/session"\
    "github.com/entireio/cli/cmd/entire/cli/strategy"\
    "github.com/entireio/cli/cmd/entire/cli/testutil"\
)\
\
func TestSessionAdopt_HelpDistinguishesForceAndYes(t *testing.T) {\
    cmd := newAdoptCmd()\
    var stdout bytes.Buffer\
    cmd.SetOut(&stdout)\
    cmd.SetArgs([]string{"--help"})\
\
    if err := cmd.ExecuteContext(context.Background()); err != nil {\
        t.Fatalf("expected help to render without error, got: %v", err)\
    }\
\
    out := stdout.String()\
    for _, want := range []string{\
        "--force",\
        "replace an existing local state file for the same session",\
        "--yes",\
        "confirm same-store adoption and replacement without prompting",\
    } {\
        if !strings.Contains(out, want) {\
            t.Fatalf("help missing %q:\n%s", want, out)\
        }\
    }\
    if strings.Count(out, "replace an existing local state file for the same session") != 1 {\
        t.Fatalf("--force and --yes should not share replacement help text:\n%s", out)\
    }\
}\
\
func TestSessionAdopt_MovesExternalSessionIntoCurrentWorktree(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-session-001"\
    transcriptPath := claudeAdoptTranscriptPath(t, sourceRepo, sessionID)\
    if err := os.MkdirAll(filepath.Dir(transcriptPath), 0o750); err != nil {\
        t.Fatal(err)\
    }\
    if err := os.WriteFile(transcriptPath, []byte(`{"type":"user","message":{"role":"user","content":"update target file"},"uuid":"u1"}`+"\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        TranscriptPath:        transcriptPath,\
        LastPrompt:            "update target file",\
        FilesTouched:          []string{"source-only.txt"},\
        TurnCheckpointIDs:     []string{"abc123def456"},\
        AttachedManually:      true,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected adopted session state in target repo")\
    }\
    if adopted.WorktreePath != targetRepo {\
        t.Fatalf("WorktreePath = %q, want %q", adopted.WorktreePath, targetRepo)\
    }\
    if adopted.BaseCommit != testutil.GetHeadHash(t, targetRepo) {\
        t.Fatalf("BaseCommit = %q, want target HEAD", adopted.BaseCommit)\
    }\
    if adopted.TranscriptPath != transcriptPath {\
        t.Fatalf("TranscriptPath = %q, want %q", adopted.TranscriptPath, transcriptPath)\
    }\
    if adopted.AttachedManually {\
        t.Fatal("adopted active sessions should not be marked manually attached")\
    }\
    if len(adopted.FilesTouched) != 1 || adopted.FilesTouched[0] != "feature.txt" {\
        t.Fatalf("FilesTouched = %v, want [feature.txt]", adopted.FilesTouched)\
    }\
    if len(adopted.TurnCheckpointIDs) != 0 {\
        t.Fatalf("TurnCheckpointIDs = %v, want empty target-local checkpoint bookkeeping", adopted.TurnCheckpointIDs)\
    }\
    if !bytes.Contains(out.Bytes(), []byte("Adopted session")) {\
        t.Fatalf("output = %q, want adoption confirmation", out.String())\
    }\
    if !bytes.Contains(out.Bytes(), []byte("Review tracked files before committing")) {\
        t.Fatalf("output = %q, want tracked-file attribution warning", out.String())\
    }\
}\
\
func TestSessionAdopt_ExternalStoreRetiresSourceSession(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-external-retire-source"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        LastPrompt:            "continue work in target repo",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected adopted target session state")\
    }\
    if adopted.Phase != session.PhaseActive || adopted.EndedAt != nil {\
        t.Fatalf("target state Phase/EndedAt = %q/%v, want active/nil", adopted.Phase, adopted.EndedAt)\
    }\
\
    sourceAfter, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if sourceAfter == nil {\
        t.Fatal("expected source session state to remain as a retired record")\
    }\
    if sourceAfter.Phase != session.PhaseEnded {\
        t.Fatalf("source Phase = %q, want ended", sourceAfter.Phase)\
    }\
    if sourceAfter.EndedAt == nil {\
        t.Fatal("source EndedAt = nil, want retirement timestamp")\
    }\
    if isAdoptableSourceSession(sourceAfter) {\
        t.Fatalf("source state remains adoptable after external adoption: %#v", sourceAfter)\
    }\
\
    t.Chdir(sourceRepo)\
    sourceAgent := &mockLifecycleAgent{name: agent.AgentNameClaudeCode, agentType: agent.AgentTypeClaudeCode}\
    if err := handleLifecycleSessionStart(context.Background(), sourceAgent, &agent.Event{\
        Type:      agent.SessionStart,\
        SessionID: sessionID,\
    }); err != nil {\
        t.Fatalf("SessionStart in the adopted-away source repo should no-op without disrupting the hook, got: %v", err)\
    }\
    sourceAfterSessionStart, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if sourceAfterSessionStart == nil {\
        entries, readErr := os.ReadDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
        if readErr != nil {\
            t.Fatalf("source state disappeared after SessionStart; read state dir: %v", readErr)\
        }\
        names := make([]string, 0, len(entries))\
        for _, entry := range entries {\
            names = append(names, entry.Name())\
        }\
        t.Fatalf("source state disappeared after SessionStart; state dir contains %v", names)\
    }\
    if sourceAfterSessionStart.Phase != session.PhaseEnded {\
        t.Fatalf("source Phase after SessionStart = %q, want ended", sourceAfterSessionStart.Phase)\
    }\
    if sourceAfterSessionStart.EndedAt == nil {\
        t.Fatal("source EndedAt after SessionStart = nil, want retirement timestamp")\
    }\
\
    err = strategy.NewManualCommitStrategy().InitializeSession(\
        context.Background(),\
        sessionID,\
        agent.AgentTypeClaudeCode,\
        "",\
        "source prompt after adoption",\
        "",\
    )\
    if err != nil {\
        t.Fatalf("InitializeSession in the adopted-away source repo should no-op without disrupting the hook, got: %v", err)\
    }\
\
    sourceAfterTurnStart, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if sourceAfterTurnStart.Phase != session.PhaseEnded {\
        t.Fatalf("source Phase after rejected TurnStart = %q, want ended", sourceAfterTurnStart.Phase)\
    }\
    if sourceAfterTurnStart.EndedAt == nil {\
        t.Fatal("source EndedAt after rejected TurnStart = nil, want retirement timestamp")\
    }\
}\
\
func TestSessionAdopt_ExternalStoreRollsBackTargetWhenSourceRetireFails(t *testing.T) {\
    if runtime.GOOS == windowsGOOS {\
        t.Skip("uses POSIX directory permissions to force source save failure")\
    }\
\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-retire-rollback"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStateDir := filepath.Join(sourceRepo, ".git", session.SessionStateDirName)\
    sourceStore := session.NewStateStoreWithDir(sourceStateDir)\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        LastPrompt:            "move this session",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    if err := targetStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-10 * time.Minute),\
        Phase:                 session.PhaseIdle,\
        BaseCommit:            testutil.GetHeadHash(t, targetRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, targetRepo),\
        WorktreePath:          targetRepo,\
        LastPrompt:            "preexisting target state",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    _, _, sourceCommonDir, err := stateStoreForWorktree(context.Background(), sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, targetCommonDir, err := stateStoreForWorktree(context.Background(), targetRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    info, err := os.Stat(sourceStateDir)\
    if err != nil {\
        t.Fatal(err)\
    }\
    restoreSourceStateDir := func() error {\
        return os.Chmod(sourceStateDir, info.Mode().Perm())\
    }\
    if err := os.Chmod(sourceStateDir, 0o500); err != nil {\
        t.Fatal(err)\
    }\
    t.Cleanup(func() {\
        if err := restoreSourceStateDir(); err != nil {\
            t.Logf("restore source state dir permissions: %v", err)\
        }\
    })\
\
    _, _, err = adoptFromExternalSessionStore(\
        context.Background(),\
        sourceStore,\
        sourceRepo,\
        sourceCommonDir,\
        targetStore,\
        targetCommonDir,\
        sessionID,\
        adoptOptions{Force: true},\
    )\
    if err := restoreSourceStateDir(); err != nil {\
        t.Fatalf("restore source state dir permissions: %v", err)\
    }\
    if err == nil {\
        t.Fatal("adoptFromExternalSessionStore succeeded, want source-retire failure")\
    }\
    if !strings.Contains(err.Error(), "retire source session state") {\
        t.Fatalf("adoptFromExternalSessionStore error = %v, want source-retire failure", err)\
    }\
\
    loadedTarget, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loadedTarget == nil {\
        t.Fatal("target rollback removed preexisting state, want restore")\
    }\
    if loadedTarget.LastPrompt != "preexisting target state" {\
        t.Fatalf("target LastPrompt after rollback = %q, want preexisting target state", loadedTarget.LastPrompt)\
    }\
    if loadedTarget.Phase != session.PhaseIdle {\
        t.Fatalf("target Phase after rollback = %q, want idle", loadedTarget.Phase)\
    }\
\
    sourceAfter, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if sourceAfter == nil || sourceAfter.Phase != session.PhaseActive {\
        t.Fatalf("source state after failed adoption = %#v, want original active state", sourceAfter)\
    }\
}\
\
func TestSessionAdopt_ExternalStoreClearsNewTargetWhenSourceRetireFails(t *testing.T) {\
    if runtime.GOOS == windowsGOOS {\
        t.Skip("uses POSIX directory permissions to force source save failure")\
    }\
\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-retire-clear-target"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStateDir := filepath.Join(sourceRepo, ".git", session.SessionStateDirName)\
    sourceStore := session.NewStateStoreWithDir(sourceStateDir)\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        LastPrompt:            "move this session",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, sourceCommonDir, err := stateStoreForWorktree(context.Background(), sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, targetCommonDir, err := stateStoreForWorktree(context.Background(), targetRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    info, err := os.Stat(sourceStateDir)\
    if err != nil {\
        t.Fatal(err)\
    }\
    restoreSourceStateDir := func() error {\
        return os.Chmod(sourceStateDir, info.Mode().Perm())\
    }\
    if err := os.Chmod(sourceStateDir, 0o500); err != nil {\
        t.Fatal(err)\
    }\
    t.Cleanup(func() {\
        if err := restoreSourceStateDir(); err != nil {\
            t.Logf("restore source state dir permissions: %v", err)\
        }\
    })\
\
    _, _, err = adoptFromExternalSessionStore(\
        context.Background(),\
        sourceStore,\
        sourceRepo,\
        sourceCommonDir,\
        targetStore,\
        targetCommonDir,\
        sessionID,\
        adoptOptions{Force: true},\
    )\
    if err := restoreSourceStateDir(); err != nil {\
        t.Fatalf("restore source state dir permissions: %v", err)\
    }\
    if err == nil {\
        t.Fatal("adoptFromExternalSessionStore succeeded, want source-retire failure")\
    }\
    if !strings.Contains(err.Error(), "retire source session state") {\
        t.Fatalf("adoptFromExternalSessionStore error = %v, want source-retire failure", err)\
    }\
\
    loadedTarget, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loadedTarget != nil {\
        t.Fatalf("target state after rollback = %#v, want nil", loadedTarget)\
    }\
}\
\
func TestSessionAdopt_ClearsSourceOwner(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-clear-owner"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        Owner:                 &proclive.Identity{PID: os.Getpid(), Start: "source-owner"},\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected adopted session state in target repo")\
    }\
    if adopted.Owner != nil {\
        t.Fatalf("Owner = %#v, want nil so source process liveness cannot finalize adopted session", adopted.Owner)\
    }\
}\
\
func TestSessionAdopt_RejectsUnexpectedSourceTranscriptPath(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-reject-transcript"\
    transcriptPath := filepath.Join(t.TempDir(), sessionID+".jsonl")\
    if err := os.WriteFile(transcriptPath, []byte(`{"type":"user"}`+"\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        TranscriptPath:        transcriptPath,\
        LastPrompt:            "update target file",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err == nil {\
        t.Fatal("runAdopt succeeded, want transcript-path refusal")\
    }\
    if !strings.Contains(err.Error(), "unexpected transcript path") {\
        t.Fatalf("runAdopt error = %v, want unexpected transcript path", err)\
    }\
\
    targetStore, storeErr := session.NewStateStore(context.Background())\
    if storeErr != nil {\
        t.Fatal(storeErr)\
    }\
    adopted, loadErr := targetStore.Load(context.Background(), sessionID)\
    if loadErr != nil {\
        t.Fatal(loadErr)\
    }\
    if adopted != nil {\
        t.Fatalf("target state was written despite transcript-path refusal: %#v", adopted)\
    }\
}\
\
func TestSessionAdopt_ExternalStoreRejectsSourceEndedAfterInitialSelection(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-external-source-stale"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
    }); err != nil {\
        t.Fatal(err)\
    }\
    if _, err := selectAdoptSourceSession(context.Background(), sourceStore, sourceRepo, sessionID); err != nil {\
        t.Fatalf("initial source selection failed: %v", err)\
    }\
\
    endedAt := time.Now()\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        EndedAt:               &endedAt,\
        Phase:                 session.PhaseIdle,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, sourceCommonDir, err := stateStoreForWorktree(context.Background(), sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, targetCommonDir, err := stateStoreForWorktree(context.Background(), targetRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    _, _, err = adoptFromExternalSessionStore(\
        context.Background(),\
        sourceStore,\
        sourceRepo,\
        sourceCommonDir,\
        targetStore,\
        targetCommonDir,\
        sessionID,\
        adoptOptions{Force: true},\
    )\
    if err == nil {\
        t.Fatal("adoptFromExternalSessionStore succeeded from stale ended source, want refusal")\
    }\
    if !strings.Contains(err.Error(), "ended or fully condensed") {\
        t.Fatalf("adoptFromExternalSessionStore error = %v, want ended-session refusal", err)\
    }\
}\
\
func TestSessionAdopt_ExternalStoreChecksTargetStateAfterLockWait(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-external-target-race"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, sourceCommonDir, err := stateStoreForWorktree(context.Background(), sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    _, _, targetCommonDir, err := stateStoreForWorktree(context.Background(), targetRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    lockPath := filepath.Join(targetCommonDir, "entire-session-locks", sessionID+".lock")\
    if err := os.MkdirAll(filepath.Dir(lockPath), 0o750); err != nil {\
        t.Fatal(err)\
    }\
    release, err := flock.Acquire(lockPath)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    done := make(chan error, 1)\
    go func() {\
        _, _, adoptErr := adoptFromExternalSessionStore(\
            context.Background(),\
            sourceStore,\
            sourceRepo,\
            sourceCommonDir,\
            targetStore,\
            targetCommonDir,\
            sessionID,\
            adoptOptions{},\
        )\
        done <- adoptErr\
    }()\
\
    select {\
    case err := <-done:\
        release()\
        t.Fatalf("adoptFromExternalSessionStore finished before target lock released: %v", err)\
    case <-time.After(100 * time.Millisecond):\
    }\
\
    if err := targetStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now(),\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, targetRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, targetRepo),\
        WorktreePath:          targetRepo,\
        LastPrompt:            "concurrent target state",\
    }); err != nil {\
        release()\
        t.Fatal(err)\
    }\
    release()\
\
    err = <-done\
    if err == nil {\
        t.Fatal("adoptFromExternalSessionStore succeeded, want existing target refusal")\
    }\
    if !strings.Contains(err.Error(), "already tracked in this repo") {\
        t.Fatalf("adoptFromExternalSessionStore error = %v, want existing-state refusal", err)\
    }\
\
    loaded, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loaded.LastPrompt != "concurrent target state" {\
        t.Fatalf("target state LastPrompt = %q, want concurrent target state", loaded.LastPrompt)\
    }\
}\
\
func TestSessionAdopt_EnablesPrepareCommitMsgTrailer(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-trailer-001"\
    targetRelPath := "src/feature.go"\
    targetAbsPath := filepath.Join(targetRepo, targetRelPath)\
\
    transcriptPath := claudeAdoptTranscriptPath(t, sourceRepo, sessionID)\
    if err := os.MkdirAll(filepath.Dir(transcriptPath), 0o750); err != nil {\
        t.Fatal(err)\
    }\
    transcript := `{"type":"human","message":{"content":"write feature.go"}}\
{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Write","input":{"file_path":"` + targetAbsPath + `","content":"package src\n"}}]}}\
`\
    if err := os.WriteFile(transcriptPath, []byte(transcript), 0o600); err != nil {\
        t.Fatal(err)\
    }\
    stale := time.Now().Add(-3 * time.Minute)\
    if err := os.Chtimes(transcriptPath, stale, stale); err != nil {\
        t.Fatal(err)\
    }\
\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseActive,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        TranscriptPath:        transcriptPath,\
        LastPrompt:            "write feature.go",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, targetRelPath, "package src\n")\
    testutil.GitAdd(t, targetRepo, targetRelPath)\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    commitMsgFile := filepath.Join(targetRepo, "COMMIT_EDITMSG")\
    if err := os.WriteFile(commitMsgFile, []byte("add feature\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
\
    if err := strategy.NewManualCommitStrategy().PrepareCommitMsg(context.Background(), commitMsgFile, ""); err != nil {\
        t.Fatalf("PrepareCommitMsg failed: %v", err)\
    }\
\
    content, err := os.ReadFile(commitMsgFile)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if !strings.Contains(string(content), "Entire-Checkpoint:") {\
        t.Fatalf("commit message = %q, want Entire-Checkpoint trailer", string(content))\
    }\
}\
\
func TestSessionAdopt_IdleSourceSurvivesPrepareCommitMsgTrailer(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-idle-source"\
    targetRelPath := "src/idle.go"\
    targetAbsPath := filepath.Join(targetRepo, targetRelPath)\
    transcriptPath := claudeAdoptTranscriptPath(t, sourceRepo, sessionID)\
    if err := os.MkdirAll(filepath.Dir(transcriptPath), 0o750); err != nil {\
        t.Fatal(err)\
    }\
    transcript := `{"type":"human","message":{"content":"write idle.go"}}\
{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Write","input":{"file_path":"` + targetAbsPath + `","content":"package src\n"}}]}}\
`\
    if err := os.WriteFile(transcriptPath, []byte(transcript), 0o600); err != nil {\
        t.Fatal(err)\
    }\
\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        Phase:                 session.PhaseIdle,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
        TranscriptPath:        transcriptPath,\
        LastPrompt:            "write idle.go",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, targetRelPath, "package src\n")\
    testutil.GitAdd(t, targetRepo, targetRelPath)\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected adopted session state")\
    }\
    if adopted.Phase != session.PhaseActive {\
        t.Fatalf("Phase = %q, want active so commit hooks do not sweep adopted state", adopted.Phase)\
    }\
    if adopted.EndedAt != nil {\
        t.Fatalf("EndedAt = %v, want nil", adopted.EndedAt)\
    }\
\
    commitMsgFile := filepath.Join(targetRepo, "COMMIT_EDITMSG")\
    if err := os.WriteFile(commitMsgFile, []byte("add idle feature\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
    if err := strategy.NewManualCommitStrategy().PrepareCommitMsg(context.Background(), commitMsgFile, ""); err != nil {\
        t.Fatalf("PrepareCommitMsg failed: %v", err)\
    }\
    content, err := os.ReadFile(commitMsgFile)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if !strings.Contains(string(content), "Entire-Checkpoint:") {\
        t.Fatalf("commit message = %q, want Entire-Checkpoint trailer", string(content))\
    }\
}\
\
func TestSessionAdopt_RejectsEndedAtSourceSession(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-ended-at"\
    endedAt := time.Now().Add(-30 * time.Second)\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:             sessionID,\
        AgentType:             agent.AgentTypeClaudeCode,\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:   &lastInteraction,\
        EndedAt:               &endedAt,\
        Phase:                 session.PhaseIdle,\
        BaseCommit:            testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit: testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:          sourceRepo,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err == nil {\
        t.Fatal("runAdopt succeeded, want ended-session refusal")\
    }\
    if !strings.Contains(err.Error(), "ended or fully condensed") {\
        t.Fatalf("runAdopt error = %v, want ended-session refusal", err)\
    }\
\
    _, err = selectAdoptSourceSession(context.Background(), sourceStore, sourceRepo, "")\
    if err == nil {\
        t.Fatal("selectAdoptSourceSession succeeded, want no recent active sessions")\
    }\
    if !strings.Contains(err.Error(), "no recent active sessions") {\
        t.Fatalf("selectAdoptSourceSession error = %v, want no recent active sessions", err)\
    }\
}\
\
func TestSessionAdopt_ResetsSourceCheckpointWindow(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sessionID := "test-adopt-reset-window"\
    targetRelPath := "src/feature.go"\
    targetAbsPath := filepath.Join(targetRepo, targetRelPath)\
\
    transcriptPath := claudeAdoptTranscriptPath(t, sourceRepo, sessionID)\
    if err := os.MkdirAll(filepath.Dir(transcriptPath), 0o750); err != nil {\
        t.Fatal(err)\
    }\
    transcript := `{"type":"human","message":{"content":"first source prompt"},"uuid":"source-user"}\
{"type":"assistant","message":{"content":"source response"},"uuid":"source-assistant"}\
{"type":"human","message":{"content":"write target feature"},"uuid":"target-user"}\
{"type":"assistant","message":{"content":[{"type":"tool_use","name":"Write","input":{"file_path":"` + targetAbsPath + `","content":"package src\n"}}]},"uuid":"target-assistant"}\
`\
    if err := os.WriteFile(transcriptPath, []byte(transcript), 0o600); err != nil {\
        t.Fatal(err)\
    }\
\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:                   sessionID,\
        AgentType:                   agent.AgentTypeClaudeCode,\
        StartedAt:                   time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:         &lastInteraction,\
        Phase:                       session.PhaseActive,\
        BaseCommit:                  testutil.GetHeadHash(t, sourceRepo),\
        AttributionBaseCommit:       testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:                sourceRepo,\
        TranscriptPath:              transcriptPath,\
        LastPrompt:                  "write target feature",\
        StepCount:                   4,\
        SessionDurationMs:           120_000,\
        SessionTurnCount:            7,\
        ContextTokens:               42_000,\
        ContextWindowSize:           200_000,\
        CheckpointTranscriptStart:   2,\
        CheckpointTranscriptSize:    1234,\
        CondensedTranscriptLines:    2,\
        TranscriptLinesAtStart:      2,\
        TranscriptIdentifierAtStart: "source-assistant",\
        TurnID:                      "source-turn",\
        TurnCheckpointIDs:           []string{"abc123def456"},\
        LastCheckpointID:            id.MustCheckpointID("abc123def456"),\
        LastCheckpointCommitHash:    "source-commit",\
        CheckpointTokenUsage:        &agent.TokenUsage{InputTokens: 100, OutputTokens: 25, APICallCount: 1},\
        UntrackedFilesAtStart:       []string{"source-only.txt"},\
        PromptWindowBase:            3,\
        PromptWindowResetPending:    true,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, targetRelPath, "package src\n")\
    testutil.GitAdd(t, targetRepo, targetRelPath)\
    testutil.WriteFile(t, targetRepo, "target-notes.txt", "user notes\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected adopted session state in target repo")\
    }\
    if adopted.StepCount != 0 {\
        t.Fatalf("StepCount = %d, want 0 for first target checkpoint", adopted.StepCount)\
    }\
    if adopted.CheckpointTranscriptStart != 0 {\
        t.Fatalf("CheckpointTranscriptStart = %d, want 0", adopted.CheckpointTranscriptStart)\
    }\
    if adopted.CheckpointTranscriptSize != 0 {\
        t.Fatalf("CheckpointTranscriptSize = %d, want 0", adopted.CheckpointTranscriptSize)\
    }\
    if adopted.TranscriptIdentifierAtStart != "" {\
        t.Fatalf("TranscriptIdentifierAtStart = %q, want empty", adopted.TranscriptIdentifierAtStart)\
    }\
    if adopted.SessionDurationMs != 120_000 {\
        t.Fatalf("SessionDurationMs = %d, want preserved source duration", adopted.SessionDurationMs)\
    }\
    if adopted.SessionTurnCount != 7 {\
        t.Fatalf("SessionTurnCount = %d, want preserved source turn count", adopted.SessionTurnCount)\
    }\
    if adopted.ContextTokens != 42_000 {\
        t.Fatalf("ContextTokens = %d, want preserved source context tokens", adopted.ContextTokens)\
    }\
    if adopted.ContextWindowSize != 200_000 {\
        t.Fatalf("ContextWindowSize = %d, want preserved source context window size", adopted.ContextWindowSize)\
    }\
    if adopted.PromptWindowBase != adopted.SessionTurnCount {\
        t.Fatalf("PromptWindowBase = %d, want current SessionTurnCount %d", adopted.PromptWindowBase, adopted.SessionTurnCount)\
    }\
    if adopted.PromptWindowResetPending {\
        t.Fatal("PromptWindowResetPending = true, want false for adopted target window")\
    }\
    if len(adopted.TurnCheckpointIDs) != 0 {\
        t.Fatalf("TurnCheckpointIDs = %v, want empty", adopted.TurnCheckpointIDs)\
    }\
    if adopted.TurnID != "" {\
        t.Fatalf("TurnID = %q, want empty target-local turn ID", adopted.TurnID)\
    }\
    if len(adopted.UntrackedFilesAtStart) != 1 || adopted.UntrackedFilesAtStart[0] != "target-notes.txt" {\
        t.Fatalf("UntrackedFilesAtStart = %v, want target worktree snapshot [target-notes.txt]", adopted.UntrackedFilesAtStart)\
    }\
    if !adopted.LastCheckpointID.IsEmpty() {\
        t.Fatalf("LastCheckpointID = %s, want empty", adopted.LastCheckpointID.String())\
    }\
    if adopted.LastCheckpointCommitHash != "" {\
        t.Fatalf("LastCheckpointCommitHash = %q, want empty", adopted.LastCheckpointCommitHash)\
    }\
    if adopted.CheckpointTokenUsage != nil {\
        t.Fatalf("CheckpointTokenUsage = %#v, want nil for first target checkpoint", adopted.CheckpointTokenUsage)\
    }\
\
    commitMsgFile := filepath.Join(targetRepo, "COMMIT_EDITMSG")\
    if err := os.WriteFile(commitMsgFile, []byte("add target feature\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
    if err := strategy.NewManualCommitStrategy().PrepareCommitMsg(context.Background(), commitMsgFile, ""); err != nil {\
        t.Fatalf("PrepareCommitMsg failed: %v", err)\
    }\
    content, err := os.ReadFile(commitMsgFile)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if !strings.Contains(string(content), "Entire-Checkpoint:") {\
        t.Fatalf("commit message = %q, want Entire-Checkpoint trailer", string(content))\
    }\
}\
\
func TestSessionAdopt_ClearsLegacyTranscriptOffsets(t *testing.T) {\
    targetRepo := setupAdoptRepo(t)\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    adopted, _, err := buildAdoptedSessionState(context.Background(), &session.State{\
        SessionID:                 "test-adopt-legacy-offsets",\
        AgentType:                 agent.AgentTypeClaudeCode,\
        StartedAt:                 time.Now().Add(-5 * time.Minute),\
        Phase:                     session.PhaseActive,\
        BaseCommit:                "source-head",\
        WorktreePath:              "/source/repo",\
        CheckpointTranscriptStart: 9,\
        CondensedTranscriptLines:  9,\
        TranscriptLinesAtStart:    9,\
    })\
    if err != nil {\
        t.Fatalf("buildAdoptedSessionState failed: %v", err)\
    }\
    if adopted.CheckpointTranscriptStart != 0 {\
        t.Fatalf("CheckpointTranscriptStart = %d, want 0", adopted.CheckpointTranscriptStart)\
    }\
\
    encoded, err := json.Marshal(adopted)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if bytes.Contains(encoded, []byte("condensed_transcript_lines")) {\
        t.Fatalf("adopted state JSON contains condensed_transcript_lines: %s", encoded)\
    }\
    if bytes.Contains(encoded, []byte("transcript_lines_at_start")) {\
        t.Fatalf("adopted state JSON contains transcript_lines_at_start: %s", encoded)\
    }\
}\
\
func TestSessionAdopt_PreservesReviewAndInvestigateMetadata(t *testing.T) {\
    for _, tc := range []struct {\
        name string\
        kind session.Kind\
    }{\
        {name: "review", kind: session.KindAgentReview},\
        {name: "investigate", kind: session.KindAgentInvestigate},\
    } {\
        t.Run(tc.name, func(t *testing.T) {\
            targetRepo := setupAdoptRepo(t)\
            testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
            t.Chdir(targetRepo)\
\
            adopted, _, err := buildAdoptedSessionState(context.Background(), &session.State{\
                SessionID:         "test-adopt-kind-" + tc.name,\
                AgentType:         agent.AgentTypeClaudeCode,\
                StartedAt:         time.Now().Add(-5 * time.Minute),\
                Phase:             session.PhaseActive,\
                Kind:              tc.kind,\
                ReviewSkills:      []string{"/review"},\
                ReviewPrompt:      "review this branch",\
                InvestigateRunID:  "abcdef012345",\
                InvestigateTopic:  "Why is adoption misclassified?",\
                BaseCommit:        "source-head",\
                WorktreePath:      "/source/repo",\
                LastCheckpointID:  id.MustCheckpointID("abc123def456"),\
                TurnCheckpointIDs: []string{"abc123def456"},\
                PromptWindowBase:  3,\
                SessionTurnCount:  7,\
                AttachedManually:  true,\
            })\
            if err != nil {\
                t.Fatalf("buildAdoptedSessionState failed: %v", err)\
            }\
\
            if adopted.Kind != tc.kind {\
                t.Fatalf("Kind = %q, want %q", adopted.Kind, tc.kind)\
            }\
            if len(adopted.ReviewSkills) != 1 || adopted.ReviewSkills[0] != "/review" {\
                t.Fatalf("ReviewSkills = %v, want [/review]", adopted.ReviewSkills)\
            }\
            if adopted.ReviewPrompt != "review this branch" {\
                t.Fatalf("ReviewPrompt = %q, want review prompt", adopted.ReviewPrompt)\
            }\
            if adopted.InvestigateRunID != "abcdef012345" {\
                t.Fatalf("InvestigateRunID = %q, want source run ID", adopted.InvestigateRunID)\
            }\
            if adopted.InvestigateTopic != "Why is adoption misclassified?" {\
                t.Fatalf("InvestigateTopic = %q, want source topic", adopted.InvestigateTopic)\
            }\
        })\
    }\
}\
\
func TestSessionAdopt_CloneSourceStateDoesNotShareMutableFields(t *testing.T) {\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    endedAt := time.Now()\
    source := &session.State{\
        SessionID:             "test-adopt-deep-copy",\
        StartedAt:             time.Now().Add(-5 * time.Minute),\
        EndedAt:               &endedAt,\
        LastInteractionTime:   &lastInteraction,\
        ReviewSkills:          []string{"/review"},\
        TurnCheckpointIDs:     []string{"source-checkpoint"},\
        UntrackedFilesAtStart: []string{"untracked.txt"},\
        FilesTouched:          []string{"source.txt"},\
        TokenUsage: &agent.TokenUsage{\
            InputTokens: 1,\
            SubagentTokens: &agent.TokenUsage{\
                OutputTokens: 2,\
            },\
        },\
        SkillEvents: []agent.SkillEvent{\
            {\
                ID: "skill-event",\
                TranscriptAnchor: &agent.SkillEventTranscriptAnchor{\
                    EntryIDs: []string{"entry-1"},\
                },\
                Native: map[string]string{"tool": "skill"},\
            },\
        },\
        PromptAttributions: []session.PromptAttribution{\
            {\
                UserAddedPerFile:   map[string]int{"source.txt": 1},\
                UserRemovedPerFile: map[string]int{"source.txt": 2},\
            },\
        },\
        PendingPromptAttribution: &session.PromptAttribution{\
            UserAddedPerFile:   map[string]int{"pending.txt": 3},\
            UserRemovedPerFile: map[string]int{"pending.txt": 4},\
        },\
    }\
\
    adopted := cloneAdoptSourceState(source)\
    *adopted.EndedAt = endedAt.Add(1 * time.Hour)\
    *adopted.LastInteractionTime = lastInteraction.Add(1 * time.Hour)\
    adopted.ReviewSkills[0] = "/changed"\
    adopted.TurnCheckpointIDs[0] = "changed-checkpoint"\
    adopted.UntrackedFilesAtStart[0] = "changed-untracked.txt"\
    adopted.FilesTouched[0] = "changed-source.txt"\
    adopted.TokenUsage.SubagentTokens.OutputTokens = 99\
    adopted.SkillEvents[0].TranscriptAnchor.EntryIDs[0] = "changed-entry"\
    adopted.SkillEvents[0].Native["tool"] = "changed-skill"\
    adopted.PromptAttributions[0].UserAddedPerFile["source.txt"] = 99\
    adopted.PromptAttributions[0].UserRemovedPerFile["source.txt"] = 99\
    adopted.PendingPromptAttribution.UserAddedPerFile["pending.txt"] = 99\
    adopted.PendingPromptAttribution.UserRemovedPerFile["pending.txt"] = 99\
\
    if !source.EndedAt.Equal(endedAt) {\
        t.Fatalf("source EndedAt was mutated: %v", source.EndedAt)\
    }\
    if !source.LastInteractionTime.Equal(lastInteraction) {\
        t.Fatalf("source LastInteractionTime was mutated: %v", source.LastInteractionTime)\
    }\
    if source.ReviewSkills[0] != "/review" {\
        t.Fatalf("source ReviewSkills = %v, want unchanged", source.ReviewSkills)\
    }\
    if source.TurnCheckpointIDs[0] != "source-checkpoint" {\
        t.Fatalf("source TurnCheckpointIDs = %v, want unchanged", source.TurnCheckpointIDs)\
    }\
    if source.UntrackedFilesAtStart[0] != "untracked.txt" {\
        t.Fatalf("source UntrackedFilesAtStart = %v, want unchanged", source.UntrackedFilesAtStart)\
    }\
    if source.FilesTouched[0] != "source.txt" {\
        t.Fatalf("source FilesTouched = %v, want unchanged", source.FilesTouched)\
    }\
    if source.TokenUsage.SubagentTokens.OutputTokens != 2 {\
        t.Fatalf("source TokenUsage.SubagentTokens.OutputTokens = %d, want unchanged", source.TokenUsage.SubagentTokens.OutputTokens)\
    }\
    if source.SkillEvents[0].TranscriptAnchor.EntryIDs[0] != "entry-1" {\
        t.Fatalf("source SkillEvents entry IDs = %v, want unchanged", source.SkillEvents[0].TranscriptAnchor.EntryIDs)\
    }\
    if source.SkillEvents[0].Native["tool"] != "skill" {\
        t.Fatalf("source SkillEvents native = %v, want unchanged", source.SkillEvents[0].Native)\
    }\
    if source.PromptAttributions[0].UserAddedPerFile["source.txt"] != 1 {\
        t.Fatalf("source PromptAttributions user added = %v, want unchanged", source.PromptAttributions[0].UserAddedPerFile)\
    }\
    if source.PromptAttributions[0].UserRemovedPerFile["source.txt"] != 2 {\
        t.Fatalf("source PromptAttributions user removed = %v, want unchanged", source.PromptAttributions[0].UserRemovedPerFile)\
    }\
    if source.PendingPromptAttribution.UserAddedPerFile["pending.txt"] != 3 {\
        t.Fatalf("source PendingPromptAttribution user added = %v, want unchanged", source.PendingPromptAttribution.UserAddedPerFile)\
    }\
    if source.PendingPromptAttribution.UserRemovedPerFile["pending.txt"] != 4 {\
        t.Fatalf("source PendingPromptAttribution user removed = %v, want unchanged", source.PendingPromptAttribution.UserRemovedPerFile)\
    }\
}\
\
func TestSessionAdopt_FromSubdirectoryReadsSourceStore(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetRepo := setupAdoptRepo(t)\
\
    sourceSubdir := filepath.Join(sourceRepo, "nested", "dir")\
    if err := os.MkdirAll(sourceSubdir, 0o750); err != nil {\
        t.Fatal(err)\
    }\
\
    sessionID := "test-adopt-from-subdir"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           sessionID,\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:        sourceRepo,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err := runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceSubdir,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed from source subdir: %v", err)\
    }\
}\
\
func TestSessionAdopt_FiltersSharedSourceStoreByFromWorktree(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    siblingWorktree := filepath.Join(t.TempDir(), "sibling-worktree")\
    runAdoptGit(t, sourceRepo, "worktree", "add", siblingWorktree, "-b", "sibling-worktree")\
    resolvedSiblingWorktree, err := filepath.EvalSymlinks(siblingWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
    siblingWorktree = resolvedSiblingWorktree\
    t.Cleanup(func() {\
        runAdoptGit(t, sourceRepo, "worktree", "remove", siblingWorktree, "--force")\
    })\
    targetRepo := setupAdoptRepo(t)\
\
    sourceWorktreeID, err := paths.GetWorktreeID(sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    siblingWorktreeID, err := paths.GetWorktreeID(siblingWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           "source-worktree-session",\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:        sourceRepo,\
        WorktreeID:          sourceWorktreeID,\
    }); err != nil {\
        t.Fatal(err)\
    }\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           "sibling-worktree-session",\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, siblingWorktree),\
        WorktreePath:        siblingWorktree,\
        WorktreeID:          siblingWorktreeID,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetRepo, "feature.txt", "agent change\n")\
    t.Chdir(targetRepo)\
\
    var out bytes.Buffer\
    err = runAdopt(context.Background(), &out, "", adoptOptions{\
        FromWorktree: sourceRepo,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    targetStore, err := session.NewStateStore(context.Background())\
    if err != nil {\
        t.Fatal(err)\
    }\
    adopted, err := targetStore.Load(context.Background(), "source-worktree-session")\
    if err != nil {\
        t.Fatal(err)\
    }\
    if adopted == nil {\
        t.Fatal("expected source worktree session to be adopted")\
    }\
    if wrong, err := targetStore.Load(context.Background(), "sibling-worktree-session"); err != nil {\
        t.Fatal(err)\
    } else if wrong != nil {\
        t.Fatalf("adopted sibling worktree session unexpectedly: %#v", wrong)\
    }\
}\
\
func TestSessionAdopt_RejectsSourceSessionWithoutWorktreeMetadata(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
\
    sessionID := "missing-worktree-metadata"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           sessionID,\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, sourceRepo),\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    _, err := selectAdoptSourceSession(context.Background(), sourceStore, sourceRepo, sessionID)\
    if err == nil {\
        t.Fatal("selectAdoptSourceSession succeeded for explicit session without worktree metadata, want refusal")\
    }\
    if !strings.Contains(err.Error(), "belongs to") || !strings.Contains(err.Error(), "unknown") {\
        t.Fatalf("selectAdoptSourceSession error = %v, want missing-worktree ownership refusal", err)\
    }\
\
    _, err = selectAdoptSourceSession(context.Background(), sourceStore, sourceRepo, "")\
    if err == nil {\
        t.Fatal("selectAdoptSourceSession auto-selected session without worktree metadata, want no candidate")\
    }\
    if !strings.Contains(err.Error(), "no recent active sessions") {\
        t.Fatalf("selectAdoptSourceSession error = %v, want no recent active sessions", err)\
    }\
}\
\
func TestStateStoreForWorktreeIgnoresGitStderrOnSuccess(t *testing.T) {\
    if runtime.GOOS == windowsGOOS {\
        t.Skip("uses a POSIX shell script fake git")\
    }\
\
    fakeBin := t.TempDir()\
    fakeGit := filepath.Join(fakeBin, "git")\
    script := `#!/bin/sh\
printf 'advice: noisy git warning\n' >&2\
printf '%s\n%s\n' "$FAKE_WORKTREE_ROOT" "$FAKE_GIT_COMMON_DIR"\
`\
    if err := os.WriteFile(fakeGit, []byte(script), 0o755); err != nil {\
        t.Fatal(err)\
    }\
\
    sourceRoot := filepath.Join(t.TempDir(), "source")\
    commonDir := filepath.Join(t.TempDir(), "common.git")\
    t.Setenv("PATH", fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"))\
    t.Setenv("FAKE_WORKTREE_ROOT", sourceRoot)\
    t.Setenv("FAKE_GIT_COMMON_DIR", commonDir)\
\
    _, gotSourceRoot, gotCommonDir, err := stateStoreForWorktree(context.Background(), ".")\
    if err != nil {\
        t.Fatalf("stateStoreForWorktree failed: %v", err)\
    }\
    if gotSourceRoot != sourceRoot {\
        t.Fatalf("sourceRoot = %q, want %q", gotSourceRoot, sourceRoot)\
    }\
    if gotCommonDir != filepath.Clean(commonDir) {\
        t.Fatalf("commonDir = %q, want %q", gotCommonDir, filepath.Clean(commonDir))\
    }\
}\
\
func TestStateStoreForWorktreePreservesGitCommonDirSymlink(t *testing.T) {\
    if runtime.GOOS == windowsGOOS {\
        t.Skip("uses a POSIX shell script fake git")\
    }\
\
    fakeBin := t.TempDir()\
    fakeGit := filepath.Join(fakeBin, "git")\
    script := `#!/bin/sh\
printf '%s\n%s\n' "$FAKE_WORKTREE_ROOT" "$FAKE_GIT_COMMON_DIR"\
`\
    if err := os.WriteFile(fakeGit, []byte(script), 0o755); err != nil {\
        t.Fatal(err)\
    }\
\
    sourceRoot := filepath.Join(t.TempDir(), "source")\
    realCommonDir := filepath.Join(t.TempDir(), "real-common.git")\
    if err := os.MkdirAll(realCommonDir, 0o750); err != nil {\
        t.Fatal(err)\
    }\
    commonDirLink := filepath.Join(t.TempDir(), "common-link.git")\
    if err := os.Symlink(realCommonDir, commonDirLink); err != nil {\
        t.Skipf("symlinks unavailable: %v", err)\
    }\
    t.Setenv("PATH", fakeBin+string(os.PathListSeparator)+os.Getenv("PATH"))\
    t.Setenv("FAKE_WORKTREE_ROOT", sourceRoot)\
    t.Setenv("FAKE_GIT_COMMON_DIR", commonDirLink)\
\
    _, _, gotCommonDir, err := stateStoreForWorktree(context.Background(), ".")\
    if err != nil {\
        t.Fatalf("stateStoreForWorktree failed: %v", err)\
    }\
    if gotCommonDir != filepath.Clean(commonDirLink) {\
        t.Fatalf("commonDir = %q, want git-reported symlink path %q", gotCommonDir, filepath.Clean(commonDirLink))\
    }\
}\
\
func TestSameAdoptStoreCanonicalizesGitCommonDirSymlinks(t *testing.T) {\
    if runtime.GOOS == windowsGOOS {\
        t.Skip("symlink path canonicalization is POSIX-only in this test")\
    }\
\
    realCommonDir := filepath.Join(t.TempDir(), "real-common.git")\
    if err := os.MkdirAll(realCommonDir, 0o750); err != nil {\
        t.Fatal(err)\
    }\
    commonDirLink := filepath.Join(t.TempDir(), "common-link.git")\
    if err := os.Symlink(realCommonDir, commonDirLink); err != nil {\
        t.Skipf("symlinks unavailable: %v", err)\
    }\
\
    if !sameAdoptStore(commonDirLink, realCommonDir) {\
        t.Fatalf("sameAdoptStore(%q, %q) = false, want true", commonDirLink, realCommonDir)\
    }\
}\
\
func TestSessionAdopt_SameStoreReloadsSourceStateUnderLock(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetWorktree := filepath.Join(t.TempDir(), "target-worktree")\
    runAdoptGit(t, sourceRepo, "worktree", "add", targetWorktree, "-b", "target-worktree")\
    resolvedTargetWorktree, err := filepath.EvalSymlinks(targetWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
    targetWorktree = resolvedTargetWorktree\
    t.Cleanup(func() {\
        runAdoptGit(t, sourceRepo, "worktree", "remove", targetWorktree, "--force")\
    })\
\
    sourceWorktreeID, err := paths.GetWorktreeID(sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    targetWorktreeID, err := paths.GetWorktreeID(targetWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    sessionID := "test-adopt-same-store-reload"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           sessionID,\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:        sourceRepo,\
        WorktreeID:          sourceWorktreeID,\
        LastPrompt:          "stale prompt",\
        SessionTurnCount:    1,\
    }); err != nil {\
        t.Fatal(err)\
    }\
    staleSelected, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:           sessionID,\
        AgentType:           agent.AgentTypeClaudeCode,\
        StartedAt:           time.Now().Add(-5 * time.Minute),\
        LastInteractionTime: &lastInteraction,\
        Phase:               session.PhaseActive,\
        BaseCommit:          testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:        sourceRepo,\
        WorktreeID:          sourceWorktreeID,\
        LastPrompt:          "fresh hook prompt",\
        SessionTurnCount:    9,\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetWorktree, "feature.txt", "agent change\n")\
    testutil.GitAdd(t, targetWorktree, "feature.txt")\
    t.Chdir(targetWorktree)\
\
    adopted, _, err := adoptFromSameSessionStore(context.Background(), sourceRepo, staleSelected, adoptOptions{\
        Force: true,\
    })\
    if err != nil {\
        t.Fatalf("adoptFromSameSessionStore failed: %v", err)\
    }\
    if adopted.LastPrompt != "fresh hook prompt" {\
        t.Fatalf("adopted LastPrompt = %q, want fresh hook prompt", adopted.LastPrompt)\
    }\
    if adopted.SessionTurnCount != 9 {\
        t.Fatalf("adopted SessionTurnCount = %d, want fresh source value", adopted.SessionTurnCount)\
    }\
\
    loaded, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loaded.WorktreePath != targetWorktree {\
        t.Fatalf("WorktreePath = %q, want %q", loaded.WorktreePath, targetWorktree)\
    }\
    if loaded.WorktreeID != targetWorktreeID {\
        t.Fatalf("WorktreeID = %q, want %q", loaded.WorktreeID, targetWorktreeID)\
    }\
    if loaded.LastPrompt != "fresh hook prompt" {\
        t.Fatalf("loaded LastPrompt = %q, want fresh hook prompt", loaded.LastPrompt)\
    }\
    if loaded.SessionTurnCount != 9 {\
        t.Fatalf("loaded SessionTurnCount = %d, want fresh source value", loaded.SessionTurnCount)\
    }\
}\
\
func TestSessionAdopt_MovesSameStoreSessionIntoCurrentWorktree(t *testing.T) {\
    sourceRepo := setupAdoptRepo(t)\
    targetWorktree := filepath.Join(t.TempDir(), "target-worktree")\
    runAdoptGit(t, sourceRepo, "worktree", "add", targetWorktree, "-b", "target-worktree")\
    resolvedTargetWorktree, err := filepath.EvalSymlinks(targetWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
    targetWorktree = resolvedTargetWorktree\
    t.Cleanup(func() {\
        runAdoptGit(t, sourceRepo, "worktree", "remove", targetWorktree, "--force")\
    })\
\
    sourceWorktreeID, err := paths.GetWorktreeID(sourceRepo)\
    if err != nil {\
        t.Fatal(err)\
    }\
    targetWorktreeID, err := paths.GetWorktreeID(targetWorktree)\
    if err != nil {\
        t.Fatal(err)\
    }\
\
    sessionID := "test-adopt-same-store"\
    lastInteraction := time.Now().Add(-1 * time.Minute)\
    sourceStore := session.NewStateStoreWithDir(filepath.Join(sourceRepo, ".git", session.SessionStateDirName))\
    if err := sourceStore.Save(context.Background(), &session.State{\
        SessionID:                 sessionID,\
        AgentType:                 agent.AgentTypeClaudeCode,\
        StartedAt:                 time.Now().Add(-5 * time.Minute),\
        LastInteractionTime:       &lastInteraction,\
        Phase:                     session.PhaseActive,\
        BaseCommit:                testutil.GetHeadHash(t, sourceRepo),\
        WorktreePath:              sourceRepo,\
        WorktreeID:                sourceWorktreeID,\
        StepCount:                 4,\
        CheckpointTranscriptStart: 2,\
        LastCheckpointID:          id.MustCheckpointID("abc123def456"),\
        LastCheckpointCommitHash:  "source-commit",\
    }); err != nil {\
        t.Fatal(err)\
    }\
\
    testutil.WriteFile(t, targetWorktree, "feature.txt", "agent change\n")\
    testutil.GitAdd(t, targetWorktree, "feature.txt")\
    t.Chdir(targetWorktree)\
\
    var out bytes.Buffer\
    err = runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
    })\
    if err == nil {\
        t.Fatal("runAdopt succeeded without --force, want existing same-store state refusal")\
    }\
    if !strings.Contains(err.Error(), "already tracked in this repo") {\
        t.Fatalf("runAdopt error = %v, want existing-state refusal", err)\
    }\
\
    loaded, err := sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loaded.WorktreePath != sourceRepo {\
        t.Fatalf("WorktreePath changed without --force: %q", loaded.WorktreePath)\
    }\
\
    err = runAdopt(context.Background(), &out, sessionID, adoptOptions{\
        FromWorktree: sourceRepo,\
        Force:        true,\
    })\
    if err != nil {\
        t.Fatalf("runAdopt failed: %v", err)\
    }\
\
    loaded, err = sourceStore.Load(context.Background(), sessionID)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if loaded.WorktreePath != targetWorktree {\
        t.Fatalf("WorktreePath = %q, want %q", loaded.WorktreePath, targetWorktree)\
    }\
    if loaded.WorktreeID != targetWorktreeID {\
        t.Fatalf("WorktreeID = %q, want %q", loaded.WorktreeID, targetWorktreeID)\
    }\
    if loaded.BaseCommit != testutil.GetHeadHash(t, targetWorktree) {\
        t.Fatalf("BaseCommit = %q, want target HEAD", loaded.BaseCommit)\
    }\
    if loaded.StepCount != 0 {\
        t.Fatalf("StepCount = %d, want reset target-local checkpoint state", loaded.StepCount)\
    }\
    if loaded.CheckpointTranscriptStart != 0 {\
        t.Fatalf("CheckpointTranscriptStart = %d, want reset target-local transcript window", loaded.CheckpointTranscriptStart)\
    }\
    if !loaded.LastCheckpointID.IsEmpty() {\
        t.Fatalf("LastCheckpointID = %s, want empty target-local checkpoint ID", loaded.LastCheckpointID.String())\
    }\
    if loaded.LastCheckpointCommitHash != "" {\
        t.Fatalf("LastCheckpointCommitHash = %q, want empty target-local commit hash", loaded.LastCheckpointCommitHash)\
    }\
\
    commitMsgFile := filepath.Join(targetWorktree, "COMMIT_EDITMSG")\
    if err := os.WriteFile(commitMsgFile, []byte("add same-store feature\n"), 0o600); err != nil {\
        t.Fatal(err)\
    }\
    if err := strategy.NewManualCommitStrategy().PrepareCommitMsg(context.Background(), commitMsgFile, ""); err != nil {\
        t.Fatalf("PrepareCommitMsg failed: %v", err)\
    }\
    content, err := os.ReadFile(commitMsgFile)\
    if err != nil {\
        t.Fatal(err)\
    }\
    if !strings.Contains(string(content), "Entire-Checkpoint:") {\
        t.Fatalf("commit message = %q, want Entire-Checkpoint trailer", string(content))\
    }\
}\
\
func setupAdoptRepo(t *testing.T) string {\
    t.Helper()\
\
    repoDir := t.TempDir()\
    testutil.InitRepo(t, repoDir)\
    testutil.WriteFile(t, repoDir, "init.txt", "init\n")\
    testutil.GitAdd(t, repoDir, "init.txt")\
    testutil.GitCommit(t, repoDir, "init")\
    enableEntire(t, repoDir)\
    realRepoDir, err := filepath.EvalSymlinks(repoDir)\
    if err != nil {\
        t.Fatal(err)\
    }\
    return realRepoDir\
}\
\
func claudeAdoptTranscriptPath(t *testing.T, sourceRepo, sessionID string) string {\
    t.Helper()\
\
    transcriptDir := filepath.Join(sourceRepo, ".claude", "projects", "adopt-test")\
    t.Setenv("ENTIRE_TEST_CLAUDE_PROJECT_DIR", transcriptDir)\
    return filepath.Join(transcriptDir, sessionID+".jsonl")\
}\
\
func runAdoptGit(t *testing.T, dir string, args ...string) {\
    t.Helper()\
\
    cmd := exec.CommandContext(context.Background(), "git", args...)\
    cmd.Dir = dir\
    cmd.Env = testutil.GitIsolatedEnv()\
    if output, err := cmd.CombinedOutput(); err != nil {\
        t.Fatalf("git %s failed: %v\n%s", strings.Join(args, " "), err, output)\
    }\
}\
```\
\
Acmd/entire/cli/session\_adopt\_test.go+1732\
\
```\
167 unmodified lines\
\
168\
169\
170\
171\
172\
173\
174\
4 unmodified lines\
\
179\
180\
181\
182\
183\
184\
185\
9 unmodified lines\
\
195\
196\
197\
198\
199\
200\
201\
\
167 unmodified lines\
\
  stop     Stop one or more active sessions\
  current  Show the active session for the current worktree\
  attach   Attach an existing agent session\
  adopt    Adopt an active session from another worktree\
  resume   Switch to a branch and resume its session\
\
Examples:\
4 unmodified lines\
\
  entire session stop                      Interactive stop\
  entire session current                   Active session for cwd\
  entire session attach <session-id>       Attach an external session\
  entire session adopt <session-id> --from ../repo  Adopt a moved session\
  entire session resume <branch>           Resume from a branch`,\
        PersistentPreRunE: func(cmd *cobra.Command, _ []string) error {\
            if _, err := paths.WorktreeRoot(cmd.Context()); err != nil {\
9 unmodified lines\
\
    cmd.AddCommand(newStopCmd())\
    cmd.AddCommand(newSessionCurrentCmd())\
    cmd.AddCommand(newAttachCmd())\
    cmd.AddCommand(newAdoptCmd())\
    cmd.AddCommand(newResumeCmd())\
\
    return cmd\
```\
\
Mcmd/entire/cli/sessions.go+3\
\
```\
1557 unmodified lines\
\
1558\
1559\
1560\
1561\
1562\
1563\
1564\
1565\
1566\
1567\
1568\
1569\
\
1557 unmodified lines\
\
    return files, nil\
}\
\
// CollectUntrackedFiles collects untracked, non-ignored paths relative to the\
// repository root.\
func CollectUntrackedFiles(ctx context.Context) ([]string, error) {\
    return collectUntrackedFiles(ctx)\
}\
\
// NOTE: The following git tree helper functions have been moved to checkpoint/ package:\
// - FlattenTree -> checkpoint.FlattenTree\
// - CreateBlobFromContent -> checkpoint.CreateBlobFromContent\
```\
\
Mcmd/entire/cli/strategy/common.go+6\
\
```\
2284 unmodified lines\
\
2285\
2286\
2287\
2288\
2289\
2290\
2291\
2292\
2293\
2294\
2295\
2296\
\
2284 unmodified lines\
\
        if state.BaseCommit == "" {\
            return errPartialState\
        }\
        if state.AdoptedIntoWorktreePath != "" {\
            logging.Info(logging.WithComponent(ctx, "hooks"), "skipping adopted-away source session",\
                slog.String("session_id", sessionID),\
                slog.String("adopted_into_worktree", state.AdoptedIntoWorktreePath))\
            return ErrMutationSkip\
        }\
        if transErr := TransitionAndLog(ctx, state, session.EventTurnStart, session.TransitionContext{}, session.NoOpActionHandler{}); transErr != nil {\
            logging.Warn(logging.WithComponent(ctx, "hooks"), "turn start transition failed",\
                slog.String("session_id", sessionID),\
```\
\
Mcmd/entire/cli/strategy/manual\_commit\_hooks.go+6\
\
```\
81 unmodified lines\
\
82\
83\
84\
85\
86\
87\
88\
89\
90\
91\
92\
93\
\
81 unmodified lines\
\
    var states []*SessionState\
    for _, sessionState := range sessionStates {\
        state := sessionState\
        // Adopted-away source records are tombstones: keep them until normal stale\
        // expiry so old source hooks cannot recreate a second live state.\
        if state.AdoptedIntoWorktreePath != "" {\
            states = append(states, state)\
            continue\
        }\
\
        // Skip and cleanup orphaned sessions whose shadow branch no longer exists.\
        // Keep active sessions (shadow branch may not be created yet) and sessions\
```\
\
Mcmd/entire/cli/strategy/manual\_commit\_session.go+6\
\
```\
591 unmodified lines\
\
592\
593\
594\
595\
596\
597\
598\
599\
600\
601\
602\
603\
604\
605\
606\
607\
608\
609\
610\
611\
612\
613\
614\
615\
616\
617\
618\
619\
620\
621\
622\
623\
624\
625\
626\
627\
628\
629\
630\
631\
632\
633\
634\
635\
636\
637\
638\
639\
36 unmodified lines\
\
676\
677\
678\
679\
680\
681\
682\
683\
684\
685\
686\
687\
688\
689\
690\
691\
692\
640\
641\
642\
643\
693\
694\
695\
\
591 unmodified lines\
\
    }, nil\
}\
\
// WithSessionStateLocks acquires the per-session state lock in each git common\
// dir, then runs fn. Lock paths are deduplicated and sorted so callers that\
// span repositories or worktrees can safely acquire more than one lock.\
func WithSessionStateLocks(ctx context.Context, sessionID string, commonDirs []string, fn func() error) error {\
    lockPaths := make([]string, 0, len(commonDirs))\
    seen := make(map[string]struct{}, len(commonDirs))\
    for _, commonDir := range commonDirs {\
        lockPath, err := stateLockPathInCommonDir(commonDir, sessionID)\
        if err != nil {\
            return err\
        }\
        if _, ok := seen[lockPath]; ok {\
            continue\
        }\
        seen[lockPath] = struct{}{}\
        lockPaths = append(lockPaths, lockPath)\
    }\
    slices.Sort(lockPaths)\
\
    releases := make([]func(), 0, len(lockPaths))\
    releaseAll := func() {\
        for i := len(releases) - 1; i >= 0; i-- {\
            releases[i]()\
        }\
    }\
    for _, lockPath := range lockPaths {\
        if err := ctx.Err(); err != nil {\
            releaseAll()\
            return fmt.Errorf("session state lock canceled: %w", err)\
        }\
        release, err := flock.Acquire(lockPath)\
        if err != nil {\
            releaseAll()\
            return fmt.Errorf("acquire session state lock: %w", err)\
        }\
        releases = append(releases, release)\
    }\
    defer releaseAll()\
\
    return fn()\
}\
\
// ErrMutationSkip signals MutateSessionState to skip the save without\
// treating fn's return as an error. Use it when the mutation function\
// observes the loaded state and decides no write is needed (for example,\
36 unmodified lines\
\
// holder distinct from the data — Save's atomic-rename pattern would\
// otherwise unlink the inode the flock is held on.\
func stateLockPath(ctx context.Context, sessionID string) (string, error) {\
    commonDir, err := GetGitCommonDir(ctx)\
    if err != nil {\
        return "", err\
    }\
    return stateLockPathInCommonDir(commonDir, sessionID)\
}\
\
func stateLockPathInCommonDir(commonDir, sessionID string) (string, error) {\
    if strings.TrimSpace(commonDir) == "" {\
        return "", errors.New("empty git common dir")\
    }\
    if err := validation.ValidateSessionID(sessionID); err != nil {\
        return "", fmt.Errorf("invalid session ID: %w", err)\
    }\
    commonDir, err := GetGitCommonDir(ctx)\
    if err != nil {\
        return "", err\
    }\
    lockDir := filepath.Join(commonDir, "entire-session-locks")\
    if err := os.MkdirAll(lockDir, 0o750); err != nil {\
        return "", fmt.Errorf("create session lock directory: %w", err)\
```\
\
Mcmd/entire/cli/strategy/session\_state.go+53/-4\
\
```\
158 unmodified lines\
\
159\
160\
161\
162\
163\
164\
165\
166\
167\
168\
169\
170\
171\
\
158 unmodified lines\
\
it falls back to deriving the branch from the session's last checkpoint ID found\
in branch-only commit trailers.\
\
`entire session adopt` moves an active session from a source repo or worktree\
into the current worktree. Adoption preserves the live transcript path, validates\
that the source state still belongs to the requested source worktree, rewrites\
the session's branch/worktree/base metadata to the target, clears target-local\
checkpoint windows and checkpoint IDs, and snapshots the target's current file\
changes so the next commit can link to the adopted session.\
\
### Temporary Checkpoints\
\
Branch: `entire/<commit[:7]>-<worktreeHash[:6]>`\
```\
\
Mdocs/architecture/sessions-and-checkpoints.md+7\
\
```\
15 unmodified lines\
\
16\
17\
18\
19\
20\
21\
22\
23\
24\
25\
\
15 unmodified lines\
\
        return\
    }\
    Register(&CursorCLI{})\
    // Cursor is rate-limited ("Increase limits for faster responses"), so gate\
    // its parallelism. Without a registered gate, AcquireSlot is a no-op and the\
    // E2E_CONCURRENT_TEST_LIMIT override the workflow sets has no effect.\
    RegisterGate("cursor-cli", 2)\
}\
\
// CursorCLI implements the E2E Agent interface for the Cursor Agent CLI binary.\
```\
\
Me2e/agents/cursor\_cli.go+4\
\
```\
19 unmodified lines\
\
20\
21\
22\
23\
24\
25\
26\
27\
28\
29\
\
19 unmodified lines\
\
        return\
    }\
    Register(&Droid{})\
    // factoryai-droid is run serially in CI (E2E_CONCURRENT_TEST_LIMIT=1).\
    // Without a registered gate, AcquireSlot is a no-op and that limit has no\
    // effect; register the gate so the intended serialization is respected.\
    RegisterGate("factoryai-droid", 1)\
}\
\
// Droid implements the Agent interface for Factory AI Droid.\
```\
\
Me2e/agents/droid.go+4\
\
```\
2 unmodified lines\
\
3\
4\
5\
6\
7\
8\
9\
10\
10\
11\
12\
13\
63 unmodified lines\
\
77\
78\
79\
80\
81\
82\
83\
80\
81\
82\
83\
84\
85\
86\
87\
88\
89\
90\
91\
92\
93\
94\
95\
96\
97\
98\
99\
100\
101\
102\
103\
104\
105\
106\
107\
108\
\
2 unmodified lines\
\
package tests\
\
import (\
    "bytes"\
    "context"\
    "encoding/json"\
    "os"\
    "path/filepath"\
    "regexp"\
    "testing"\
    "time"\
\
63 unmodified lines\
\
    data, err := os.ReadFile(rolloutPath)\
    require.NoError(t, err)\
\
    re := regexp.MustCompile(`(?m)^\{"timestamp":".*","type":"session_meta","payload":\{"id":"([^"]+)"`)\
    m := re.FindSubmatch(data)\
    require.Len(t, m, 2, "session_meta id not found in rollout")\
    return string(m[1])\
    // Codex rollout files are JSONL; the session id lives in the payload of the\
    // first "session_meta" line. Parse line-by-line rather than anchoring a\
    // regex on field order — Codex reorders JSON keys between versions (the old\
    // regex silently stopped matching on Codex 0.142.x). This mirrors the CLI's\
    // own parser in cmd/entire/cli/agent/codex/transcript.go.\
    for _, raw := range bytes.Split(data, []byte("\n")) {\
        if len(bytes.TrimSpace(raw)) == 0 {\
            continue\
        }\
        var line struct {\
            Type    string          `json:"type"`\
            Payload json.RawMessage `json:"payload"`\
        }\
        if err := json.Unmarshal(raw, &line); err != nil || line.Type != "session_meta" {\
            continue\
        }\
        var payload struct {\
            ID string `json:"id"`\
        }\
        require.NoError(t, json.Unmarshal(line.Payload, &payload))\
        require.NotEmpty(t, payload.ID, "session_meta payload missing id")\
        return payload.ID\
    }\
\
    t.Fatalf("session_meta line not found in rollout %s", rolloutPath)\
    return ""\
}\
\
func appendCompactedEncryptedHistory(t *testing.T, rolloutPath string) {\
```\
\
Me2e/tests/codex\_resume\_test.go+27/-5\
\
```\
14908 unmodified lines\
\
14909\
14910\
14911\
14912\
14913\
14914\
14915\
14916\
14917\
14918\
14919\
14920\
2 unmodified lines\
\
14923\
14924\
14925\
14926\
14927\
14928\
14929\
14930\
14931\
14932\
3 unmodified lines\
\
14936\
14937\
14938\
14929\
14939\
14940\
14931\
14932\
14941\
14942\
14943\
14944\
14945\
14946\
14947\
18 unmodified lines\
\
14966\
14967\
14968\
14969\
14970\
14971\
14972\
14973\
14974\
14975\
14976\
14977\
14978\
14979\
14958\
14980\
14981\
14982\
14983\
5 unmodified lines\
\
14989\
14990\
14991\
14970\
14992\
14993\
14994\
14995\
4 unmodified lines\
\
15000\
15001\
15002\
15003\
15004\
15005\
15006\
15007\
15008\
15009\
15010\
15011\
15012\
15013\
15014\
15015\
15016\
15017\
15 unmodified lines\
\
15033\
15034\
15035\
15002\
15036\
15037\
15038\
15039\
545 unmodified lines\
\
15585\
15586\
15587\
15588\
15589\
15590\
15591\
15592\
15593\
15594\
15595\
15596\
2 unmodified lines\
\
15599\
15600\
15601\
15602\
15603\
15604\
15605\
15606\
15607\
15608\
3 unmodified lines\
\
15612\
15613\
15614\
15571\
15615\
15616\
15573\
15574\
15617\
15618\
15619\
15620\
15621\
15622\
15623\
18 unmodified lines\
\
15642\
15643\
15644\
15645\
15646\
15647\
15648\
15649\
15650\
15651\
15652\
15653\
15654\
15655\
15600\
15656\
15657\
15658\
15659\
5 unmodified lines\
\
15665\
15666\
15667\
15612\
15668\
15669\
15670\
15671\
4 unmodified lines\
\
15676\
15677\
15678\
15679\
15680\
15681\
15682\
15683\
15684\
15685\
15686\
15687\
15688\
15689\
15690\
15691\
15692\
15693\
15 unmodified lines\
\
15709\
15710\
15711\
15644\
15712\
15713\
15714\
15715\
\
14908 unmodified lines\
\
        e.FieldStart("granteeId")\
        e.Str(s.GranteeId)\
    }\
    {\
        if s.GranteeName.Set {\
            e.FieldStart("granteeName")\
            s.GranteeName.Encode(e)\
        }\
    }\
    {\
        e.FieldStart("granteeType")\
        e.Str(s.GranteeType)\
2 unmodified lines\
\
        e.FieldStart("role")\
        e.Str(s.Role)\
    }\
    {\
        e.FieldStart("source")\
        e.Str(s.Source)\
    }\
    for k, elem := range s.AdditionalProps {\
        e.FieldStart(k)\
\
3 unmodified lines\
\
    }\
}\
\
var jsonFieldsNameOfProjectGrant = [3]string{\
var jsonFieldsNameOfProjectGrant = [5]string{\
    0: "granteeId",\
    1: "granteeType",\
    2: "role",\
    1: "granteeName",\
    2: "granteeType",\
    3: "role",\
    4: "source",\
}\
\
// Decode decodes ProjectGrant from json.\
18 unmodified lines\
\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"granteeId\"")\
            }\
        case "granteeName":\
            if err := func() error {\
                s.GranteeName.Reset()\
                if err := s.GranteeName.Decode(d); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"granteeName\"")\
            }\
        case "granteeType":\
            requiredBitSet[0] |= 1 << 1\
            requiredBitSet[0] |= 1 << 2\
            if err := func() error {\
                v, err := d.Str()\
                s.GranteeType = string(v)\
5 unmodified lines\
\
                return errors.Wrap(err, "decode field \"granteeType\"")\
            }\
        case "role":\
            requiredBitSet[0] |= 1 << 2\
            requiredBitSet[0] |= 1 << 3\
            if err := func() error {\
                v, err := d.Str()\
                s.Role = string(v)\
4 unmodified lines\
\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"role\"")\
            }\
        case "source":\
            requiredBitSet[0] |= 1 << 4\
            if err := func() error {\
                v, err := d.Str()\
                s.Source = string(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"source\"")\
            }\
        default:\
            var elem jx.Raw\
            if err := func() error {\
15 unmodified lines\
\
    // Validate required fields.\
    var failures []validate.FieldError\
    for i, mask := range [1]uint8{\
        0b00000111,\
        0b00011101,\
    } {\
        if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {\
            // Mask only required fields and check equality to mask using XOR.\
545 unmodified lines\
\
        e.FieldStart("granteeId")\
        e.Str(s.GranteeId)\
    }\
    {\
        if s.GranteeName.Set {\
            e.FieldStart("granteeName")\
            s.GranteeName.Encode(e)\
        }\
    }\
    {\
        e.FieldStart("granteeType")\
        e.Str(s.GranteeType)\
2 unmodified lines\
\
        e.FieldStart("role")\
        e.Str(s.Role)\
    }\
    {\
        e.FieldStart("source")\
        e.Str(s.Source)\
    }\
    for k, elem := range s.AdditionalProps {\
        e.FieldStart(k)\
\
3 unmodified lines\
\
    }\
}\
\
var jsonFieldsNameOfRepoGrant = [3]string{\
var jsonFieldsNameOfRepoGrant = [5]string{\
    0: "granteeId",\
    1: "granteeType",\
    2: "role",\
    1: "granteeName",\
    2: "granteeType",\
    3: "role",\
    4: "source",\
}\
\
// Decode decodes RepoGrant from json.\
18 unmodified lines\
\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"granteeId\"")\
            }\
        case "granteeName":\
            if err := func() error {\
                s.GranteeName.Reset()\
                if err := s.GranteeName.Decode(d); err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"granteeName\"")\
            }\
        case "granteeType":\
            requiredBitSet[0] |= 1 << 1\
            requiredBitSet[0] |= 1 << 2\
            if err := func() error {\
                v, err := d.Str()\
                s.GranteeType = string(v)\
5 unmodified lines\
\
                return errors.Wrap(err, "decode field \"granteeType\"")\
            }\
        case "role":\
            requiredBitSet[0] |= 1 << 2\
            requiredBitSet[0] |= 1 << 3\
            if err := func() error {\
                v, err := d.Str()\
                s.Role = string(v)\
4 unmodified lines\
\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"role\"")\
            }\
        case "source":\
            requiredBitSet[0] |= 1 << 4\
            if err := func() error {\
                v, err := d.Str()\
                s.Source = string(v)\
                if err != nil {\
                    return err\
                }\
                return nil\
            }(); err != nil {\
                return errors.Wrap(err, "decode field \"source\"")\
            }\
        default:\
            var elem jx.Raw\
            if err := func() error {\
15 unmodified lines\
\
    // Validate required fields.\
    var failures []validate.FieldError\
    for i, mask := range [1]uint8{\
        0b00000111,\
        0b00011101,\
    } {\
        if result := (requiredBitSet[i] & mask) ^ mask; result != 0 {\
            // Mask only required fields and check equality to mask using XOR.\
```\
\
Minternal/coreapi/oas\_json\_gen.go+80/-12\
\
```\
6438 unmodified lines\
\
6439\
6440\
6441\
6442\
6443\
6444\
6442\
6443\
6444\
6445\
6446\
6447\
6448\
6449\
2 unmodified lines\
\
6452\
6453\
6454\
6455\
6456\
6457\
6458\
6459\
6460\
6461\
6462\
4 unmodified lines\
\
6467\
6468\
6469\
6470\
6471\
6472\
6473\
6474\
6475\
6476\
6477\
4 unmodified lines\
\
6482\
6483\
6484\
6485\
6486\
6487\
6488\
6489\
6490\
6491\
6492\
4 unmodified lines\
\
6497\
6498\
6499\
6500\
6501\
6502\
6503\
6504\
6505\
6506\
6507\
237 unmodified lines\
\
6745\
6746\
6747\
6726\
6727\
6728\
6748\
6749\
6750\
6751\
6752\
6753\
6754\
6755\
2 unmodified lines\
\
6758\
6759\
6760\
6761\
6762\
6763\
6764\
6765\
6766\
6767\
6768\
4 unmodified lines\
\
6773\
6774\
6775\
6776\
6777\
6778\
6779\
6780\
6781\
6782\
6783\
4 unmodified lines\
\
6788\
6789\
6790\
6791\
6792\
6793\
6794\
6795\
6796\
6797\
6798\
4 unmodified lines\
\
6803\
6804\
6805\
6806\
6807\
6808\
6809\
6810\
6811\
6812\
6813\
\
6438 unmodified lines\
\
// Ref: #/components/schemas/ProjectGrant\
type ProjectGrant struct {\
    GranteeId       string `json:"granteeId"`\
    GranteeType     string `json:"granteeType"`\
    Role            string `json:"role"`\
    GranteeId       string    `json:"granteeId"`\
    GranteeName     OptString `json:"granteeName"`\
    GranteeType     string    `json:"granteeType"`\
    Role            string    `json:"role"`\
    Source          string    `json:"source"`\
    AdditionalProps ProjectGrantAdditional\
}\
\
2 unmodified lines\
\
    return s.GranteeId\
}\
\
// GetGranteeName returns the value of GranteeName.\
func (s *ProjectGrant) GetGranteeName() OptString {\
    return s.GranteeName\
}\
\
// GetGranteeType returns the value of GranteeType.\
func (s *ProjectGrant) GetGranteeType() string {\
    return s.GranteeType\
4 unmodified lines\
\
    return s.Role\
}\
\
// GetSource returns the value of Source.\
func (s *ProjectGrant) GetSource() string {\
    return s.Source\
}\
\
// GetAdditionalProps returns the value of AdditionalProps.\
func (s *ProjectGrant) GetAdditionalProps() ProjectGrantAdditional {\
    return s.AdditionalProps\
4 unmodified lines\
\
    s.GranteeId = val\
}\
\
// SetGranteeName sets the value of GranteeName.\
func (s *ProjectGrant) SetGranteeName(val OptString) {\
    s.GranteeName = val\
}\
\
// SetGranteeType sets the value of GranteeType.\
func (s *ProjectGrant) SetGranteeType(val string) {\
    s.GranteeType = val\
4 unmodified lines\
\
    s.Role = val\
}\
\
// SetSource sets the value of Source.\
func (s *ProjectGrant) SetSource(val string) {\
    s.Source = val\
}\
\
// SetAdditionalProps sets the value of AdditionalProps.\
func (s *ProjectGrant) SetAdditionalProps(val ProjectGrantAdditional) {\
    s.AdditionalProps = val\
237 unmodified lines\
\
// Ref: #/components/schemas/RepoGrant\
type RepoGrant struct {\
    GranteeId       string `json:"granteeId"`\
    GranteeType     string `json:"granteeType"`\
    Role            string `json:"role"`\
    GranteeId       string    `json:"granteeId"`\
    GranteeName     OptString `json:"granteeName"`\
    GranteeType     string    `json:"granteeType"`\
    Role            string    `json:"role"`\
    Source          string    `json:"source"`\
    AdditionalProps RepoGrantAdditional\
}\
\
2 unmodified lines\
\
    return s.GranteeId\
}\
\
// GetGranteeName returns the value of GranteeName.\
func (s *RepoGrant) GetGranteeName() OptString {\
    return s.GranteeName\
}\
\
// GetGranteeType returns the value of GranteeType.\
func (s *RepoGrant) GetGranteeType() string {\
    return s.GranteeType\
4 unmodified lines\
\
    return s.Role\
}\
\
// GetSource returns the value of Source.\
func (s *RepoGrant) GetSource() string {\
    return s.Source\
}\
\
// GetAdditionalProps returns the value of AdditionalProps.\
func (s *RepoGrant) GetAdditionalProps() RepoGrantAdditional {\
    return s.AdditionalProps\
4 unmodified lines\
\
    s.GranteeId = val\
}\
\
// SetGranteeName sets the value of GranteeName.\
func (s *RepoGrant) SetGranteeName(val OptString) {\
    s.GranteeName = val\
}\
\
// SetGranteeType sets the value of GranteeType.\
func (s *RepoGrant) SetGranteeType(val string) {\
    s.GranteeType = val\
4 unmodified lines\
\
    s.Role = val\
}\
\
// SetSource sets the value of Source.\
func (s *RepoGrant) SetSource(val string) {\
    s.Source = val\
}\
\
// SetAdditionalProps sets the value of AdditionalProps.\
func (s *RepoGrant) SetAdditionalProps(val RepoGrantAdditional) {\
    s.AdditionalProps = val\
```\
\
Minternal/coreapi/oas\_schemas\_gen.go+50/-6\
\
```\
1843 unmodified lines\
\
1844\
1845\
1846\
1847\
1848\
1849\
1850\
1851\
1852\
1853\
1854\
1855\
1856\
1857\
1858\
1859\
1860\
1861\
1862\
1857\
1863\
1864\
1865\
1866\
1867\
76 unmodified lines\
\
1944\
1945\
1946\
1947\
1948\
1949\
1950\
1951\
1952\
1953\
1954\
1955\
1956\
1957\
1958\
1959\
1960\
1961\
1962\
1950\
1963\
1964\
1965\
1966\
1967\
\
1843 unmodified lines\
\
          "granteeId": {\
            "type": "string"\
          },\
          "granteeName": {\
            "type": "string"\
          },\
          "granteeType": {\
            "type": "string"\
          },\
          "role": {\
            "type": "string"\
          },\
          "source": {\
            "type": "string"\
          }\
        },\
        "required": [\
          "granteeType",\
          "granteeId",\
          "role"\
          "role",\
          "source"\
        ],\
        "type": "object"\
      },\
76 unmodified lines\
\
          "granteeId": {\
            "type": "string"\
          },\
          "granteeName": {\
            "type": "string"\
          },\
          "granteeType": {\
            "type": "string"\
          },\
          "role": {\
            "type": "string"\
          },\
          "source": {\
            "type": "string"\
          }\
        },\
        "required": [\
          "granteeType",\
          "granteeId",\
          "role"\
          "role",\
          "source"\
        ],\
        "type": "object"\
      },\
```\
\
Minternal/coreapi/spec/core.gen.json+16/-2\
\
```\
1\
\
1\
\
{"components":{"schemas":{"AddOrgMemberInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/AddOrgMemberInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"default":"member","description":"Role at the org; defaults to member.","enum":["owner","admin","member"],"type":"string"}},"required":["provider","providerUserId"],"type":"object"},"AuditEvent":{"additionalProperties":true,"properties":{"actorId":{"type":"string"},"eventType":{"type":"string"},"id":{"type":"string"},"ipAddress":{"description":"Source IP recorded when the event was logged.","type":"string"},"metadata":{"additionalProperties":{},"type":"object"},"occurredAt":{"format":"date-time","type":"string"}},"required":["id","occurredAt","eventType","actorId"],"type":"object"},"AvailableMirror":{"additionalProperties":true,"properties":{"access":{"description":"Caller's effective GitHub access: read, write, or admin.","enum":["read","write","admin"],"type":"string"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"owner":{"type":"string"},"repo":{"type":"string"},"status":{"description":"available (can onboard), mirrored (already mirrored), or owner-only (personal repo of another user).","enum":["available","mirrored","owner-only"],"type":"string"}},"required":["owner","repo","access","status"],"type":"object"},"BatchLookupInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/BatchLookupInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"refs":{"items":{"$ref":"#/components/schemas/LookupRef"},"maxItems":100,"minItems":1,"type":"array"}},"required":["refs"],"type":"object"},"BatchLookupOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/BatchLookupOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"refs":{"items":{"$ref":"#/components/schemas/LookupRefResult"},"type":"array"}},"required":["refs"],"type":"object"},"Binding":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Binding.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"attributeFilter":{},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"providerId":{"type":"string"}},"required":["id","accountId","providerId","attributeFilter","createdAt"],"type":"object"},"Cluster":{"additionalProperties":true,"properties":{"apiUrl":{"type":"string"},"isDefault":{"type":"boolean"},"jurisdiction":{"type":"string"},"publicUrl":{"type":"string"},"slug":{"type":"string"}},"required":["slug","jurisdiction","publicUrl","isDefault"],"type":"object"},"CreateBindingInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateBindingInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"attributeFilter":{"description":"Exact-match key/value map; empty filter matches any token."},"providerId":{"minLength":1,"type":"string"}},"required":["providerId"],"type":"object"},"CreateMirrorInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateMirrorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"DNS host of the destination cluster.","minLength":1,"type":"string"},"owner":{"minLength":1,"type":"string"},"provider":{"enum":["github"],"type":"string"},"repo":{"minLength":1,"type":"string"}},"required":["provider","owner","repo","clusterHost"],"type":"object"},"CreateOrgInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateOrgInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"description":"Display name.","maxLength":100,"minLength":1,"type":"string"},"region":{"description":"Jurisdiction slug; defaults to the server's home jurisdiction.","type":"string"}},"required":["name"],"type":"object"},"CreateProjectInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateProjectInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"maxLength":100,"minLength":1,"type":"string"},"ownerId":{"minLength":1,"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"region":{"type":"string"}},"required":["name","ownerType","ownerId"],"type":"object"},"CreateRepoInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateRepoInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"Public host of the cluster to pin the repo to (e.g. royalcanin.partial.to); empty lands on the jurisdiction default.","type":"string"},"name":{"minLength":1,"type":"string"},"objectFormat":{"description":"Hash format; defaults to sha1.","enum":["sha1","sha256"],"type":"string"},"projectId":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},"required":["projectId","name"],"type":"object"},"CreateServiceAccountInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateServiceAccountInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"minLength":1,"type":"string"},"orgId":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},"required":["orgId","name"],"type":"object"},"CreatedMirror":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreatedMirror.json"],"format":"uri","readOnly":true,"type":"string"},"created":{"description":"true on fresh creation; false when an existing mirror was returned.","type":"boolean"},"empty":{"description":"true when the upstream has no refs to clone.","type":"boolean"},"mirrorId":{"type":"string"},"mirrorUrl":{"type":"string"},"publicUrl":{"type":"string"}},"required":["mirrorId","mirrorUrl","publicUrl","created","empty"],"type":"object"},"ErrorDetail":{"additionalProperties":true,"properties":{"location":{"description":"Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'","type":"string"},"message":{"description":"Error message text","type":"string"},"value":{"description":"The value at the given location"}},"type":"object"},"ErrorModel":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ErrorModel.json"],"format":"uri","readOnly":true,"type":"string"},"detail":{"description":"A human-readable explanation specific to this occurrence of the problem.","examples":["Property foo is required but is missing."],"type":"string"},"errors":{"description":"Optional list of individual error details","items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array"},"instance":{"description":"A URI reference that identifies the specific occurrence of the problem.","examples":["https://example.com/error-log/abc123"],"format":"uri","type":"string"},"status":{"description":"HTTP status code","examples":[400],"format":"int64","type":"integer"},"title":{"description":"A short, human-readable summary of the problem type. This value should not change between occurrences of the error.","examples":["Bad Request"],"type":"string"},"type":{"default":"about:blank","description":"A URI reference to human-readable documentation for the error.","examples":["https://example.com/errors/example"],"format":"uri","type":"string"}},"type":"object"},"GetMeOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetMeOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"auth":{"$ref":"#/components/schemas/MeAuth"},"global":{"$ref":"#/components/schemas/MeGlobal"},"jurisdiction":{"type":"string"},"mode":{"enum":["standalone","global","regional"],"type":"string"},"regional":{"$ref":"#/components/schemas/MeRegional"},"regionalUnavailable":{"$ref":"#/components/schemas/MeRegionalUnavailable"}},"required":["global","auth"],"type":"object"},"GetPermissionsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetPermissionsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"explain":{"additionalProperties":{},"type":"object"},"permissions":{"items":{"type":"string"},"type":"array"},"resourceId":{"type":"string"},"resourceType":{"type":"string"}},"required":["resourceType","resourceId"],"type":"object"},"GetRepoVisibilityOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetRepoVisibilityOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"GetVersionOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetVersionOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"mode":{"description":"Server mode.","enum":["standalone","global","regional"],"type":"string"},"version":{"description":"Git commit SHA of the running entire-core binary, or \"dev\" for an untagged local build.","type":"string"}},"required":["version"],"type":"object"},"GrantMirrorCollaboratorInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantMirrorCollaboratorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"},"handle":{"description":"Qualified grantee handle, e.g. github:alice.","minLength":1,"type":"string"},"owner":{"minLength":1,"type":"string"},"provider":{"enum":["github"],"type":"string"},"repo":{"minLength":1,"type":"string"},"role":{"description":"Grant level: reader (pull) or writer (pull+push).","enum":["reader","writer"],"type":"string"}},"required":["provider","owner","repo","clusterHost","handle","role"],"type":"object"},"GrantProjectAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantProjectAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"granteeType":{"default":"account","enum":["account"],"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["provider","providerUserId","role"],"type":"object"},"GrantProjectAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantProjectAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantRepoAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantRepoAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"granteeType":{"default":"account","enum":["account"],"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["provider","providerUserId","role"],"type":"object"},"GrantRepoAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantRepoAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantServiceAccountAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantServiceAccountAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"resourceId":{"minLength":1,"type":"string"},"resourceType":{"enum":["repo","project"],"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["resourceType","resourceId","role"],"type":"object"},"GrantServiceAccountAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantServiceAccountAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantedMirrorCollaborator":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantedMirrorCollaborator.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"description":"Entire account the grant was written for.","type":"string"},"role":{"type":"string"}},"required":["accountId","role"],"type":"object"},"ListAuditEventsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListAuditEventsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"events":{"items":{"$ref":"#/components/schemas/AuditEvent"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["events"],"type":"object"},"ListAvailableMirrorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListAvailableMirrorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"available":{"items":{"$ref":"#/components/schemas/AvailableMirror"},"type":"array"}},"required":["available"],"type":"object"},"ListBindingsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListBindingsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"bindings":{"items":{"$ref":"#/components/schemas/Binding"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["bindings"],"type":"object"},"ListClustersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListClustersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusters":{"items":{"$ref":"#/components/schemas/Cluster"},"type":"array"}},"required":["clusters"],"type":"object"},"ListMirrorCollaboratorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListMirrorCollaboratorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"collaborators":{"items":{"$ref":"#/components/schemas/MirrorCollaborator"},"type":"array"}},"required":["collaborators"],"type":"object"},"ListMirrorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListMirrorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"mirrors":{"items":{"$ref":"#/components/schemas/Mirror"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["mirrors"],"type":"object"},"ListOIDCProvidersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOIDCProvidersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"providers":{"items":{"$ref":"#/components/schemas/OIDCProvider"},"type":"array"}},"required":["providers"],"type":"object"},"ListOrgMembersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgMembersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"members":{"items":{"$ref":"#/components/schemas/Membership"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["members"],"type":"object"},"ListOrgProjectsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"project":{"$ref":"#/components/schemas/Project"},"projects":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}},"type":"object"},"ListOrgsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"org":{"$ref":"#/components/schemas/Org"},"orgs":{"items":{"$ref":"#/components/schemas/Org"},"type":"array"}},"type":"object"},"ListProjectMembersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectMembersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"members":{"items":{"$ref":"#/components/schemas/ProjectGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["members"],"type":"object"},"ListProjectReposOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectReposOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"repo":{"$ref":"#/components/schemas/Repo"},"repos":{"items":{"$ref":"#/components/schemas/Repo"},"type":"array"}},"type":"object"},"ListProjectsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"project":{"$ref":"#/components/schemas/Project"},"projects":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}},"type":"object"},"ListRepoGrantsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListRepoGrantsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"grants":{"items":{"$ref":"#/components/schemas/RepoGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["grants"],"type":"object"},"ListServiceAccountGrantsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListServiceAccountGrantsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"grants":{"items":{"$ref":"#/components/schemas/ServiceAccountGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["grants"],"type":"object"},"ListServiceAccountsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListServiceAccountsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"serviceAccounts":{"items":{"$ref":"#/components/schemas/ServiceAccountWithGrants"},"type":"array"}},"required":["serviceAccounts"],"type":"object"},"LookupRef":{"additionalProperties":true,"properties":{"id":{"minLength":1,"type":"string"},"type":{"description":"Resource type slug; \"org\", \"project\", \"repo\" are enriched, unknown types pass through.","minLength":1,"type":"string"}},"required":["type","id"],"type":"object"},"LookupRefResult":{"additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"ownerId":{"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"projectId":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["type","id"],"type":"object"},"LookupResourcesOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/LookupResourcesOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"permission":{"type":"string"},"resourceIds":{"items":{"type":"string"},"type":"array"},"resourceType":{"type":"string"},"resources":{"items":{"$ref":"#/components/schemas/ResourceAccess"},"type":"array"}},"required":["resourceType"],"type":"object"},"MeAuth":{"additionalProperties":true,"properties":{"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["provider","providerUserId"],"type":"object"},"MeGlobal":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"avatarUrl":{"type":"string"},"handle":{"type":"string"},"handles":{"items":{"$ref":"#/components/schemas/MeIdentityHandle"},"type":"array"},"homeJurisdiction":{"type":"string"}},"required":["accountId","handles"],"type":"object"},"MeIdentityHandle":{"additionalProperties":true,"properties":{"email":{"description":"The provider's publicly-visible profile email (from the provider at login; may be empty). NOT the account's contact email.","type":"string"},"handle":{"type":"string"},"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["provider","handle","providerUserId"],"type":"object"},"MeRegional":{"additionalProperties":true,"properties":{"bio":{"type":"string"},"company":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"location":{"type":"string"}},"type":"object"},"MeRegionalUnavailable":{"additionalProperties":true,"properties":{"error":{"description":"Always 'foreign_jurisdiction'. Discriminator for client-side state machines.","enum":["foreign_jurisdiction"],"type":"string"},"homeCoreUrl":{"description":"Deep link into the home console for this account.","type":"string"},"jurisdiction":{"description":"The account's home jurisdiction (e.g. 'us', 'eu').","type":"string"},"message":{"description":"Human-readable copy ready to surface in a UI.","type":"string"}},"required":["error","jurisdiction","homeCoreUrl","message"],"type":"object"},"Membership":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Membership.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"orgId":{"type":"string"},"role":{"type":"string"},"status":{"type":"string"},"workosOrgMembershipId":{"type":"string"}},"required":["id","accountId","orgId","role","status","createdAt"],"type":"object"},"Mirror":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Mirror.json"],"format":"uri","readOnly":true,"type":"string"},"cell":{"description":"Physical cell the mirror's cluster runs in, e.g. aws-us-east-2.","type":"string"},"clusterHost":{"description":"Public host of the cluster serving this mirror.","type":"string"},"createdAt":{"format":"date-time","type":"string"},"installationId":{"format":"int64","type":"integer"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"jurisdiction":{"type":"string"},"mirrorId":{"type":"string"},"owner":{"type":"string"},"provider":{"type":"string"},"repo":{"type":"string"},"status":{"description":"Clone lifecycle: processing (cloning), ready (clonable), failed (initial clone failed), or suspended.","enum":["processing","ready","failed","suspended"],"type":"string"},"suspendedAt":{"format":"date-time","type":"string"}},"required":["mirrorId","provider","owner","repo","clusterHost","createdAt"],"type":"object"},"MirrorCollaborator":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"handle":{"description":"Primary handle (provider:label), empty if none resolves.","type":"string"},"role":{"description":"reader (pull) or writer (pull+push).","type":"string"}},"required":["accountId","role"],"type":"object"},"OIDCProvider":{"additionalProperties":true,"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"id":{"type":"string"},"issuer":{"type":"string"}},"required":["id","issuer"],"type":"object"},"Org":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Org.json"],"format":"uri","readOnly":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"region":{"type":"string"},"workosOrganizationId":{"type":"string"}},"required":["id","name","region","createdAt"],"type":"object"},"Project":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Project.json"],"format":"uri","readOnly":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"ownerId":{"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"region":{"type":"string"}},"required":["id","name","ownerType","ownerId","region","createdAt"],"type":"object"},"ProjectGrant":{"additionalProperties":true,"properties":{"granteeId":{"type":"string"},"granteeType":{"type":"string"},"role":{"type":"string"}},"required":["granteeType","granteeId","role"],"type":"object"},"Repo":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Repo.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"type":"string"},"foreign":{"type":"boolean"},"id":{"type":"string"},"mirrorSuspended":{"type":"boolean"},"mirrorSuspendedAt":{"type":"string"},"name":{"type":"string"},"objectFormat":{"enum":["sha1","sha256"],"type":"string"},"owningProjectId":{"type":"string"},"path":{"type":"string"},"provisionAttempts":{"format":"int64","type":"integer"},"provisionReason":{"type":"string"},"state":{"enum":["provisioning","active","failed"],"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["id","owningProjectId","name"],"type":"object"},"RepoGrant":{"additionalProperties":true,"properties":{"granteeId":{"type":"string"},"granteeType":{"type":"string"},"role":{"type":"string"}},"required":["granteeType","granteeId","role"],"type":"object"},"ResolvedIdentity":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ResolvedIdentity.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"handle":{"type":"string"},"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["accountId","provider","handle","providerUserId"],"type":"object"},"ResourceAccess":{"additionalProperties":true,"properties":{"permissions":{"items":{"type":"string"},"type":"array"},"resourceId":{"type":"string"}},"required":["resourceId","permissions"],"type":"object"},"ServiceAccount":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ServiceAccount.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"orgId":{"type":"string"},"status":{"type":"string"},"systemManaged":{"type":"boolean"}},"required":["accountId","name","orgId","status","systemManaged","createdAt"],"type":"object"},"ServiceAccountGrant":{"additionalProperties":true,"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"resourceType":{"type":"string"},"role":{"type":"string"}},"required":["resourceType","resourceId","role"],"type":"object"},"ServiceAccountWithGrants":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"grants":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"orgId":{"type":"string"},"status":{"type":"string"},"systemManaged":{"type":"boolean"}},"required":["grants","accountId","name","orgId","status","systemManaged","createdAt"],"type":"object"},"SetRepoVisibilityInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/SetRepoVisibilityInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"SetRepoVisibilityOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/SetRepoVisibilityOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"UpdateMeInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/UpdateMeInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"email":{"description":"Contact email.","format":"email","maxLength":254,"minLength":5,"type":"string"}},"required":["email"],"type":"object"},"UpdateMeOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/UpdateMeOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"email":{"type":"string"}},"required":["email"],"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT","description":"Bearer token minted by entire-core's device-code flow or STS exchange.","scheme":"bearer","type":"http"},"sessionAuth":{"description":"Console session cookie issued by the browser login flow.","in":"cookie","name":"entire_session","type":"apiKey"}}},"info":{"description":"Entire control plane: identity, orgs, projects, repos, mirrors, service accounts.","title":"Entire Core API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/access/{resourceType}":{"get":{"operationId":"lookupResources","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"SpiceDB resource type (e.g. \"repo\", \"project\", \"org\").","in":"path","name":"resourceType","required":true,"schema":{"description":"SpiceDB resource type (e.g. \"repo\", \"project\", \"org\").","minLength":1,"type":"string"}},{"description":"Optional: only list resources where the caller has this permission. pageSize/pageToken apply only when set.","explode":false,"in":"query","name":"permission","schema":{"description":"Optional: only list resources where the caller has this permission. pageSize/pageToken apply only when set.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResourcesOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List resources of a type the caller can access","tags":["identity"]}},"/access/{resourceType}/{resourceId}":{"get":{"operationId":"getPermissions","parameters":[{"in":"path","name":"resourceType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"resourceId","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"If set, return the SpiceDB trace for this permission instead of the permission list.","explode":false,"in":"query","name":"explain","schema":{"description":"If set, return the SpiceDB trace for this permission instead of the permission list.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPermissionsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the caller's permissions on a single resource","tags":["identity"]}},"/audit":{"get":{"operationId":"listAuditEvents","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAuditEventsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the calling account's recent audit events","tags":["identity"]}},"/clusters":{"get":{"operationId":"listClusters","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListClustersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List data-plane clusters attached to this control plane","tags":["clusters"]}},"/identity/handles/{provider}/{handle}":{"get":{"operationId":"resolveHandle","parameters":[{"description":"IdP slug (e.g. \"github\").","in":"path","name":"provider","required":true,"schema":{"description":"IdP slug (e.g. \"github\").","minLength":1,"type":"string"}},{"description":"User-visible handle at the provider.","in":"path","name":"handle","required":true,"schema":{"description":"User-visible handle at the provider.","minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolvedIdentity"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Resolve account by external provider handle","tags":["identity"]}},"/lookup":{"post":{"operationId":"batchLookup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Batch-resolve (type, id) refs to enriched records","tags":["identity"]}},"/me":{"get":{"operationId":"getMe","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMeOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get the calling account's identity and profile","tags":["identity"]},"patch":{"operationId":"updateMe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Update the calling account's contact email","tags":["identity"]}},"/mirrors":{"delete":{"operationId":"deleteMirror","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete a mirror by upstream coords + cluster host","tags":["mirrors"]},"get":{"operationId":"listMirrors","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: restrict to mirrors on this cluster (public host, e.g. royalcanin.partial.to). Case-sensitive exact match.","explode":false,"in":"query","name":"cluster","schema":{"description":"Optional: restrict to mirrors on this cluster (public host, e.g. royalcanin.partial.to). Case-sensitive exact match.","type":"string"}},{"description":"Optional: restrict to mirrors of this upstream provider, case-insensitive (e.g. \"github\").","explode":false,"in":"query","name":"provider","schema":{"description":"Optional: restrict to mirrors of this upstream provider, case-insensitive (e.g. \"github\").","type":"string"}},{"description":"Optional: restrict to mirrors with this upstream owner login (case-insensitive).","explode":false,"in":"query","name":"owner","schema":{"description":"Optional: restrict to mirrors with this upstream owner login (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMirrorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List mirrors visible to the caller","tags":["mirrors"]},"post":{"operationId":"createMirror","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMirrorInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedMirror"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"412":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Precondition Failed"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create GitHub mirror","tags":["mirrors"]}},"/mirrors/available":{"get":{"operationId":"listAvailableMirrors","parameters":[{"description":"Optional: restrict to repos with this owner login (case-insensitive).","explode":false,"in":"query","name":"owner","schema":{"description":"Optional: restrict to repos with this owner login (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAvailableMirrorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List GitHub repos the caller could onboard as mirrors","tags":["mirrors"]}},"/mirrors/collaborators":{"delete":{"operationId":"revokeMirrorCollaborator","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}},{"description":"Qualified grantee handle, e.g. github:alice.","explode":false,"in":"query","name":"handle","required":true,"schema":{"description":"Qualified grantee handle, e.g. github:alice.","minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke a user's access to a mirror (live GitHub-admin gated)","tags":["mirrors"]},"get":{"operationId":"listMirrorCollaborators","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMirrorCollaboratorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the principals with access to a mirror (live GitHub-admin gated)","tags":["mirrors"]},"post":{"operationId":"grantMirrorCollaborator","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantMirrorCollaboratorInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantedMirrorCollaborator"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant a user reader/writer access to a mirror (live GitHub-admin gated)","tags":["mirrors"]}},"/mirrors/{mirrorId}":{"get":{"operationId":"getMirror","parameters":[{"in":"path","name":"mirrorId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mirror"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get mirror by id","tags":["mirrors"]}},"/oidc-providers":{"get":{"operationId":"listOIDCProviders","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOIDCProvidersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List federated OIDC identity providers","tags":["identity"]}},"/orgs":{"get":{"operationId":"listOrgs","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: exact-match org name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match org name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List organizations the caller can see (or one by name)","tags":["orgs"]},"post":{"operationId":"createOrg","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create organization","tags":["orgs"]}},"/orgs/{orgId}":{"delete":{"operationId":"deleteOrg","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete an organization","tags":["orgs"]},"get":{"operationId":"getOrg","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get an organization","tags":["orgs"]}},"/orgs/{orgId}/members":{"get":{"operationId":"listOrgMembers","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgMembersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List members of an organization","tags":["orgs"]},"post":{"operationId":"addOrgMember","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrgMemberInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Add a member to an organization","tags":["orgs"]}},"/orgs/{orgId}/members/{provider}/{providerUserId}":{"delete":{"operationId":"removeOrgMember","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Remove a member from an organization","tags":["orgs"]}},"/orgs/{orgId}/projects":{"get":{"operationId":"listOrgProjects","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"description":"Optional: exact-match project name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match project name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgProjectsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List projects owned by an organization","tags":["projects"]}},"/projects":{"get":{"operationId":"listProjects","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: exact-match project name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match project name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List projects accessible to the caller (or one by name)","tags":["projects"]},"post":{"operationId":"createProject","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create project","tags":["projects"]}},"/projects/{projectId}":{"delete":{"operationId":"deleteProject","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete a project","tags":["projects"]},"get":{"operationId":"getProject","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get a project by id","tags":["projects"]}},"/projects/{projectId}/grants":{"post":{"operationId":"grantProjectAccess","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantProjectAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantProjectAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant project access to an identity","tags":["projects"]}},"/projects/{projectId}/grants/account/{provider}/{providerUserId}":{"delete":{"operationId":"revokeProjectAccessByProvider","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke project access by provider identity","tags":["projects"]}},"/projects/{projectId}/grants/{granteeType}/{granteeId}":{"delete":{"operationId":"revokeProjectAccess","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"granteeType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"granteeId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke project access by grantee id","tags":["projects"]}},"/projects/{projectId}/members":{"get":{"operationId":"listProjectMembers","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectMembersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List project members and their roles","tags":["projects"]}},"/projects/{projectId}/repos":{"get":{"operationId":"listProjectRepos","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"description":"Optional: exact-match repo name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match repo name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectReposOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List repositories in a project (or one by name)","tags":["repos"]}},"/repos":{"post":{"operationId":"createRepo","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRepoInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Repo"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create repository","tags":["repos"]}},"/repos/{repoId}":{"delete":{"operationId":"deleteRepo","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete repository","tags":["repos"]},"get":{"operationId":"getRepo","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Repo"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get repository","tags":["repos"]}},"/repos/{repoId}/grants":{"get":{"operationId":"listRepoGrants","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListRepoGrantsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List repo grants","tags":["repos"]},"post":{"operationId":"grantRepoAccess","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRepoAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRepoAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant repo access to an identity","tags":["repos"]}},"/repos/{repoId}/grants/account/{provider}/{providerUserId}":{"delete":{"operationId":"revokeRepoAccessByProvider","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke repo access by provider identity","tags":["repos"]}},"/repos/{repoId}/grants/{granteeType}/{granteeId}":{"delete":{"operationId":"revokeRepoAccess","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"granteeType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"granteeId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke repo access by grantee id","tags":["repos"]}},"/repos/{repoId}/visibility":{"get":{"operationId":"getRepoVisibility","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRepoVisibilityOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get repository visibility","tags":["repos"]},"put":{"operationId":"setRepoVisibility","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetRepoVisibilityInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetRepoVisibilityOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Set repository visibility","tags":["repos"]}},"/service-accounts":{"get":{"operationId":"listServiceAccounts","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"explode":false,"in":"query","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceAccountsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List service accounts in an org","tags":["service-accounts"]},"post":{"operationId":"createServiceAccount","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceAccountInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccount"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create service account","tags":["service-accounts"]}},"/service-accounts/{accountId}":{"delete":{"operationId":"deleteServiceAccount","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete service account","tags":["service-accounts"]},"get":{"operationId":"getServiceAccount","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccount"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get service account","tags":["service-accounts"]}},"/service-accounts/{accountId}/bindings":{"get":{"operationId":"listBindings","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBindingsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List OIDC bindings","tags":["service-accounts"]},"post":{"operationId":"createBinding","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBindingInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Binding"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create OIDC binding","tags":["service-accounts"]}},"/service-accounts/{accountId}/bindings/{bindingId}":{"delete":{"operationId":"deleteBinding","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"bindingId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete OIDC binding","tags":["service-accounts"]}},"/service-accounts/{accountId}/grants":{"get":{"operationId":"listServiceAccountGrants","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceAccountGrantsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List service account grants","tags":["service-accounts"]},"post":{"operationId":"grantServiceAccountAccess","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantServiceAccountAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantServiceAccountAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant service account access on a repo or project","tags":["service-accounts"]}},"/service-accounts/{accountId}/grants/{resourceType}/{resourceId}":{"delete":{"operationId":"revokeServiceAccountAccess","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"resourceType","required":true,"schema":{"enum":["repo","project"],"type":"string"}},{"in":"path","name":"resourceId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke service account access","tags":["service-accounts"]}},"/version":{"get":{"operationId":"getVersion","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetVersionOutputBody"}}},"description":"OK"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"summary":"Get the server version and mode","tags":["meta"]}}},"servers":[{"url":"/api/v1"}]}\
No newline at end of file\
{"components":{"schemas":{"AddOrgMemberInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/AddOrgMemberInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"default":"member","description":"Role at the org; defaults to member.","enum":["owner","admin","member"],"type":"string"}},"required":["provider","providerUserId"],"type":"object"},"AuditEvent":{"additionalProperties":true,"properties":{"actorId":{"type":"string"},"eventType":{"type":"string"},"id":{"type":"string"},"ipAddress":{"description":"Source IP recorded when the event was logged.","type":"string"},"metadata":{"additionalProperties":{},"type":"object"},"occurredAt":{"format":"date-time","type":"string"}},"required":["id","occurredAt","eventType","actorId"],"type":"object"},"AvailableMirror":{"additionalProperties":true,"properties":{"access":{"description":"Caller's effective GitHub access: read, write, or admin.","enum":["read","write","admin"],"type":"string"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"owner":{"type":"string"},"repo":{"type":"string"},"status":{"description":"available (can onboard), mirrored (already mirrored), or owner-only (personal repo of another user).","enum":["available","mirrored","owner-only"],"type":"string"}},"required":["owner","repo","access","status"],"type":"object"},"BatchLookupInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/BatchLookupInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"refs":{"items":{"$ref":"#/components/schemas/LookupRef"},"maxItems":100,"minItems":1,"type":"array"}},"required":["refs"],"type":"object"},"BatchLookupOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/BatchLookupOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"refs":{"items":{"$ref":"#/components/schemas/LookupRefResult"},"type":"array"}},"required":["refs"],"type":"object"},"Binding":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Binding.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"attributeFilter":{},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"providerId":{"type":"string"}},"required":["id","accountId","providerId","attributeFilter","createdAt"],"type":"object"},"Cluster":{"additionalProperties":true,"properties":{"apiUrl":{"type":"string"},"isDefault":{"type":"boolean"},"jurisdiction":{"type":"string"},"publicUrl":{"type":"string"},"slug":{"type":"string"}},"required":["slug","jurisdiction","publicUrl","isDefault"],"type":"object"},"CreateBindingInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateBindingInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"attributeFilter":{"description":"Exact-match key/value map; empty filter matches any token."},"providerId":{"minLength":1,"type":"string"}},"required":["providerId"],"type":"object"},"CreateMirrorInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateMirrorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"DNS host of the destination cluster.","minLength":1,"type":"string"},"owner":{"minLength":1,"type":"string"},"provider":{"enum":["github"],"type":"string"},"repo":{"minLength":1,"type":"string"}},"required":["provider","owner","repo","clusterHost"],"type":"object"},"CreateOrgInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateOrgInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"description":"Display name.","maxLength":100,"minLength":1,"type":"string"},"region":{"description":"Jurisdiction slug; defaults to the server's home jurisdiction.","type":"string"}},"required":["name"],"type":"object"},"CreateProjectInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateProjectInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"maxLength":100,"minLength":1,"type":"string"},"ownerId":{"minLength":1,"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"region":{"type":"string"}},"required":["name","ownerType","ownerId"],"type":"object"},"CreateRepoInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateRepoInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"Public host of the cluster to pin the repo to (e.g. royalcanin.partial.to); empty lands on the jurisdiction default.","type":"string"},"name":{"minLength":1,"type":"string"},"objectFormat":{"description":"Hash format; defaults to sha1.","enum":["sha1","sha256"],"type":"string"},"projectId":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},"required":["projectId","name"],"type":"object"},"CreateServiceAccountInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreateServiceAccountInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"name":{"minLength":1,"type":"string"},"orgId":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},"required":["orgId","name"],"type":"object"},"CreatedMirror":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/CreatedMirror.json"],"format":"uri","readOnly":true,"type":"string"},"created":{"description":"true on fresh creation; false when an existing mirror was returned.","type":"boolean"},"empty":{"description":"true when the upstream has no refs to clone.","type":"boolean"},"mirrorId":{"type":"string"},"mirrorUrl":{"type":"string"},"publicUrl":{"type":"string"}},"required":["mirrorId","mirrorUrl","publicUrl","created","empty"],"type":"object"},"ErrorDetail":{"additionalProperties":true,"properties":{"location":{"description":"Where the error occurred, e.g. 'body.items[3].tags' or 'path.thing-id'","type":"string"},"message":{"description":"Error message text","type":"string"},"value":{"description":"The value at the given location"}},"type":"object"},"ErrorModel":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ErrorModel.json"],"format":"uri","readOnly":true,"type":"string"},"detail":{"description":"A human-readable explanation specific to this occurrence of the problem.","examples":["Property foo is required but is missing."],"type":"string"},"errors":{"description":"Optional list of individual error details","items":{"$ref":"#/components/schemas/ErrorDetail"},"type":"array"},"instance":{"description":"A URI reference that identifies the specific occurrence of the problem.","examples":["https://example.com/error-log/abc123"],"format":"uri","type":"string"},"status":{"description":"HTTP status code","examples":[400],"format":"int64","type":"integer"},"title":{"description":"A short, human-readable summary of the problem type. This value should not change between occurrences of the error.","examples":["Bad Request"],"type":"string"},"type":{"default":"about:blank","description":"A URI reference to human-readable documentation for the error.","examples":["https://example.com/errors/example"],"format":"uri","type":"string"}},"type":"object"},"GetMeOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetMeOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"auth":{"$ref":"#/components/schemas/MeAuth"},"global":{"$ref":"#/components/schemas/MeGlobal"},"jurisdiction":{"type":"string"},"mode":{"enum":["standalone","global","regional"],"type":"string"},"regional":{"$ref":"#/components/schemas/MeRegional"},"regionalUnavailable":{"$ref":"#/components/schemas/MeRegionalUnavailable"}},"required":["global","auth"],"type":"object"},"GetPermissionsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetPermissionsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"explain":{"additionalProperties":{},"type":"object"},"permissions":{"items":{"type":"string"},"type":"array"},"resourceId":{"type":"string"},"resourceType":{"type":"string"}},"required":["resourceType","resourceId"],"type":"object"},"GetRepoVisibilityOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetRepoVisibilityOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"GetVersionOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GetVersionOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"mode":{"description":"Server mode.","enum":["standalone","global","regional"],"type":"string"},"version":{"description":"Git commit SHA of the running entire-core binary, or \"dev\" for an untagged local build.","type":"string"}},"required":["version"],"type":"object"},"GrantMirrorCollaboratorInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantMirrorCollaboratorInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"},"handle":{"description":"Qualified grantee handle, e.g. github:alice.","minLength":1,"type":"string"},"owner":{"minLength":1,"type":"string"},"provider":{"enum":["github"],"type":"string"},"repo":{"minLength":1,"type":"string"},"role":{"description":"Grant level: reader (pull) or writer (pull+push).","enum":["reader","writer"],"type":"string"}},"required":["provider","owner","repo","clusterHost","handle","role"],"type":"object"},"GrantProjectAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantProjectAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"granteeType":{"default":"account","enum":["account"],"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["provider","providerUserId","role"],"type":"object"},"GrantProjectAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantProjectAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantRepoAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantRepoAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"granteeType":{"default":"account","enum":["account"],"type":"string"},"provider":{"minLength":1,"type":"string"},"providerUserId":{"minLength":1,"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["provider","providerUserId","role"],"type":"object"},"GrantRepoAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantRepoAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantServiceAccountAccessInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantServiceAccountAccessInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"resourceId":{"minLength":1,"type":"string"},"resourceType":{"enum":["repo","project"],"type":"string"},"role":{"enum":["reader","writer","admin"],"type":"string"}},"required":["resourceType","resourceId","role"],"type":"object"},"GrantServiceAccountAccessOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantServiceAccountAccessOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"status":{"type":"string"}},"required":["status"],"type":"object"},"GrantedMirrorCollaborator":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/GrantedMirrorCollaborator.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"description":"Entire account the grant was written for.","type":"string"},"role":{"type":"string"}},"required":["accountId","role"],"type":"object"},"ListAuditEventsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListAuditEventsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"events":{"items":{"$ref":"#/components/schemas/AuditEvent"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["events"],"type":"object"},"ListAvailableMirrorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListAvailableMirrorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"available":{"items":{"$ref":"#/components/schemas/AvailableMirror"},"type":"array"}},"required":["available"],"type":"object"},"ListBindingsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListBindingsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"bindings":{"items":{"$ref":"#/components/schemas/Binding"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["bindings"],"type":"object"},"ListClustersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListClustersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"clusters":{"items":{"$ref":"#/components/schemas/Cluster"},"type":"array"}},"required":["clusters"],"type":"object"},"ListMirrorCollaboratorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListMirrorCollaboratorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"collaborators":{"items":{"$ref":"#/components/schemas/MirrorCollaborator"},"type":"array"}},"required":["collaborators"],"type":"object"},"ListMirrorsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListMirrorsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"mirrors":{"items":{"$ref":"#/components/schemas/Mirror"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["mirrors"],"type":"object"},"ListOIDCProvidersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOIDCProvidersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"providers":{"items":{"$ref":"#/components/schemas/OIDCProvider"},"type":"array"}},"required":["providers"],"type":"object"},"ListOrgMembersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgMembersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"members":{"items":{"$ref":"#/components/schemas/Membership"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["members"],"type":"object"},"ListOrgProjectsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"project":{"$ref":"#/components/schemas/Project"},"projects":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}},"type":"object"},"ListOrgsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListOrgsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"org":{"$ref":"#/components/schemas/Org"},"orgs":{"items":{"$ref":"#/components/schemas/Org"},"type":"array"}},"type":"object"},"ListProjectMembersOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectMembersOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"members":{"items":{"$ref":"#/components/schemas/ProjectGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["members"],"type":"object"},"ListProjectReposOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectReposOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"repo":{"$ref":"#/components/schemas/Repo"},"repos":{"items":{"$ref":"#/components/schemas/Repo"},"type":"array"}},"type":"object"},"ListProjectsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListProjectsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"project":{"$ref":"#/components/schemas/Project"},"projects":{"items":{"$ref":"#/components/schemas/Project"},"type":"array"}},"type":"object"},"ListRepoGrantsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListRepoGrantsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"grants":{"items":{"$ref":"#/components/schemas/RepoGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["grants"],"type":"object"},"ListServiceAccountGrantsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListServiceAccountGrantsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"grants":{"items":{"$ref":"#/components/schemas/ServiceAccountGrant"},"type":"array"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"}},"required":["grants"],"type":"object"},"ListServiceAccountsOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ListServiceAccountsOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"serviceAccounts":{"items":{"$ref":"#/components/schemas/ServiceAccountWithGrants"},"type":"array"}},"required":["serviceAccounts"],"type":"object"},"LookupRef":{"additionalProperties":true,"properties":{"id":{"minLength":1,"type":"string"},"type":{"description":"Resource type slug; \"org\", \"project\", \"repo\" are enriched, unknown types pass through.","minLength":1,"type":"string"}},"required":["type","id"],"type":"object"},"LookupRefResult":{"additionalProperties":true,"properties":{"id":{"type":"string"},"name":{"type":"string"},"ownerId":{"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"projectId":{"type":"string"},"type":{"type":"string"},"url":{"type":"string"}},"required":["type","id"],"type":"object"},"LookupResourcesOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/LookupResourcesOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"nextPageToken":{"description":"Pass back to fetch the next page; empty when no more entries.","type":"string"},"permission":{"type":"string"},"resourceIds":{"items":{"type":"string"},"type":"array"},"resourceType":{"type":"string"},"resources":{"items":{"$ref":"#/components/schemas/ResourceAccess"},"type":"array"}},"required":["resourceType"],"type":"object"},"MeAuth":{"additionalProperties":true,"properties":{"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["provider","providerUserId"],"type":"object"},"MeGlobal":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"avatarUrl":{"type":"string"},"handle":{"type":"string"},"handles":{"items":{"$ref":"#/components/schemas/MeIdentityHandle"},"type":"array"},"homeJurisdiction":{"type":"string"}},"required":["accountId","handles"],"type":"object"},"MeIdentityHandle":{"additionalProperties":true,"properties":{"email":{"description":"The provider's publicly-visible profile email (from the provider at login; may be empty). NOT the account's contact email.","type":"string"},"handle":{"type":"string"},"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["provider","handle","providerUserId"],"type":"object"},"MeRegional":{"additionalProperties":true,"properties":{"bio":{"type":"string"},"company":{"type":"string"},"displayName":{"type":"string"},"email":{"type":"string"},"location":{"type":"string"}},"type":"object"},"MeRegionalUnavailable":{"additionalProperties":true,"properties":{"error":{"description":"Always 'foreign_jurisdiction'. Discriminator for client-side state machines.","enum":["foreign_jurisdiction"],"type":"string"},"homeCoreUrl":{"description":"Deep link into the home console for this account.","type":"string"},"jurisdiction":{"description":"The account's home jurisdiction (e.g. 'us', 'eu').","type":"string"},"message":{"description":"Human-readable copy ready to surface in a UI.","type":"string"}},"required":["error","jurisdiction","homeCoreUrl","message"],"type":"object"},"Membership":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Membership.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"orgId":{"type":"string"},"role":{"type":"string"},"status":{"type":"string"},"workosOrgMembershipId":{"type":"string"}},"required":["id","accountId","orgId","role","status","createdAt"],"type":"object"},"Mirror":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Mirror.json"],"format":"uri","readOnly":true,"type":"string"},"cell":{"description":"Physical cell the mirror's cluster runs in, e.g. aws-us-east-2.","type":"string"},"clusterHost":{"description":"Public host of the cluster serving this mirror.","type":"string"},"createdAt":{"format":"date-time","type":"string"},"installationId":{"format":"int64","type":"integer"},"isArchived":{"type":"boolean"},"isPrivate":{"type":"boolean"},"jurisdiction":{"type":"string"},"mirrorId":{"type":"string"},"owner":{"type":"string"},"provider":{"type":"string"},"repo":{"type":"string"},"status":{"description":"Clone lifecycle: processing (cloning), ready (clonable), failed (initial clone failed), or suspended.","enum":["processing","ready","failed","suspended"],"type":"string"},"suspendedAt":{"format":"date-time","type":"string"}},"required":["mirrorId","provider","owner","repo","clusterHost","createdAt"],"type":"object"},"MirrorCollaborator":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"handle":{"description":"Primary handle (provider:label), empty if none resolves.","type":"string"},"role":{"description":"reader (pull) or writer (pull+push).","type":"string"}},"required":["accountId","role"],"type":"object"},"OIDCProvider":{"additionalProperties":true,"properties":{"description":{"type":"string"},"displayName":{"type":"string"},"id":{"type":"string"},"issuer":{"type":"string"}},"required":["id","issuer"],"type":"object"},"Org":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Org.json"],"format":"uri","readOnly":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"region":{"type":"string"},"workosOrganizationId":{"type":"string"}},"required":["id","name","region","createdAt"],"type":"object"},"Project":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Project.json"],"format":"uri","readOnly":true,"type":"string"},"createdAt":{"format":"date-time","type":"string"},"id":{"type":"string"},"name":{"type":"string"},"ownerId":{"type":"string"},"ownerType":{"enum":["org","account"],"type":"string"},"region":{"type":"string"}},"required":["id","name","ownerType","ownerId","region","createdAt"],"type":"object"},"ProjectGrant":{"additionalProperties":true,"properties":{"granteeId":{"type":"string"},"granteeName":{"type":"string"},"granteeType":{"type":"string"},"role":{"type":"string"},"source":{"type":"string"}},"required":["granteeType","granteeId","role","source"],"type":"object"},"Repo":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/Repo.json"],"format":"uri","readOnly":true,"type":"string"},"clusterHost":{"type":"string"},"foreign":{"type":"boolean"},"id":{"type":"string"},"mirrorSuspended":{"type":"boolean"},"mirrorSuspendedAt":{"type":"string"},"name":{"type":"string"},"objectFormat":{"enum":["sha1","sha256"],"type":"string"},"owningProjectId":{"type":"string"},"path":{"type":"string"},"provisionAttempts":{"format":"int64","type":"integer"},"provisionReason":{"type":"string"},"state":{"enum":["provisioning","active","failed"],"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["id","owningProjectId","name"],"type":"object"},"RepoGrant":{"additionalProperties":true,"properties":{"granteeId":{"type":"string"},"granteeName":{"type":"string"},"granteeType":{"type":"string"},"role":{"type":"string"},"source":{"type":"string"}},"required":["granteeType","granteeId","role","source"],"type":"object"},"ResolvedIdentity":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ResolvedIdentity.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"handle":{"type":"string"},"provider":{"type":"string"},"providerUserId":{"type":"string"}},"required":["accountId","provider","handle","providerUserId"],"type":"object"},"ResourceAccess":{"additionalProperties":true,"properties":{"permissions":{"items":{"type":"string"},"type":"array"},"resourceId":{"type":"string"}},"required":["resourceId","permissions"],"type":"object"},"ServiceAccount":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/ServiceAccount.json"],"format":"uri","readOnly":true,"type":"string"},"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"name":{"type":"string"},"orgId":{"type":"string"},"status":{"type":"string"},"systemManaged":{"type":"boolean"}},"required":["accountId","name","orgId","status","systemManaged","createdAt"],"type":"object"},"ServiceAccountGrant":{"additionalProperties":true,"properties":{"resourceId":{"type":"string"},"resourceName":{"type":"string"},"resourceType":{"type":"string"},"role":{"type":"string"}},"required":["resourceType","resourceId","role"],"type":"object"},"ServiceAccountWithGrants":{"additionalProperties":true,"properties":{"accountId":{"type":"string"},"createdAt":{"format":"date-time","type":"string"},"grants":{"items":{"type":"string"},"type":"array"},"name":{"type":"string"},"orgId":{"type":"string"},"status":{"type":"string"},"systemManaged":{"type":"boolean"}},"required":["grants","accountId","name","orgId","status","systemManaged","createdAt"],"type":"object"},"SetRepoVisibilityInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/SetRepoVisibilityInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"SetRepoVisibilityOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/SetRepoVisibilityOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"visibility":{"enum":["public","private"],"type":"string"}},"required":["visibility"],"type":"object"},"UpdateMeInputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/UpdateMeInputBody.json"],"format":"uri","readOnly":true,"type":"string"},"email":{"description":"Contact email.","format":"email","maxLength":254,"minLength":5,"type":"string"}},"required":["email"],"type":"object"},"UpdateMeOutputBody":{"additionalProperties":true,"properties":{"$schema":{"description":"A URL to the JSON Schema for this object.","examples":["/api/v1/schemas/UpdateMeOutputBody.json"],"format":"uri","readOnly":true,"type":"string"},"email":{"type":"string"}},"required":["email"],"type":"object"}},"securitySchemes":{"bearerAuth":{"bearerFormat":"JWT","description":"Bearer token minted by entire-core's device-code flow or STS exchange.","scheme":"bearer","type":"http"},"sessionAuth":{"description":"Console session cookie issued by the browser login flow.","in":"cookie","name":"entire_session","type":"apiKey"}}},"info":{"description":"Entire control plane: identity, orgs, projects, repos, mirrors, service accounts.","title":"Entire Core API","version":"1.0.0"},"openapi":"3.1.0","paths":{"/access/{resourceType}":{"get":{"operationId":"lookupResources","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"SpiceDB resource type (e.g. \"repo\", \"project\", \"org\").","in":"path","name":"resourceType","required":true,"schema":{"description":"SpiceDB resource type (e.g. \"repo\", \"project\", \"org\").","minLength":1,"type":"string"}},{"description":"Optional: only list resources where the caller has this permission. pageSize/pageToken apply only when set.","explode":false,"in":"query","name":"permission","schema":{"description":"Optional: only list resources where the caller has this permission. pageSize/pageToken apply only when set.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/LookupResourcesOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List resources of a type the caller can access","tags":["identity"]}},"/access/{resourceType}/{resourceId}":{"get":{"operationId":"getPermissions","parameters":[{"in":"path","name":"resourceType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"resourceId","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"If set, return the SpiceDB trace for this permission instead of the permission list.","explode":false,"in":"query","name":"explain","schema":{"description":"If set, return the SpiceDB trace for this permission instead of the permission list.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetPermissionsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the caller's permissions on a single resource","tags":["identity"]}},"/audit":{"get":{"operationId":"listAuditEvents","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAuditEventsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the calling account's recent audit events","tags":["identity"]}},"/clusters":{"get":{"operationId":"listClusters","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListClustersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List data-plane clusters attached to this control plane","tags":["clusters"]}},"/identity/handles/{provider}/{handle}":{"get":{"operationId":"resolveHandle","parameters":[{"description":"IdP slug (e.g. \"github\").","in":"path","name":"provider","required":true,"schema":{"description":"IdP slug (e.g. \"github\").","minLength":1,"type":"string"}},{"description":"User-visible handle at the provider.","in":"path","name":"handle","required":true,"schema":{"description":"User-visible handle at the provider.","minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ResolvedIdentity"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Resolve account by external provider handle","tags":["identity"]}},"/lookup":{"post":{"operationId":"batchLookup","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/BatchLookupOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Batch-resolve (type, id) refs to enriched records","tags":["identity"]}},"/me":{"get":{"operationId":"getMe","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetMeOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get the calling account's identity and profile","tags":["identity"]},"patch":{"operationId":"updateMe","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/UpdateMeOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Update the calling account's contact email","tags":["identity"]}},"/mirrors":{"delete":{"operationId":"deleteMirror","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete a mirror by upstream coords + cluster host","tags":["mirrors"]},"get":{"operationId":"listMirrors","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: restrict to mirrors on this cluster (public host, e.g. royalcanin.partial.to). Case-sensitive exact match.","explode":false,"in":"query","name":"cluster","schema":{"description":"Optional: restrict to mirrors on this cluster (public host, e.g. royalcanin.partial.to). Case-sensitive exact match.","type":"string"}},{"description":"Optional: restrict to mirrors of this upstream provider, case-insensitive (e.g. \"github\").","explode":false,"in":"query","name":"provider","schema":{"description":"Optional: restrict to mirrors of this upstream provider, case-insensitive (e.g. \"github\").","type":"string"}},{"description":"Optional: restrict to mirrors with this upstream owner login (case-insensitive).","explode":false,"in":"query","name":"owner","schema":{"description":"Optional: restrict to mirrors with this upstream owner login (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMirrorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List mirrors visible to the caller","tags":["mirrors"]},"post":{"operationId":"createMirror","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateMirrorInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreatedMirror"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"412":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Precondition Failed"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create GitHub mirror","tags":["mirrors"]}},"/mirrors/available":{"get":{"operationId":"listAvailableMirrors","parameters":[{"description":"Optional: restrict to repos with this owner login (case-insensitive).","explode":false,"in":"query","name":"owner","schema":{"description":"Optional: restrict to repos with this owner login (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListAvailableMirrorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List GitHub repos the caller could onboard as mirrors","tags":["mirrors"]}},"/mirrors/collaborators":{"delete":{"operationId":"revokeMirrorCollaborator","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}},{"description":"Qualified grantee handle, e.g. github:alice.","explode":false,"in":"query","name":"handle","required":true,"schema":{"description":"Qualified grantee handle, e.g. github:alice.","minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke a user's access to a mirror (live GitHub-admin gated)","tags":["mirrors"]},"get":{"operationId":"listMirrorCollaborators","parameters":[{"explode":false,"in":"query","name":"provider","required":true,"schema":{"enum":["github"],"type":"string"}},{"explode":false,"in":"query","name":"owner","required":true,"schema":{"minLength":1,"type":"string"}},{"explode":false,"in":"query","name":"repo","required":true,"schema":{"minLength":1,"type":"string"}},{"description":"Public host of the cluster serving the mirror.","explode":false,"in":"query","name":"clusterHost","required":true,"schema":{"description":"Public host of the cluster serving the mirror.","minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListMirrorCollaboratorsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List the principals with access to a mirror (live GitHub-admin gated)","tags":["mirrors"]},"post":{"operationId":"grantMirrorCollaborator","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantMirrorCollaboratorInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantedMirrorCollaborator"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant a user reader/writer access to a mirror (live GitHub-admin gated)","tags":["mirrors"]}},"/mirrors/{mirrorId}":{"get":{"operationId":"getMirror","parameters":[{"in":"path","name":"mirrorId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Mirror"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get mirror by id","tags":["mirrors"]}},"/oidc-providers":{"get":{"operationId":"listOIDCProviders","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOIDCProvidersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List federated OIDC identity providers","tags":["identity"]}},"/orgs":{"get":{"operationId":"listOrgs","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: exact-match org name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match org name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List organizations the caller can see (or one by name)","tags":["orgs"]},"post":{"operationId":"createOrg","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateOrgInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create organization","tags":["orgs"]}},"/orgs/{orgId}":{"delete":{"operationId":"deleteOrg","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete an organization","tags":["orgs"]},"get":{"operationId":"getOrg","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Org"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get an organization","tags":["orgs"]}},"/orgs/{orgId}/members":{"get":{"operationId":"listOrgMembers","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgMembersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List members of an organization","tags":["orgs"]},"post":{"operationId":"addOrgMember","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/AddOrgMemberInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Membership"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Add a member to an organization","tags":["orgs"]}},"/orgs/{orgId}/members/{provider}/{providerUserId}":{"delete":{"operationId":"removeOrgMember","parameters":[{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Remove a member from an organization","tags":["orgs"]}},"/orgs/{orgId}/projects":{"get":{"operationId":"listOrgProjects","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"description":"Optional: exact-match project name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match project name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListOrgProjectsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List projects owned by an organization","tags":["projects"]}},"/projects":{"get":{"operationId":"listProjects","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"description":"Optional: exact-match project name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match project name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List projects accessible to the caller (or one by name)","tags":["projects"]},"post":{"operationId":"createProject","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateProjectInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create project","tags":["projects"]}},"/projects/{projectId}":{"delete":{"operationId":"deleteProject","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete a project","tags":["projects"]},"get":{"operationId":"getProject","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Project"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get a project by id","tags":["projects"]}},"/projects/{projectId}/grants":{"post":{"operationId":"grantProjectAccess","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantProjectAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantProjectAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant project access to an identity","tags":["projects"]}},"/projects/{projectId}/grants/account/{provider}/{providerUserId}":{"delete":{"operationId":"revokeProjectAccessByProvider","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke project access by provider identity","tags":["projects"]}},"/projects/{projectId}/grants/{granteeType}/{granteeId}":{"delete":{"operationId":"revokeProjectAccess","parameters":[{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"granteeType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"granteeId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke project access by grantee id","tags":["projects"]}},"/projects/{projectId}/members":{"get":{"operationId":"listProjectMembers","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectMembersOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List project members and their roles","tags":["projects"]}},"/projects/{projectId}/repos":{"get":{"operationId":"listProjectRepos","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"projectId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"description":"Optional: exact-match repo name (case-insensitive).","explode":false,"in":"query","name":"name","schema":{"description":"Optional: exact-match repo name (case-insensitive).","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListProjectReposOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List repositories in a project (or one by name)","tags":["repos"]}},"/repos":{"post":{"operationId":"createRepo","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateRepoInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Repo"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create repository","tags":["repos"]}},"/repos/{repoId}":{"delete":{"operationId":"deleteRepo","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"421":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Misdirected Request"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"502":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Gateway"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete repository","tags":["repos"]},"get":{"operationId":"getRepo","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Repo"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get repository","tags":["repos"]}},"/repos/{repoId}/grants":{"get":{"operationId":"listRepoGrants","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListRepoGrantsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List repo grants","tags":["repos"]},"post":{"operationId":"grantRepoAccess","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRepoAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantRepoAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant repo access to an identity","tags":["repos"]}},"/repos/{repoId}/grants/account/{provider}/{providerUserId}":{"delete":{"operationId":"revokeRepoAccessByProvider","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"provider","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"providerUserId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke repo access by provider identity","tags":["repos"]}},"/repos/{repoId}/grants/{granteeType}/{granteeId}":{"delete":{"operationId":"revokeRepoAccess","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}},{"in":"path","name":"granteeType","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"granteeId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke repo access by grantee id","tags":["repos"]}},"/repos/{repoId}/visibility":{"get":{"operationId":"getRepoVisibility","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetRepoVisibilityOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get repository visibility","tags":["repos"]},"put":{"operationId":"setRepoVisibility","parameters":[{"in":"path","name":"repoId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetRepoVisibilityInputBody"}}},"required":true},"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/SetRepoVisibilityOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Set repository visibility","tags":["repos"]}},"/service-accounts":{"get":{"operationId":"listServiceAccounts","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"explode":false,"in":"query","name":"orgId","required":true,"schema":{"pattern":"^[0-9A-HJKMNP-TV-Z]{26}$","type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceAccountsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List service accounts in an org","tags":["service-accounts"]},"post":{"operationId":"createServiceAccount","requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateServiceAccountInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccount"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create service account","tags":["service-accounts"]}},"/service-accounts/{accountId}":{"delete":{"operationId":"deleteServiceAccount","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete service account","tags":["service-accounts"]},"get":{"operationId":"getServiceAccount","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ServiceAccount"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Get service account","tags":["service-accounts"]}},"/service-accounts/{accountId}/bindings":{"get":{"operationId":"listBindings","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListBindingsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List OIDC bindings","tags":["service-accounts"]},"post":{"operationId":"createBinding","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/CreateBindingInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/Binding"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Create OIDC binding","tags":["service-accounts"]}},"/service-accounts/{accountId}/bindings/{bindingId}":{"delete":{"operationId":"deleteBinding","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"bindingId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Delete OIDC binding","tags":["service-accounts"]}},"/service-accounts/{accountId}/grants":{"get":{"operationId":"listServiceAccountGrants","parameters":[{"description":"Maximum entries to return; server may cap further.","explode":false,"in":"query","name":"pageSize","schema":{"description":"Maximum entries to return; server may cap further.","format":"int32","maximum":500,"minimum":0,"type":"integer"}},{"description":"Opaque cursor from a previous response's nextPageToken.","explode":false,"in":"query","name":"pageToken","schema":{"description":"Opaque cursor from a previous response's nextPageToken.","type":"string"}},{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/ListServiceAccountGrantsOutputBody"}}},"description":"OK"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"List service account grants","tags":["service-accounts"]},"post":{"operationId":"grantServiceAccountAccess","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}}],"requestBody":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantServiceAccountAccessInputBody"}}},"required":true},"responses":{"201":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GrantServiceAccountAccessOutputBody"}}},"description":"Created"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"409":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Conflict"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Grant service account access on a repo or project","tags":["service-accounts"]}},"/service-accounts/{accountId}/grants/{resourceType}/{resourceId}":{"delete":{"operationId":"revokeServiceAccountAccess","parameters":[{"in":"path","name":"accountId","required":true,"schema":{"minLength":1,"type":"string"}},{"in":"path","name":"resourceType","required":true,"schema":{"enum":["repo","project"],"type":"string"}},{"in":"path","name":"resourceId","required":true,"schema":{"minLength":1,"type":"string"}}],"responses":{"204":{"description":"No Content"},"400":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Bad Request"},"401":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unauthorized"},"403":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Forbidden"},"404":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Not Found"},"422":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Unprocessable Entity"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"},"503":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Service Unavailable"}},"security":[{"bearerAuth":[]},{"sessionAuth":[]}],"summary":"Revoke service account access","tags":["service-accounts"]}},"/version":{"get":{"operationId":"getVersion","responses":{"200":{"content":{"application/json":{"schema":{"$ref":"#/components/schemas/GetVersionOutputBody"}}},"description":"OK"},"500":{"content":{"application/problem+json":{"schema":{"$ref":"#/components/schemas/ErrorModel"}}},"description":"Internal Server Error"}},"summary":"Get the server version and mode","tags":["meta"]}}},"servers":[{"url":"/api/v1"}]}\
No newline at end of file\
```\
\
Minternal/coreapi/spec/core.openapi.json+1/-1\
\
```\
892 unmodified lines\
\
893\
894\
895\
896\
897\
896\
897\
898\
899\
900\
901\
902\
903\
904\
905\
906\
907\
908\
909\
910\
911\
899\
900\
901\
902\
903\
904\
905\
906\
907\
908\
912\
913\
914\
910\
911\
915\
916\
917\
918\
919\
913\
914\
915\
916\
917\
918\
919\
920\
921\
922\
923\
920\
921\
922\
925\
926\
927\
923\
924\
925\
926\
927\
\
892 unmodified lines\
\
    }))\
    defer entry.Close()\
\
    const iterations = 8\
    deadFailoverObserved := false\
    var failed []string\
    p := New(Config{\
        Nodes: replicas.NodeConfig{\
            EntryURL:    entry.URL,\
            ClusterHost: "127.0.0.1",\
        },\
        Path:         "/et/alice/repo",\
        OnNodeFailed: func(node string) { failed = append(failed, node) },\
    })\
    // doWithFailover starts at a random offset for load-spreading, so with two\
    // adopted replicas [dead, alive] the dead one is only attempted ~half the\
    // time — the old loop-8-and-hope made that a ~1/256 flake. Pin the sticky\
    // node to dead so it is always tried first: that deterministically exercises\
    // the failure → OnNodeFailed → failover-to-alive path this test is about.\
    p.stickyNode = dead\
\
    for i := range iterations {\
        var failed []string\
        p := New(Config{\
            Nodes: replicas.NodeConfig{\
                EntryURL:    entry.URL,\
                ClusterHost: "127.0.0.1",\
            },\
            Path:         "/et/alice/repo",\
            OnNodeFailed: func(node string) { failed = append(failed, node) },\
        })\
    body, err := p.InfoRefs(context.Background(), "git-upload-pack")\
    require.NoError(t, err, "failover must succeed")\
\
        body, err := p.InfoRefs(context.Background(), "git-upload-pack")\
        require.NoErrorf(t, err, "iteration %d: failover must succeed", i)\
    got, err := io.ReadAll(body)\
    require.NoError(t, err)\
    _ = body.Close()\
    assert.Equal(t, "refs from alive", string(got), "response must come from alive replica")\
\
        got, err := io.ReadAll(body)\
        require.NoError(t, err)\
        _ = body.Close()\
        assert.Equal(t, "refs from alive", string(got), "iteration %d: response must come from alive replica", i)\
\
        if !slices.Equal(p.nodes, []string{aliveURL}) {\
            t.Errorf("iteration %d: nodes after failover = %v, want [%s]", i, p.nodes, aliveURL)\
        }\
        if slices.Contains(failed, dead) {\
            deadFailoverObserved = true\
        }\
    if !slices.Equal(p.nodes, []string{aliveURL}) {\
        t.Errorf("nodes after failover = %v, want [%s]", p.nodes, aliveURL)\
    }\
\
    if !deadFailoverObserved {\
        t.Errorf("dead replica never marked failed across %d iterations — failover path may not be exercised", iterations)\
    if !slices.Contains(failed, dead) {\
        t.Errorf("dead replica %s was not marked failed; failover path not exercised (failed=%v)", dead, failed)\
    }\
}\
```\
\
Minternal/remotehelper/transport/proxy\_test.go+25/-28\
\
```\
30 unmodified lines\
\
31\
32\
33\
34\
34\
35\
36\
37\
38\
39\
40\
41\
42\
43\
44\
36\
37\
45\
46\
47\
48\
\
30 unmodified lines\
\
# triggers a Go toolchain panic on Windows ARM64 in modload/search.go\
# (slice bounds out of range) and a GetFileInformationByHandleEx error\
# with relative paths.\
gotestsum --format dots --jsonfile "$E2E_ARTIFACT_DIR/test-events.json" -- \\
#\
# --rerun-fails retries only the tests that failed (one extra attempt) to\
# absorb transient real-agent non-determinism. It requires --packages (so the\
# package list can no longer be a trailing positional arg) and -count=1. The\
# default --rerun-fails-max-failures=10 means a broadly-broken run (>10\
# failures) is reported as-is rather than retried, so a genuine regression\
# still surfaces. Do NOT add reruns to the deterministic canary/roger-roger\
# tasks — there a failure is always a real bug and must not be masked.\
gotestsum --format dots --rerun-fails=1 --packages=./e2e/tests \\
  --jsonfile "$E2E_ARTIFACT_DIR/test-events.json" -- \\
  -tags=e2e -count=1 -timeout=30m \\
  ${filter:+-run "$filter"} \\
  ./e2e/tests || rc=$?\
  ${filter:+-run "$filter"} || rc=$?\
\
go run ./e2e/cmd/testreport -color -o "$E2E_ARTIFACT_DIR/report.txt" "$E2E_ARTIFACT_DIR/test-events.json"\
echo ""\
```\
\
Mmise-tasks/test/e2e/\_default+11/-3