Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Platform/Claude-Code-Prompt-Strict-Ops.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ Rules:
- Use `--scope-note` and `--non-goal` when extra boundaries matter.
- Use `summary`, `cleanup-report --markdown`, `close-ready-report --markdown`, and `notify-close-plan --markdown` for user-facing communication.
- Check `gh-auth-check --compact` before merge-aware cleanup.
- Never delete or close a Jules session without explicit user confirmation.
- Never close, cancel, or delete a Jules session without explicit user confirmation.
- Summarize long JSON outputs into concise operational messages.

Sequence:
Expand All @@ -41,7 +41,7 @@ Rules:
- Use `--scope-note` and `--non-goal` when extra boundaries matter.
- Use `summary`, `cleanup-report --markdown`, `close-ready-report --markdown`, and `notify-close-plan --markdown` for user-facing communication.
- Check `gh-auth-check --compact` before merge-aware cleanup.
- Never delete or close a Jules session without explicit user confirmation.
- Never close, cancel, or delete a Jules session without explicit user confirmation.
- Summarize long JSON outputs into concise operational messages.

Sequence:
Expand Down
4 changes: 2 additions & 2 deletions Platform/Claude-Code-Prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ When working with Google Jules:
- If the task is ambiguous or appears to require out-of-scope work, ask a clarifying question instead of broadening the task.
- Use `--scope-note` and `--non-goal` when you need extra task boundaries.
- Use `summary`, `cleanup-report`, `close-ready-report`, and `notify-close-plan --markdown` to keep users informed.
- Never close or delete a Jules session without explicit user confirmation.
- Never close, cancel, or delete a Jules session without explicit user confirmation.
- Before merge-aware cleanup actions, verify GitHub auth with `gh-auth-check --compact`.
- Summarize long JSON outputs into concise user-facing updates.

Expand Down Expand Up @@ -44,7 +44,7 @@ When working with Google Jules:
- If the task is ambiguous or appears to require out-of-scope work, ask a clarifying question instead of broadening the task.
- Use `--scope-note` and `--non-goal` when you need extra task boundaries.
- Use `summary`, `cleanup-report`, `close-ready-report`, and `notify-close-plan --markdown` to keep users informed.
- Never close or delete a Jules session without explicit user confirmation.
- Never close, cancel, or delete a Jules session without explicit user confirmation.
- Before merge-aware cleanup actions, verify GitHub auth with `gh-auth-check --compact`.
- Summarize long JSON outputs into concise user-facing updates.

Expand Down
4 changes: 2 additions & 2 deletions Platform/Migration-Guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ Use this document as the shared baseline when adapting the `google-jules-control
- Keep `google-jules-control/scripts/jules_api.py` as the single source of truth for Jules API control
- Keep `.env` and `JULES_API_KEY` as the primary auth path
- Keep merge checks based on `gh`
- Keep session closure behind explicit user confirmation
- Keep session close, cancel, and delete actions behind explicit user confirmation

### Platform Migration Checkpoints

Expand All @@ -68,7 +68,7 @@ Use this document as the shared baseline when adapting the `google-jules-control
- support for absolute-path links
- markdown rendering behavior
4. Operational safety
- user confirmation before deletion
- user confirmation before close, cancel, or delete
- merge-state verification
- protection against leaking `.env`

Expand Down
7 changes: 6 additions & 1 deletion docs/setup-and-test.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,8 +84,13 @@ python3 google-jules-control/scripts/jules_api.py summary --session sessions/SES

5. 테스트 세션 정리 / Clean up the test session

이 단계는 되돌릴 수 없는 세션 삭제입니다. 테스트 세션을 삭제해도 된다고 확인한 뒤 실행합니다.
This is irreversible session deletion. Run it only after confirming the test session can be deleted.

```bash
python3 google-jules-control/scripts/jules_api.py delete-session --session sessions/SESSION_ID
python3 google-jules-control/scripts/jules_api.py delete-session \
--session sessions/SESSION_ID \
--confirm-delete DELETE_JULES_SESSION
```

## 자주 쓰는 명령 / Common Commands
Expand Down
16 changes: 13 additions & 3 deletions google-jules-control/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -247,10 +247,15 @@ Suggested flow:

