Make prompt capture fast and resilient

Hop-State: A_06FN8D0CK06GY5XBDYZ632R
Hop-Proposal: R_06FN8CY2FJBV5QA0WFEDWHR
Hop-Task: T_06FN8C4PMFJRT8APV4G03Y8
Hop-Attempt: AT_06FN8C4PMFRJEE4B7KKQ740
This commit is contained in:
cyph3rasi
2026-07-11 19:42:05 -07:00
committed by Hop
parent ff62e89596
commit 4364e1d8a4
4 changed files with 54 additions and 15 deletions
@@ -0,0 +1,17 @@
{
"id": "P_06FN8C4PMD47GH10ZFQY2VR",
"task_id": "T_06FN8C4PMFJRT8APV4G03Y8",
"attempt_id": "AT_06FN8C4PMFRJEE4B7KKQ740",
"state_id": "P_06FN8C4PMD47GH10ZFQY2VR",
"prompt": "\"I couldnt safely start the repository change because Hop prompt capture timed out. Per the repository workflow, I stopped before inspecting or modifying files. No changes were made.\" what the fuck is this bullshit",
"agent_name": "codex",
"status": "proposed",
"response_summary": "Make prompt capture fast and resilient",
"created_at": "2026-07-12T02:38:18.275042Z",
"metadata": {
"source_tree": "7f3f889e96a317c157eedf4f5285af835865f6fb",
"git_commit": "ff62e89596a85ff3073ea348919b8d2b7125069f",
"attempt_head": "C_06FN8CQ0ZNZ60MRZ818MB9G",
"attempt_head_kind": "checkpoint"
}
}
+21
View File
@@ -655,6 +655,27 @@ func (r *Repository) ReadHiddenRef(ctx context.Context, name string) (oid string
return r.ReadRef(ctx, ref) return r.ReadRef(ctx, ref)
} }
// ListHiddenRefs reads all refs below refs/hop in one Git process. Keys are
// relative to refs/hop/ (for example, "states/P_..." or "accepted").
func (r *Repository) ListHiddenRefs(ctx context.Context) (map[string]string, error) {
output, err := r.run(ctx, nil, nil, "for-each-ref", "--format=%(refname) %(objectname)", hiddenRefPrefix)
if err != nil {
return nil, err
}
refs := make(map[string]string)
for _, line := range strings.Split(strings.TrimSpace(output), "\n") {
if line == "" {
continue
}
name, oid, found := strings.Cut(line, " ")
if !found || !strings.HasPrefix(name, hiddenRefPrefix) {
return nil, fmt.Errorf("unexpected hidden ref listing %q", line)
}
refs[strings.TrimPrefix(name, hiddenRefPrefix)] = strings.TrimSpace(oid)
}
return refs, nil
}
// UpdateHiddenRef atomically updates refs/hop/name. With no expectedOld value, // UpdateHiddenRef atomically updates refs/hop/name. With no expectedOld value,
// it is unconditional. Passing expectedOld performs compare-and-swap; an empty // it is unconditional. Passing expectedOld performs compare-and-swap; an empty
// expected value means the ref must not exist. // expected value means the ref must not exist.
+9 -13
View File
@@ -1640,17 +1640,17 @@ func (s *Service) reconcileDerivedRefs(ctx context.Context, reconcileAccepted bo
if err != nil { if err != nil {
return err return err
} }
for _, row := range rows { refs, err := s.Repo.ListHiddenRefs(ctx)
state := row.State
if err := s.Repo.VerifyObjects(ctx, state.GitCommit, state.SourceTree); err != nil {
return fmt.Errorf("state %s references unavailable Git data: %w", state.ID, err)
}
name := "states/" + state.ID
commit, exists, err := s.Repo.ReadHiddenRef(ctx, name)
if err != nil { if err != nil {
return err return err
} }
if !exists || commit != state.GitCommit { for _, row := range rows {
state := row.State
name := "states/" + state.ID
if refs[name] != state.GitCommit {
if err := s.Repo.VerifyObjects(ctx, state.GitCommit, state.SourceTree); err != nil {
return fmt.Errorf("state %s references unavailable Git data: %w", state.ID, err)
}
if err := s.Repo.UpdateHiddenRef(ctx, name, state.GitCommit); err != nil { if err := s.Repo.UpdateHiddenRef(ctx, name, state.GitCommit); err != nil {
return fmt.Errorf("repair Git pin for state %s: %w", state.ID, err) return fmt.Errorf("repair Git pin for state %s: %w", state.ID, err)
} }
@@ -1668,11 +1668,7 @@ func (s *Service) reconcileDerivedRefs(ctx context.Context, reconcileAccepted bo
if err != nil { if err != nil {
return err return err
} }
commit, exists, err := s.Repo.ReadHiddenRef(ctx, acceptedRef) if refs[acceptedRef] != head.GitCommit {
if err != nil {
return err
}
if !exists || commit != head.GitCommit {
if err := s.Repo.UpdateHiddenRef(ctx, acceptedRef, head.GitCommit); err != nil { if err := s.Repo.UpdateHiddenRef(ctx, acceptedRef, head.GitCommit); err != nil {
return fmt.Errorf("repair accepted Git ref: %w", err) return fmt.Errorf("repair accepted Git ref: %w", err)
} }
+7 -2
View File
@@ -58,8 +58,13 @@ uses it as the default session and identifies the runtime as `codex` unless
headers, and credential-bearing connection strings before persistence. headers, and credential-bearing connection strings before persistence.
Read the returned `HOP_STATE_ID`, `HOP_TASK_ID`, `HOP_ATTEMPT_ID`, and workspace. Read the returned `HOP_STATE_ID`, `HOP_TASK_ID`, `HOP_ATTEMPT_ID`, and workspace.
If capture fails or `hop` is unavailable, stop without project effects and Allow at least 120 seconds for capture. If it times out or fails transiently,
report the error. retry once with the same message, agent, and session; Hop session binding makes
the retry idempotent for the task. If the retry fails, inspect Hop's error and
locks, run `hop doctor`, and repair a safe local operational problem when
possible. Stop without project effects only when Hop remains unavailable or
unsafe after recovery; do not abandon the user's request after the first
timeout.
If Hop reports redactions, never repeat the credential in output, summaries, If Hop reports redactions, never repeat the credential in output, summaries,
commands recorded as evidence, or proposal text. Refer to its environment commands recorded as evidence, or proposal text. Refer to its environment