| 1 | package control |
| 2 | |
| 3 | import ( |
| 4 | "context" |
| 5 | "strings" |
| 6 | ) |
| 7 | |
| 8 | const ( |
| 9 | // FinalReadinessRecoveryAction is the typed transport action used by HTTP |
| 10 | // and ACP. It is additive and optional, so older clients remain compatible. |
| 11 | FinalReadinessRecoveryAction = "final_readiness_recovery" |
| 12 | ContinueChecksCommand = "/continue-checks" |
| 13 | defaultContinueChecksPrompt = "Continue checking the work. Preserve completed changes, run relevant checks, and report the observed results and anything you could not verify." |
| 14 | ) |
| 15 | |
| 16 | // ParseFinalReadinessRecoveryCommand converts the explicit slash action into a |
| 17 | // model prompt. Optional trailing text is user guidance; the command token |
| 18 | // itself never reaches the provider. |
| 19 | func ParseFinalReadinessRecoveryCommand(input string) (prompt string, ok bool) { |
| 20 | trimmed := strings.TrimSpace(input) |
| 21 | if trimmed != ContinueChecksCommand && !strings.HasPrefix(trimmed, ContinueChecksCommand+" ") { |
| 22 | return "", false |
| 23 | } |
| 24 | prompt = strings.TrimSpace(strings.TrimPrefix(trimmed, ContinueChecksCommand)) |
| 25 | if prompt == "" { |
| 26 | prompt = defaultContinueChecksPrompt |
| 27 | } |
| 28 | return prompt, true |
| 29 | } |
| 30 | |
| 31 | // RunFinalReadinessRecovery is the synchronous transport-neutral recovery path. |
| 32 | func (c *Controller) RunFinalReadinessRecovery(ctx context.Context, input string) error { |
| 33 | return c.RunFinalReadinessRecoveryWithAdmission(ctx, input, nil) |
| 34 | } |
| 35 | |
| 36 | // RunFinalReadinessRecoveryWithAdmission is a retired compatibility action. Old |
| 37 | // history stays readable, but no checkpoint can authorize or replay work. |
| 38 | func (c *Controller) RunFinalReadinessRecoveryWithAdmission(ctx context.Context, input string, onAdmitted func()) error { |
| 39 | return ErrNoFinalReadinessRecovery |
| 40 | } |
| 41 | |
| 42 | // SubmitFinalReadinessRecovery retains the asynchronous symbol for old clients |
| 43 | // and emits the stable retirement error through the ordinary turn path. |
| 44 | func (c *Controller) SubmitFinalReadinessRecovery(display, input string) { |
| 45 | c.submissions.mu.Lock() |
| 46 | defer c.releaseSubmissionAdmission() |
| 47 | c.submitFinalReadinessRecoveryLocked(display, input, turnAdmission{}) |
| 48 | } |
| 49 | |
| 50 | func (c *Controller) submitFinalReadinessRecoveryLocked(display, input string, admission turnAdmission) { |
| 51 | c.runGuardedWithAdmission(func(ctx context.Context) error { |
| 52 | return ErrNoFinalReadinessRecovery |
| 53 | }, admission) |
| 54 | } |
| 55 | |
| 56 | // SubmitDeliveryRecovery preserves the v1.25 desktop/API symbol. |
| 57 | func (c *Controller) SubmitDeliveryRecovery(display, input string) { |
| 58 | c.SubmitFinalReadinessRecovery(display, input) |
| 59 | } |
| 60 | |
| 61 | func (c *Controller) submitFinalReadinessCommand(trimmed, display string, admission turnAdmission) bool { |
| 62 | prompt, ok := ParseFinalReadinessRecoveryCommand(trimmed) |
| 63 | if !ok { |
| 64 | return false |
| 65 | } |
| 66 | if strings.TrimSpace(display) == "" { |
| 67 | display = trimmed |
| 68 | } |
| 69 | c.submitFinalReadinessRecoveryLocked(display, prompt, admission) |
| 70 | return true |
| 71 | } |
| 72 |