```bash
python3 scripts/jules_api.py summary --session sessions/SESSION_ID
python3 scripts/jules_api.py cancel-session --session sessions/SESSION_ID
```

State clearly that this is implemented as session deletion and is not a reversible pause.
State clearly that cancel is implemented as session deletion and is not a reversible pause. After explicit user confirmation, run:

```bash
python3 scripts/jules_api.py cancel-session \
--session sessions/SESSION_ID \
--confirm-delete DELETE_JULES_SESSION
```

### Example: Check active sessions and close a merged one safely

Expand Down Expand Up @@ -461,7 +466,7 @@ python3 scripts/jules_api.py close-ready-report --repo-filter owner/repo --requi
- Use `--non-goal` when you need to forbid refactors, cleanup, dependency changes, schema changes, or other adjacent work.
- If the task is ambiguous, prefer a follow-up question over a broader instruction.
- Prefer goal-driven follow-ups with explicit verification targets over vague “improve this” style prompts.
- Treat `cancel-session` as destructive. It maps to session deletion, not a reversible pause.
- Treat `delete-session` and `cancel-session` as destructive. They map to irreversible session deletion, not a reversible pause, and require explicit user confirmation plus `--confirm-delete DELETE_JULES_SESSION`.
- For merged-work cleanup, always follow this order: inspect session, verify merged PR status, ask the user, then run `close-merged-session`.
- Use `--require-all-merged` when the session output contains more than one pull request URL.
- Use `list-unmerged-sessions` when the user wants a single view of work that is still open on GitHub.
Expand Down Expand Up @@ -514,6 +519,11 @@ The bundled script lives at `scripts/jules_api.py` and supports:

Run `python3 scripts/jules_api.py --help` or `python3 scripts/jules_api.py <command> --help` for flags.

Destructive commands intentionally require safety tokens:

- `delete-session` and `cancel-session`: `--confirm-delete DELETE_JULES_SESSION`
- `close-merged-session`: `--confirm-close CLOSE_MERGED_SESSION`

## Credential Setup

Create a `.env` file and add:
Expand Down
5 changes: 4 additions & 1 deletion google-jules-control/references/jules-reference.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Important session fields:
Practical note:

- There is no dedicated REST `resume` endpoint in the current public API. If a session is waiting, resume it by approving a pending plan or sending a follow-up message.
- There is no reversible REST `cancel` endpoint distinct from deletion. Deleting the session is the closest cancellation-style action.
- There is no reversible REST `cancel` endpoint distinct from deletion. Deleting the session is the closest cancellation-style action, so `delete-session` and `cancel-session` require `--confirm-delete DELETE_JULES_SESSION` after explicit user confirmation.
- The public API does not document a reliable remaining-usage or quota-balance endpoint for end users. Handle quota failures explicitly, but do not guess a remaining amount.

Activity types to watch:
Expand Down Expand Up @@ -107,6 +107,8 @@ The bundled `jules_api.py` script automates this with:
- `check-pr-readiness --session ...`
- `request-pr-rework --session ... --markdown`
- `notify-close-plan --session ... --markdown`
- `delete-session --session ... --confirm-delete DELETE_JULES_SESSION`
- `cancel-session --session ... --confirm-delete DELETE_JULES_SESSION`
- `close-merged-session --session ... --confirm-close CLOSE_MERGED_SESSION`

Implementation detail:
Expand All @@ -118,6 +120,7 @@ Implementation detail:
- `doctor` separates `api_ready`, `cli_ready`, and `merge_ready`. `ready=yes` means at least one control path is available.
- `close-ready-report` distinguishes between `candidates` and `cautionCandidates`. Treat caution entries as manual-review items, not automatic close targets.
- `close-merged-session` refuses `caution` sessions by default. Only use `--allow-caution-close` after explicit user approval.
- `delete-session` and `cancel-session` refuse to run without the explicit delete token because both are irreversible deletion flows.

Pagination contract:

