checkpoint_push_remote: reject pushurl-only remotes during election - #5
Draft
entire[bot] wants to merge 341 commits into
Draft
entire[bot] wants to merge 341 commits into
entire[bot] wants to merge 341 commits into
Conversation
Entire-Checkpoint: 01M2ECEB687V1RPFCT12TJ06J1
Entire-Checkpoint: 01M2EE0S4GJ8TPCXQ6D8KTBGWP
Entire-Checkpoint: 01M2EFKAY90V79JV8S53QXATCY
Entire-Checkpoint: 01M2EHA4WBB9J2YSNKDB2PPPQE
Entire-Checkpoint: 01M2EK27T5WEXV83TC3SC3XDBF
Entire-Checkpoint: 01M2ENTH8GCT0EWB4B32MER546
The session-ID fix does not need this. The store's location comes from the agent's own GetSessionDir, not from checkpoint metadata or a hook payload, so it is not the untrusted input this PR is about. It arrived as a bypass of the ValidateWritePath preflight added here, and that preflight is itself point-in-time defense-in-depth against an external subprocess that can race it. The cost was real and had no opt-out: a ~/.claude or ~/.codex managed by chezmoi/stow/yadm failed whenever the link was the deepest component that existed yet, and a session directory symlinked onto another volume failed outright, on reads as well as writes. allow_symlinked_agent_dirs covers worktree-relative config dirs only, never the home session store. openRoot and openRootForWrite go back to what main does. Everything that fixes the reported vulnerability stays: ValidateSessionID in SessionFile, Name containment, the write-name component check, the resume guards, the external ref preflight, and every symlink refusal inside the store. Two tests pin the decision in the other direction so it is not re-added by the next review round. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2JMBPGYQBDGMVPKP4N3Q0C5
Entire-Checkpoint: 01M2P2CE7CZJHX19NGX6HMHQNG
Decode the cell snake_case schema while preserving CLI JSON keys. Follow opaque cursors for finding pages, summaries, lookups, and snapshots without overriding their filters with CLI defaults. Entire-Checkpoint: 01M2P3G14M44M5A5FDS9ZY93SS
Decode snake_case stream fields and payloads, recognize review.started, and preserve dotted event names. Cover the documented public events, raw JSON envelopes, and reconnect cursors on both cell route forms. Entire-Checkpoint: 01M2P45TD5Z6B66322N98392D7
Use discussion routes, envelopes and review linkage in the cell client and CLI JSON. Update help and output while preserving command verbs, resource IDs, user-authored text and unrelated output fields. Keep raw watch events in the public cell form through reconnects and remove obsolete thread event rendering. Entire-Checkpoint: 01M2P6JYP8KPGE95JEEB3XVP7S
Entire-Checkpoint: 01M2PA4G6BX7ABH4Q5PXP9KMG6
Entire-Checkpoint: 01M2PCMGZ36EHNNVJVYS4SF1GV
`project grant add` and `repo grant add` now take the grantee as an optional argument. Omitted on a terminal, they offer the members of the owning org who do not hold the target yet, then collect a role per grantee — so one run can add a reader and an admin together. `--role` given fixes every row, rendered as a huh note so the pairing is shown but not editable. The pool is the owning org's membership minus whoever already holds the target, and both halves matter. Org membership grants no data access: the authz schema gives an org member project `view` but neither `read` nor `write`, and entiredb's TestPermissionCascade pins that an org owner can manage a project yet neither push nor pull its repos. So every org member is a real candidate until they hold a grant. Project access DOES reach the project's repos, which is why the repo pool also subtracts the `project:<name>` rows ListRepoGrants returns beside the direct ones — offering access someone already holds through the project would grant a no-op. `org grant add` gets no picker and keeps its two required arguments: everyone eligible is by definition absent from the only list there is. There is no user search or global account listing anywhere in the API, so the subtraction is client-side. Org membership is also the only pool whose entries are directly grantable — project and repo grant rows carry a grantee ULID and no provider identity, with no reverse lookup, while a Membership carries the provider:handle resolveGranteeProvider already takes. --role stops being a cobra-required flag, because cobra enforces those before RunE and a role that cannot reach RunE cannot be prompted for. The guarantee it provided — an omitted role never reaching validation, a lookup, or the API — moves into the RunE, which settles both non-interactive refusals from the command line before any request: an unanswerable prompt must not cost a lookup. Four no-candidate conditions get four messages, since "every member already has access" is a claim about members that must not be printed when there are none, and an account-owned project is the absence of a pool rather than an empty one. Each still names the explicit provider:handle form, because a grantee never has to be an org member. No candidate is auto-picked even when only one is eligible, unlike selectPlacement, which returns a lone cluster without prompting: this writes access. A failure part-way through a batch stops and reports what landed, in --json as well as text, since those grants are real and cannot be undone here. Verified against a throwaway org rather than a production one: all four refusals, and the pool tracking a project grant being added and revoked. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2N9T1MNH9Z7FQQGHPMCM3G0
huh writes a form to stderr in its TUI mode but to stdout in accessible mode, and `grant add` can be asked for --json, so under ACCESSIBLE the prompts landed in the middle of the JSON a caller was parsing. The pickers now build their forms through promptForm, which pins output to the command's stderr — where a prompt belongs whenever stdout carries data, and what the TUI mode already did. TestPickRoles_PromptsStayOffStdout fails if a form is ever built without it, checking both the process's stdout and the command's. The form tests get simpler as a result: they capture the command's stderr buffer instead of swapping os.Stdout, leaving stdin as the only global swap. Verified live in accessible mode: selecting a grantee and taking a role emits only clean JSON on stdout, with every prompt on stderr. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2NAGZ2CGRE7WKCPZTEQJ5QC
`remove` now takes the grantee optionally on all three targets, offering who holds the target now. Unlike `add` this includes org: everyone eligible to be added to an org is by definition absent from the only list there is, but the members to remove ARE that list, so the asymmetry belongs to add rather than to the org. The pool is filtered to rows revoking would actually remove, both filters for a condition a real listing carries. The `owner` row is the owning org, which holds the target through the authz schema rather than a grant, so there is nothing to revoke and the typed route could not address it. A repo listing also carries the project's grants as `project:<name>` rows; revoking one of those at the repo level answers "no such grant; nothing to revoke", verified against a live repo before writing the filter, so offering it would be offering a no-op. Removing that access means removing the project grant. Project and repo rows are revoked by account ULID through the typed-id route, which needs no handle lookup and cannot be defeated by a handle renamed since the listing. So grantCandidate now carries a `ref` to act on and a `label` to show, and the confirmation names the label: picking github:alice off a list and being told an opaque id was revoked is not an answer to what was asked. A ULID the user typed keeps its existing "account <id>" wording. Org membership has no typed-id route, so its rows are addressed by handle, and a member whose handle the server did not resolve is left out rather than offered and then refused. Picked and typed grantees share one revoke path, so the routing cannot drift between them. The non-interactive refusal is settled from the command line before any request, and names the forms that target accepts — only project and repo take a ULID. Verified live against the fixture org: the repo pool offering only the direct grant while excluding the inherited holder and the owner row, the org pool listing all five members, and a revoke reporting the handle it showed. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2NEHJAXNTW97TW6EDWHH06D
`grant remove` now asks before revoking. The prompt comes after the picker, so it names what was actually chosen, and it is one prompt for the whole set: a single grantee reads as a sentence, several are counted in the title and listed under it. A prompt that summarises a set as a bare number is not a confirmation of anything. It asks only where there is a terminal, and there is no flag to bypass it. This is deliberately not what `delete` does — that refuses without --force rather than acting unprompted — and the difference is blast radius: a deleted repo is gone, while a revoked grant is one command from being restored. So a script that has always revoked without being prompted keeps working, and no --force has to exist to let it. The prompt is for the hand on the keyboard, above all for a set just picked off a list, and that hand is by definition at a terminal. The gate is shared rather than cloned: confirmDestructiveAction holds the logic and a destructiveAction supplies the three words that differ, with confirmControlPlaneDeletion reduced to its delete-worded wrapper. The two verbs therefore cannot drift, and a third would be a value rather than a copy. revokeConfirmed joins removePicker as a seam, since forcing a terminal in tests puts the confirmation in play too. Both halves of the rule are covered: declining revokes nothing and exits cleanly, and a non-terminal run is never asked a question it cannot answer. Verified live: a scripted remove still revoking unprompted, the prompt rendering after a pick, "y" revoking, and anything else leaving the grant in place. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
`entire repo grant add /et/pgrant/rgrant` refused with "every member of the org owning repo ... already has access to it" on a repo whose org showed three members, none of which could be picked — and exited 1 while doing it. **The pool rule.** It subtracted everyone holding the target, and a repo inherits its project's grants, so a project covering the org emptied it. The rule is one step narrower: subtract members with a DIRECT grant, keep the ones who hold it only through the project. A direct holder has nothing to add here (changing their role is the typed form's job, `grant add` being an upsert — verified live, where re-granting a writer as reader moves them to reader). An inherited holder has no grant on THIS target, so granting one pins the role on this repo instead of following the project's, which is a real action. `directHolders` is the single place `source` is read for this. **Exit codes.** Having nobody to add is not a failure. Nothing went wrong, nothing is left for the user to fix, and in the common case the state they wanted already holds — the same reasoning that already makes revoking an already-revoked grant a success rather than a 404. The three empty-pool cases report and exit 0, as does confirming an empty selection, which used to exit 1 with no message at all. What stays an error is a command that cannot run as asked: an account-owned target, which has no membership list anywhere, and a non-interactive run with no grantee. **Messages.** Those two errors spell out the provider:handle form because the user is stuck without it. The empty-pool messages state the fact and stop: there is nobody left to add, so a worked example with an invented handle would answer a question nobody asked. Under --json the reason moves to stderr and stdout gets the empty array, so a caller parsing stdout is never handed a sentence. Candidates are plain handles and every role row defaults to the least-privileged role. An intermediate version labelled inherited rows with the role and project they came from and defaulted their row to it; both are detail about a grant the user is not editing, in a list whose question is only "who". Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
PR review found four things, all real. **Nobody selected crashed the add picker.** Confirming an empty multi-select fell through to the role screen, and `huh` indexes a group's first field unguarded, so a group with no fields panics rather than merely looking odd — verified, not inferred. The remove side had the same shape and asked "Revoke 0 grants on …?" before revoking nothing. Both now stop where the selection is made. My tests missed both because they stub the picker seam, so the real `runGrantPicker` never ran; the new ones go through it. **A grantee is a provider-qualified handle and nothing else.** Account ULIDs are internal ids the interface should not ask anyone to copy, so `remove` no longer takes one and its help no longer offers one. `ensureGranteeIsHandle` is the single check, run before the target is resolved so a grantee that cannot work costs no lookup, and shared with `resolveGranteeProvider` so there is one message. The typed-id revoke route stays, reached only by the picker, which reads the id off a listing: candidates now carry `byID` instead of the route being sniffed from the ref's shape. That sniffing was also latently wrong — a non-ULID grantee id would have taken the handle path — and it is what let a pasted ULID reach the route at all. The "account <id>" wording goes with it, since every label is now a handle. **`add --json` is always an array.** It was an object or an array depending on how many grants landed, so a caller had to inspect the result to learn which shape the run had chosen. One entry per grant, including none. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Always emitting an array made `grant add` the only mutation in the CLI answering `--json` with one. `org create`, `project create` and every other single mutation emit the bare wire object, and so did `grant add` itself through runCoreMutation before the picker existed, so a script parsing `.status` broke silently for no gain. The shape now follows the INVOCATION rather than the outcome. A grantee named on the command line is one mutation and emits the object. The picker grants a set and emits an array, one entry per grant that landed including none, so a caller reading that form never has to branch. Keying on the outcome is the version worth naming as wrong, since it is the tempting middle: an array only once more than one grant landed makes a picker run that granted one person indistinguishable from a typed one, so the shape depends on what the user happened to click. Found by Entire Gates on the PR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four review fixes against the entire-api RFD-026 branch (ENT-2466). trail watch dropped thread.created/message_added/message_edited instead of following their rename. entire-api snake-cases each event segment and maps the thread domain to discussion on read, so those rows now arrive as discussion.*; without the cases they fall through to the generic default line. Two of the three never matched before either: stored rows spell them thread.messageAdded, so this is the first release where they can work. trail_review_json.go leaked discussion_id/discussion_message_count into CLI output, where every sibling key is camelCase. The presenter exists to keep the CLI's JSON independent of the cell schema, so these become discussionId/discussionMessageCount. The camelCase request test was renamed to assert snake_case but lost its negative guard; restore it in the new direction. CheckResponse now carries the RFC 9457 request_id through to HTTPError and falls back to title when a problem body has no detail, so an API failure names the id support needs instead of echoing raw JSON. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M2QD7EXBTJ9N2N0QHN99MAMX
An unqualified grantee was answered with `account "asdasdasd" must be a qualified handle like "github:alice" (or a ULID)` — pointing at a form the command had just stopped accepting, and calling the grantee an account. That message belongs to parseQualifiedHandle, which is shared with `project create --owner`, where a ULID genuinely is accepted and the sentence is true. So ensureGranteeIsHandle borrows the split RULE and not the sentence: a grantee that is not provider-qualified now gets a grantee-worded error naming the one form it takes, and the owner ref keeps its own. Both halves are pinned, including that no grantee error may end up offering the ULID form again. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
# Conflicts: # docs/development/cli-conventions.md
Antigravity's two truncated-step warnings logged through context.Background(). logging.log resolves its logger from the context and falls back to slog.Default() when there is none, and nothing outside tests calls slog.SetDefault — so those warnings went to the stdlib text handler on stderr, not to .entire/logs/entire.log where the integration says they are recorded. ExtractModifiedFilesFromOffset runs inside prepare-commit-msg and post-commit condensation, whose stderr the user sees, so a transcript with truncated replace_file_content steps (measured at roughly 0.8% of such calls) printed unstructured `level=WARN msg=...` lines into `git commit` output, without session_id. The method had no context to log through, so the interface gains one. The other ten implementations ignore it; only the call sites, which all had a context in scope, and the antigravity implementation change behaviour. Mechanical apart from those two log calls, and separable from the rest of this branch if it is better carried by the prompt-resolution follow-up. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M396KXK11H1DRDMZ9R131KPR
The import guard built its per-package allowlist with `append(allowedPrefixes, scopedPrefixes[pkgName]...)` inside the loop. That is safe only because allowedPrefixes is a composite literal whose cap equals its len, forcing a copy. Build it any other way — make() with spare capacity, or one more appended entry — and append writes the antigravity-scoped internal/flock entry into its backing array, handing every package checked afterwards the exception scopedPrefixes exists to contain, with no test failure to signal it. slices.Clone makes that independent of how the slice is built, and hoisting it out of the inner loop drops a re-allocation per import. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M396KYVETYG282B0SZZZ0ZSV
Answer repo access with one verb, `repo grant`
…s9oyo Add --nearest flag to measure latency and auto-select closest cluster
stderrURLRe ended the match on a quote as well as whitespace, so that git's own `to 'https://…'` quoting read cleanly. A password containing a quote then split the match inside the credential: in error: cannot fetch https://user:pa'ss@github.com/o/r now out error: cannot fetch <unparseable>'ss@github.com/o/r now leaving the tail of the password in text that reaches stderr, logs and pasted transcripts — the one thing this redaction exists to prevent. Only whitespace ends the match now; a trailing quote swept into the path is the harmless side of that trade. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39ADA184XW2WXHQB3B7PPT2
main removed `repo access list` in favour of `repo grant list` and added
`repo clone --nearest`, so the conflicts are mostly this branch's rename
meeting those two changes:
- cluster_list, the --json ledger and the deprecated-spelling ban take
main's removal of `access list` alongside this branch's rename and
its two new banned spellings.
- repo clone keeps main's --nearest/--cluster refusal and latency
picker, called through the resolver signature this branch trimmed.
- repo_mirror_ref_test drops the access-list test main deleted with the
command it covered.
- the conventions doc keeps this branch's rewritten placement
paragraph, minus the `access list` sentence.
main's new code also names the deleted verbs in four comments
(placement_latency, repo_clone, repo_grant, the doc); those now name the
commands that exist.
The e2e lifecycle test crossed the maintidx threshold with this branch's
three `remote add` phases on top of main's additions, so those phases
move to remoteAddPhases.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Four conflicts, all from main reworking `repo grant` under this branch's picker. Both sides kept everywhere; nothing was dropped. grantTarget: main added unwritableRef and listBranch alongside this branch's candidates/holders/ownerNotOrg, so the struct takes all of them and main's grantListBranch type comes with it. Both `add` and `remove` leaves: main set Args back to ExactArgs(2), which is the pre-picker shape, and added PreRunE: refuseUnwritableRef(t). Kept the picker's optional grantee (RangeArgs, and the variable Args on add) with main's PreRunE — refuseUnwritableRef reads args[0], which RangeArgs(1, 2) still guarantees. A /gh/ mirror ref is now refused before the picker opens, which is the right order: the answer is about the repo the user named. repoGrantTarget: same field list on both sides; leastRole joins listBranch and unwritableRef. repo_clone.go: main added withLatencyProbe and probeableHosts in the block this branch emptied by moving placementPromptTerminal into the shared promptTerminal. Kept main's two helpers and dropped the re-introduced pair — selectPlacement auto-merged onto runPromptForm and has no other caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39C17YX1WNYTB4D22TSRF37
revokeConfirmed returned (false, nil) when the command context was already
cancelled, and the caller exits 0 on that pair — so a signal landing just
before the confirmation revoked nothing, said nothing, and reported success.
The same silent no-op the confirmation gate was reworked to avoid.
It now returns an error wrapping ctx.Err(), which is what the rest of the CLI
does: plugin_confirm.go checks either side of its form and login.go goes out
of its way to record the signal before returning context.Canceled, because
main.go matches on it to re-raise the signal — that is where Ctrl+C's quiet
130 and a broken enclosing shell loop come from. confirmControlPlaneDeletion's
nilerr skip is the outlier this had copied, and still carries the bug for
`delete`.
Checked on both sides of the form, and the far-side check comes before the
form error is inspected: handleFormCancellation treats context.Canceled as a
clean abort and would otherwise report a signal as an answer. An abort from
inside the form stays an answer — Esc or Ctrl+C at the prompt is the user
saying no.
The caller's `if err != nil || !proceed { return err }` is split in two. The
compressed form returned a known-nil err to mean success, which is what hid
this; runControlPlaneDelete, whose gate this copied, already kept the branches
apart.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Entire-Checkpoint: 01M39DF985VCE97GT55ERBWZCM
Interactive grantee pickers for grant add and grant remove
main moved the placement picker's terminal plumbing into a shared runPromptForm (from the grant-picker work), deleting placementPromptTerminal and openPlacementPromptTerminal. Taking that side leaves this branch's picker-routing test driving a seam that no longer exists, so it moves to openPromptTerminal — that test is the only coverage of the routing, which is why it was ported here in the first place. repo_remote_url_test.go stays deleted; main keeps editing the tests of a command this branch removes. main's new text also uses `repo remote url` as the motivating example for "stdout is captured by design, so prompts go to the terminal", in utils.go and in the conventions doc. The rule outlives the command, so both now name a --json read piped into a parser instead. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
repo remote: one `add <remote-name>`, and a primary default for scripts
…06ac test: characterize Git CLI behavior before go-git migration
Remove Gemini CLI support
Entire-Checkpoint: 01M39JB34RP7Q4EFVK1ADWYVBD
entireio#2548 removed `entire repo remote url`, but a native-mirror lifecycle phase still called it, so the control-plane suite failed on main. The primary-default half was already covered by the `remote add` phase; the unknown-cluster refusal moves there, run inside the clone. The suite only ran on pushes to main, which is how this slipped through. It now also runs on same-repo PRs to main (forks get no secrets). Slack still notifies only for main. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39N8CRAJM2WR38XJ0K4M862
Entire-Checkpoint: 01M39NPY3D7VYE65CZAJW5X33S
…-split coreapi: land vendored Core spec refresh before entireio#2561
Dependabot PRs come from same-repo branches, so the fork guard let them through, but their runs get only Dependabot secrets and would fail at login while holding the shared account's queue. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39PGZS1S9FK09YTYYNN84GV
e2e controlplane: drop removed `repo remote url`, run suite on PRs
…ion-2360' into fix-checkpoint-push-remote-election-2360 # Conflicts: # cmd/entire/cli/strategy/checkpoint_sync_remote.go
main removed Gemini CLI support while this branch was adding Antigravity, so every conflict was the same collision: both sides edit the lists of supported agents. Resolved by taking main's list and restoring Antigravity to it, keeping main's other additions (Codex, where it had been added alongside). Substantive resolutions: - generateSummary: kept this branch's sliceByAgentMetric delegation and took main's geminilegacy call inside the fallback switch. The branch's inner Gemini case still named the geminicli package, which main deleted, so it could not have compiled either way. - agent/geminicli: accepted main's deletion of the package. This branch had only touched it to thread ctx through ExtractModifiedFilesFromOffset. - e2e/testutil/repo.go: kept RepoPreparer/RepoCleaner, dropped the gemini-cli branch — setupGeminiTestHome no longer exists. - antigravity_test.go: the "wrong agent" sentinel named AgentNameGemini, a constant main deleted. Now AgentNameClaudeCode. This one is a semantic conflict git resolved cleanly and only vet caught. Verified on the merged tree: go build, go vet (including GOOS=windows), gofmt, mise run lint (0 issues) and mise run test:ci, all green. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39RV7DGXVXPKTJRE1KGKEZ8
Legacy records normalize to IDLE on list and isOrphanedSessionState never deletes IDLE, so they now remain until StaleSessionThreshold expiry. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39S21QM1G5K5G7EGNG5AKAZ
Both comments described the secret as shared with the gemini-cli E2E leg. That leg no longer exists — main removed Gemini CLI support — so the only consumer is agy's API-key mode, and "shared" now points a reader at an agent that is not there. Comment-only; the secret, the workflow and the fallback behaviour are unchanged. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39S2GZKTWB0KJFM3TQ86THA
…-remote-election-2360
isOrphanedSessionState re-spelled "ended" as a phase check, so an IDLE state with EndedAt stamped was kept for 7 days (the finalizer skips it via IsEnded). Use the canonical predicate and drop test leftovers from the owner-liveness version. Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39SKJBAWGZCRTKYPYZ8ZHP2
Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com> Entire-Checkpoint: 01M39SWN2NJNA43TF5Z9P5457V
…-remote-election-2360
feat(agent): add Antigravity (agy) CLI agent
…urvive-hooks Hooks stop deleting live idle sessions
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
https://entire.io/gh/MuskanPaliwal/cli/trails/4
This draft pull request was opened by Entire after CI was requested for the linked trail. Feel free to edit the title or body — the link above is what keeps the trail and PR connected.