Expand Down
14 changes: 14 additions & 0 deletions google-jules-control/scripts/jules_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@
OPEN_STATES = ACTIVE_STATES | WAITING_STATES
PR_URL_RE = re.compile(r"https://github\.com/[^/\s]+/[^/\s]+/pull/\d+")
CLOSE_CONFIRM_TOKEN = "CLOSE_MERGED_SESSION"
DELETE_CONFIRM_TOKEN = "DELETE_JULES_SESSION"
SUCCESSFUL_CHECK_CONCLUSIONS = {"SUCCESS", "NEUTRAL", "SKIPPED"}
FAILED_CHECK_CONCLUSIONS = {"FAILURE", "TIMED_OUT", "CANCELLED", "STARTUP_FAILURE", "ACTION_REQUIRED", "STALE"}
PENDING_CHECK_STATUSES = {"EXPECTED", "IN_PROGRESS", "PENDING", "QUEUED", "REQUESTED", "WAITING"}
Expand Down Expand Up @@ -1024,6 +1025,11 @@ def list_sessions(args: argparse.Namespace) -> None:

def delete_session(args: argparse.Namespace) -> None:
session_name = normalize_session_name(args.session)
if args.confirm_delete != DELETE_CONFIRM_TOKEN:
fail(
"Session deletion is irreversible and is not a pause. "
f"Re-run with --confirm-delete {DELETE_CONFIRM_TOKEN} after explicit user approval."
)
api_request("DELETE", f"/{session_name}")
print(json.dumps({"ok": True, "deleted": session_name}, ensure_ascii=False))

Expand Down Expand Up @@ -1793,13 +1799,21 @@ def build_parser() -> argparse.ArgumentParser:

delete_session_parser = subparsers.add_parser("delete-session", help="Delete a Jules session.")
delete_session_parser.add_argument("--session", required=True, help="Session id or sessions/<id> resource name.")
delete_session_parser.add_argument(
"--confirm-delete",
help=f"Required safety token. Must be exactly {DELETE_CONFIRM_TOKEN}.",
)
delete_session_parser.set_defaults(func=delete_session)

cancel_session_parser = subparsers.add_parser(
"cancel-session",
help="Delete a Jules session as a cancel-style action. This is permanent.",
)
cancel_session_parser.add_argument("--session", required=True, help="Session id or sessions/<id> resource name.")
cancel_session_parser.add_argument(
"--confirm-delete",
help=f"Required safety token. Must be exactly {DELETE_CONFIRM_TOKEN}.",
)
cancel_session_parser.set_defaults(func=delete_session)

get_session_parser = subparsers.add_parser("get-session", help="Fetch one Jules session.")
Expand Down
35 changes: 35 additions & 0 deletions tests/test_jules_api.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,41 @@ def test_close_merged_session_refuses_caution_without_override(self) -> None:

self.assertEqual(1, api_request.call_count)

def test_delete_session_requires_explicit_confirmation_token(self) -> None:
args = argparse.Namespace(session="sessions/123", confirm_delete=None)

with mock.patch.object(jules_api, "api_request") as api_request:
with redirect_stderr(io.StringIO()) as stderr:
with self.assertRaises(SystemExit):
jules_api.delete_session(args)

self.assertEqual(0, api_request.call_count)
self.assertIn("DELETE_JULES_SESSION", stderr.getvalue())

def test_delete_session_allows_explicit_confirmation_token(self) -> None:
args = argparse.Namespace(session="sessions/123", confirm_delete="DELETE_JULES_SESSION")

with mock.patch.object(jules_api, "api_request", return_value={}) as api_request:
with redirect_stdout(io.StringIO()):
jules_api.delete_session(args)

api_request.assert_called_once_with("DELETE", "/sessions/123")

def test_delete_session_parser_accepts_confirmation_token(self) -> None:
parser = jules_api.build_parser()

args = parser.parse_args(
[
"delete-session",
"--session",
"sessions/123",
"--confirm-delete",
"DELETE_JULES_SESSION",
]
)

self.assertEqual("DELETE_JULES_SESSION", args.confirm_delete)

def test_doctor_reports_api_ready_without_merge_ready(self) -> None:
args = argparse.Namespace(compact=True)
output = io.StringIO()
Expand Down