fix: improve recovery failure diagnostics (#3869)
This commit is contained in:
@@ -0,0 +1,170 @@
|
||||
---
|
||||
created: 2026-09-22
|
||||
status: complete
|
||||
requirements:
|
||||
- REQ-WORKSPACES-WORKTREE-BASE-REFRESH-001
|
||||
- REQ-INTEGRATIONS-GITHUB-PR-DISCOVERY-001
|
||||
- REQ-PLATFORM-DIAGNOSTIC-LOGGING-001
|
||||
system_design:
|
||||
- ../../specs/workspaces/system-design/worktree-base-refresh.md
|
||||
- ../../specs/integrations/system-design/github-pr-discovery-health.md
|
||||
- ../../specs/platform/system-design/diagnostic-logging-01.md
|
||||
legacy_specs: []
|
||||
---
|
||||
|
||||
# Implementation plan: Recovery diagnostic fixes
|
||||
|
||||
## Overview
|
||||
|
||||
Improve evidence for failed Git refresh and PR discovery. Remove warnings for
|
||||
intentional setup-script omissions. Execute three work orders sequentially.
|
||||
Each work order has an independent result. The order prioritizes lost failure
|
||||
evidence before warning cleanup.
|
||||
|
||||
## Evidence and requirement conformance
|
||||
|
||||
The supplied September 22 logs show two failed recovery attempts at 16:12 and
|
||||
16:15 Lisbon time. Both stop at required base refresh with `exit status 128`.
|
||||
Terminal requests report a missing workspace path. Another task later continues
|
||||
with an `origin/master` fallback. The original task reaches setup resolution at
|
||||
16:25, then reports no PR after push at 16:26.
|
||||
|
||||
These logs do not establish the Git cause, installed version, local-ref state,
|
||||
or whether a PR existed. The plan repairs source-confirmed diagnostic defects.
|
||||
It does not claim to repair the user's environment or change refresh policy.
|
||||
|
||||
- `handleBaseFetchFailure` uses output for coarse classification, then discards
|
||||
it. `pullCurrentBranchOrFallback` passes the process error without useful
|
||||
command context. The original logs can match pull failures too.
|
||||
- `detectPushAndAssociatePRWithIdentity` merges errors and empty search results.
|
||||
`searchPRForExistingWatch` also discards provider errors. These are concrete
|
||||
gaps in discovery AC .3, extended with diagnostic AC .5.
|
||||
- `resolvePreparerSetupScript` intentionally skips comment-only scripts but
|
||||
logs WARN. Platform AC .12 makes the intended severity explicit.
|
||||
|
||||
Smallest reproductions use fake Git command output, a scripted PR service,
|
||||
and the existing setup resolver with an observed logger. No live environment
|
||||
or GitHub credentials are needed. Permanent regression tests belong to the
|
||||
implementation turn and must fail before the correction.
|
||||
|
||||
## Scope
|
||||
|
||||
### In scope
|
||||
|
||||
- Credential-safe Git operation, checkout identity, and diagnostic categories.
|
||||
- Distinct provider-error, empty, found, and cancellation outcomes for post-push lookup.
|
||||
- Debug severity for comment-only setup scripts.
|
||||
- Tests for diagnostics and unchanged execution decisions.
|
||||
|
||||
### Out of scope
|
||||
|
||||
- Terminal readiness, frontend reconnect timing, 503 severity, or rate limiting.
|
||||
- Git fallback policy, automatic credentials repair, branch changes, or lock deletion.
|
||||
- Provider retry budgets, discovery health state, schema, APIs, UI, or translations.
|
||||
- Deployment, live-instance changes, persistent subtasks, or delegated implementation.
|
||||
|
||||
## Ownership and technical approach
|
||||
|
||||
Workspaces owns base refresh and its Git diagnostics. Integrations owns PR
|
||||
query outcomes. Platform owns diagnostic severity. Extend those existing
|
||||
requirement/design pairs rather than creating a separate repair specification.
|
||||
|
||||
Task 01 adds diagnostic-only classification to fetch and pull failure paths in
|
||||
`internal/worktree`. Fixed explanations preserve secret exclusion without a new
|
||||
raw-output redaction boundary. Existing reasons retain their policy meaning.
|
||||
Task 02 separates PR lookup results in both orchestrator entry paths and reuses
|
||||
the GitHub category classifier. Task 03 changes one setup omission log level.
|
||||
The paired designs define fields, cancellation, and final-outcome semantics.
|
||||
|
||||
No new ADR is needed. Existing credential exclusion and refresh decisions remain
|
||||
authoritative, including the local-first and required-refresh ADRs linked by the
|
||||
workspace design. No public documentation changes are needed for this change.
|
||||
|
||||
## Tests
|
||||
|
||||
| Acceptance criteria | Regression evidence |
|
||||
| --- | --- |
|
||||
| Workspace .14; preserve .3, .6, .10, .13 | New `internal/worktree/refresh_diagnostics_test.go`: `TestRefreshDiagnosticClassification`, `TestBaseRefreshDiagnosticEmission`, `TestRefreshDiagnosticsPreservePolicy`. Cover fetch/pull, fallback failure, unknown output, secrets, context, and unchanged decisions. |
|
||||
| Discovery .3, .5; preserve .1, .2, .4 | New `internal/orchestrator/event_handlers_github_push_diagnostics_test.go`: `TestPushDiscoveryDiagnostics`, `TestExistingWatchDiscoveryDiagnostics`. Include mixed outcomes, cancellation, found PR, and exact repository identity. |
|
||||
| Platform .12 | Extend `internal/agent/runtime/lifecycle/preparer_script_test.go`: `TestResolvePreparerSetupScriptDiagnosticSeverity`. Assert debug omission and unchanged resolution. Retain actual failure coverage in `env_preparer_setup_script_test.go`. |
|
||||
|
||||
These are backend log contracts. Observer tests through the real service paths
|
||||
provide end-to-end diagnostic evidence. No browser layout or flow changes, so
|
||||
no Playwright work order is necessary. Unknown environmental failures remain
|
||||
unknown even after richer diagnostics.
|
||||
|
||||
## Companion package inventory
|
||||
|
||||
The completed `github-pr-discovery-health` and `github-pr-watch-reconciliation`
|
||||
packages own health UI/admission and watch target stability. Their delivered
|
||||
scope, browser matrices, and historical results stay unchanged. This package
|
||||
adds only post-push log correctness. Existing Git fallback tests remain policy
|
||||
regressions; this package does not reopen the refresh decisions.
|
||||
|
||||
## Work orders
|
||||
|
||||
- [x] [Task 01: Explain Git refresh failures](task-01-git-diagnostics.md)
|
||||
- [x] [Task 02: Distinguish PR lookup outcomes](task-02-pr-diagnostics.md)
|
||||
- [x] [Task 03: Quiet empty setup scripts](task-03-setup-log-severity.md)
|
||||
|
||||
## Verification results
|
||||
|
||||
Implementation complete on September 22, 2026. Design validation passed before
|
||||
implementation:
|
||||
|
||||
- Catalog validation: 299 decisions and 1,108 specifications.
|
||||
- Specification linter tests: 36 passed.
|
||||
- Full specification lint: passed.
|
||||
- Scoped diff whitespace check: passed.
|
||||
- Scoped status inspection: six specification edits and four new plan files.
|
||||
|
||||
Implementation verification passed for every work order, including targeted
|
||||
race checks, and `go build ./...` passed. Targeted `golangci-lint` for all four
|
||||
affected backend packages passed with zero issues. Specification catalog
|
||||
validation and full specification lint also passed. A repository-wide
|
||||
`go test ./... -count=1` run reached the changed packages successfully but exited non-zero on unrelated
|
||||
environment-sensitive failures in process probing, home-config discovery,
|
||||
websocket subscription counting, GitHub fixture setup, launcher restart tests,
|
||||
and executor timing.
|
||||
|
||||
The review remediation then corrected provider deadline handling in PR
|
||||
discovery and the pull fast-forward diagnostic classifier, and added focused
|
||||
regression fixtures. Builds and tests were not rerun for that remediation per
|
||||
the review instruction. The verification results above therefore apply to the
|
||||
pre-remediation implementation state at that time.
|
||||
|
||||
PR fixup then split the PR discovery retry handler to satisfy the backend
|
||||
function-size limit, added the review-requested assertions and comments, and
|
||||
guarded optional secret checks. The focused orchestrator, worktree, and GitHub
|
||||
tests passed, and base-relative `golangci-lint` completed with zero issues. The
|
||||
later CI remediation corrected a stale websocket subscription-count assertion,
|
||||
made tracked execution persistence read status under the execution-store lock,
|
||||
and raised the static-analysis budget from five to ten minutes after CI timed
|
||||
out with zero lint findings. The new lifecycle and websocket regressions pass
|
||||
under `-race`, the full lifecycle and websocket packages pass, `go build ./...`
|
||||
passes, and base-relative `golangci-lint --timeout=10m` reports zero issues.
|
||||
A complete local `go test ./... -count=1` run still exits nonzero on the
|
||||
environment-sensitive process-tree probe, home-config discovery, and launcher
|
||||
restart tests, and one lifecycle package failure under aggregate scheduling;
|
||||
the lifecycle package passes when rerun alone. CI's E2E matrix was green during
|
||||
both post-push runs, so no E2E code change was made.
|
||||
|
||||
Each work order supplies exact Go checks, rooted at the repository root.
|
||||
Final package checks:
|
||||
|
||||
```bash
|
||||
python3 scripts/list-docs.py validate
|
||||
python3 scripts/lint-spec-files.test.py
|
||||
python3 scripts/lint-spec-files.py --all
|
||||
git diff --check -- docs/specs docs/plans/recovery-diagnostics
|
||||
git status --short -- docs/specs docs/plans/recovery-diagnostics
|
||||
```
|
||||
|
||||
## Risks
|
||||
|
||||
- Git text varies by version and locale. Unrecognized output must remain unknown.
|
||||
- Diagnostic codes must not become fallback policy inputs.
|
||||
- Raw Git and provider output can contain credentials. Only fixed summaries leave classifiers.
|
||||
- PR retries can have mixed results. Tests must prove the final outcome reflects the latest attempt.
|
||||
- Package-level logger replacement in tests needs cleanup and serial execution.
|
||||
- The original Git root cause requires the affected version and additional safe evidence.
|
||||
@@ -0,0 +1,144 @@
|
||||
---
|
||||
id: "01-git-diagnostics"
|
||||
title: "Explain Git refresh failures"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-WORKSPACES-WORKTREE-BASE-REFRESH-001
|
||||
acceptance_criteria:
|
||||
- AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.3
|
||||
- AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.6
|
||||
- AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.10
|
||||
- AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.13
|
||||
- AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.14
|
||||
system_design:
|
||||
- ../../specs/workspaces/system-design/worktree-base-refresh.md
|
||||
---
|
||||
|
||||
# Task 01: Explain Git refresh failures
|
||||
|
||||
## Summary
|
||||
|
||||
Add diagnostic evidence without changing refresh admission or fallback. Use
|
||||
fixed categories and explanations instead of raw Git output.
|
||||
|
||||
## In scope
|
||||
|
||||
- Add `refresh_diagnostics.go` with a diagnostic-only classifier and fixed summaries.
|
||||
- Integrate both fetch and pull failure paths, including configured fallback failures.
|
||||
- Record operation, local checkout path, branch, existing reason, diagnostic code,
|
||||
fixed detail, and optional exit code. Do not read or emit remote URLs.
|
||||
- Preserve error identities, progress payloads, return values, and ref selection.
|
||||
- Correct the misleading raw-output comment on `syncFailureCause`.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Other work orders, terminal behavior, UI, schema, retry policy, and live-instance changes.
|
||||
|
||||
## Acceptance
|
||||
|
||||
1. Known failures have useful fixed explanations. Unknown output remains unknown.
|
||||
2. Logs, errors, and progress contain no injected secret or raw command output.
|
||||
3. Required refresh, local fallback, missing-ref fallback, and cancellation retain their decisions.
|
||||
|
||||
## Regression evidence
|
||||
|
||||
In new `refresh_diagnostics_test.go`, add `TestRefreshDiagnosticClassification`,
|
||||
`TestBaseRefreshDiagnosticEmission`, and `TestRefreshDiagnosticsPreservePolicy`.
|
||||
The emission test must fail first because current logs lack the new fields.
|
||||
Use the fake command pattern from `manager_test.go` and the observer pattern
|
||||
from `manager_state_test.go`. Exercise fetch and pull through the manager,
|
||||
not only the classifier. Include SSH public-key and host-key errors, DNS,
|
||||
connection/TLS errors, missing refs, non-fast-forward errors, inaccessible
|
||||
repositories, locks, unknown text, timeout, and cancellation.
|
||||
|
||||
Inject tokens, credential URLs, headers, control characters, and long output.
|
||||
Assert their absence from all emitted fields, returned errors, and progress.
|
||||
Use actual local refs to cover required failure and successful local fallback.
|
||||
Assert no ref mutation or extra Git invocation. Keep the existing missing-ref
|
||||
policy tests authoritative for eligibility.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the regression first and record its expected failure. After implementation,
|
||||
run these commands from the repository root:
|
||||
|
||||
```bash
|
||||
(cd apps/backend && go test ./internal/worktree -count=1)
|
||||
(cd apps/backend && go test -race ./internal/worktree -run 'Test(RefreshDiagnostic|BaseRefreshDiagnostic|RefreshDiagnosticsPreservePolicy)' -count=1)
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/backend/internal/worktree/manager_git.go`
|
||||
- `apps/backend/internal/worktree/refresh_diagnostics.go (new)`
|
||||
- `apps/backend/internal/worktree/refresh_diagnostics_test.go (new)`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None. Execute in the plan order by default.
|
||||
|
||||
## Risks
|
||||
|
||||
Git text is not a stable API. Keep diagnostic classification separate from policy.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- [Requirement](../../specs/workspaces/requirements/worktree-base-refresh.md)
|
||||
- [System design](../../specs/workspaces/system-design/worktree-base-refresh.md)
|
||||
- [Plan evidence and exclusions](plan.md)
|
||||
- Backend AGENTS.md and TDD backend test guidance.
|
||||
|
||||
## Results
|
||||
|
||||
Implemented credential-safe diagnostic classification and emission for base
|
||||
branch fetch and pull failures. The diagnostic fields preserve the existing
|
||||
refresh reason and policy decisions, while fixed explanations and optional
|
||||
exit codes keep raw Git output out of logs, returned errors, and progress.
|
||||
|
||||
The new regression suite covers authentication, SSH key and host failures,
|
||||
DNS, connection, TLS, missing refs, non-fast-forward updates, repository
|
||||
access, lock contention, unknown output, timeout, cancellation, fetch and
|
||||
pull emission, and required-refresh policy preservation. The first emission
|
||||
test failed before the production change because no diagnostic log existed.
|
||||
|
||||
Verification passed:
|
||||
|
||||
```text
|
||||
go test ./internal/worktree -count=1
|
||||
go test -race ./internal/worktree -run 'Test(RefreshDiagnostic|BaseRefreshDiagnostic|RefreshDiagnosticsPreservePolicy)' -count=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Review remediation
|
||||
|
||||
The classifier now recognizes Git's pull fast-forward refusal, uses local
|
||||
branch wording for that outcome, and leaves tag-clobber output unknown. Fetch
|
||||
and pull fixtures cover the real failure forms. Builds and tests were not
|
||||
rerun for this review remediation, so the verification above describes the
|
||||
pre-remediation implementation state at that time.
|
||||
|
||||
## PR fixup verification
|
||||
|
||||
The focused worktree regression passed after the review fixes:
|
||||
|
||||
```text
|
||||
go test ./internal/worktree -run 'Test(RefreshDiagnostic|BaseRefreshDiagnostic|RefreshDiagnosticsPreservePolicy)' -count=1
|
||||
golangci-lint run ./... --new-from-rev='195530e5766abf7f44f08e2d5ad436b22c28d748' --timeout=5m
|
||||
```
|
||||
|
||||
The later CI remediation also passed `go build ./...` and the full
|
||||
`internal/worktree`, `internal/agent/runtime/lifecycle`, and
|
||||
`internal/gateway/websocket` package suites, including race-enabled runs for
|
||||
the two packages affected by CI. Base-relative lint reports zero issues with
|
||||
the ten-minute analyzer budget. A complete local `go test ./... -count=1`
|
||||
run remains nonzero on environment-sensitive process-tree, home-config, and
|
||||
launcher tests, plus one lifecycle package failure under aggregate scheduling;
|
||||
the lifecycle package passed when rerun alone.
|
||||
@@ -0,0 +1,149 @@
|
||||
---
|
||||
id: "02-pr-diagnostics"
|
||||
title: "Distinguish PR lookup outcomes"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-INTEGRATIONS-GITHUB-PR-DISCOVERY-001
|
||||
acceptance_criteria:
|
||||
- AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.1
|
||||
- AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.2
|
||||
- AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.3
|
||||
- AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.4
|
||||
- AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.5
|
||||
system_design:
|
||||
- ../../specs/integrations/system-design/github-pr-discovery-health.md
|
||||
---
|
||||
|
||||
# Task 02: Distinguish PR lookup outcomes
|
||||
|
||||
## Summary
|
||||
|
||||
Separate provider errors from successful empty post-push searches. Apply the
|
||||
same diagnostic contract to existing searching watches.
|
||||
|
||||
## In scope
|
||||
|
||||
- Update `detectPushAndAssociatePRWithIdentity` and `searchPRForExistingWatch`.
|
||||
- Reuse GitHub discovery categories through a narrow exported classifier.
|
||||
- Warn on failed attempts with resolved target identity and safe category.
|
||||
- Keep empty and exhausted summaries at debug. Record counts and latest outcome.
|
||||
- Preserve production delays, context cancellation, association, and admission.
|
||||
- Add a private wait seam only as needed to exercise retries without real sleeps.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Other work orders, terminal behavior, UI, schema, retry policy, and live-instance changes.
|
||||
|
||||
## Acceptance
|
||||
|
||||
1. Provider errors never become successful empty results or expose raw error text.
|
||||
2. Empty results and cancellation produce no provider-failure or exhaustion warning.
|
||||
3. Later success, watch reuse, repository identity, and association behavior remain correct.
|
||||
|
||||
## Regression evidence
|
||||
|
||||
Add `TestPushDiscoveryDiagnostics` and `TestExistingWatchDiscoveryDiagnostics`
|
||||
in new `event_handlers_github_push_diagnostics_test.go`. Reuse service seeding
|
||||
and mocks from `event_handlers_github_test.go` and multi-branch association tests.
|
||||
First prove a provider error is currently absent from warnings and that all-empty
|
||||
retries currently end with WARN. Use an observed logger through the real caller.
|
||||
|
||||
Cover all-empty, all-error, error then empty, empty then error, error then found,
|
||||
and cancellation before lookup and during a delay. Include an existing searching
|
||||
watch, a numbered watch, empty legacy repository name, and a secondary repository.
|
||||
Assert exact provider calls, wait schedule, no duplicate associations, no health
|
||||
mutation, and no raw credential-containing errors in logs. A successful lookup
|
||||
must continue through existing association persistence. Extend classifier tests
|
||||
in `service_pr_discovery_health_test.go` only if the exported seam needs coverage.
|
||||
Do not duplicate the category rules in the orchestrator.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the regression first and record its expected failure. After implementation,
|
||||
run these commands from the repository root:
|
||||
|
||||
```bash
|
||||
(cd apps/backend && go test ./internal/orchestrator -run 'Test(PushDiscoveryDiagnostics|ExistingWatchDiscoveryDiagnostics|DetectPushAndAssociatePR|GitHubPushAssociation|PushAssociation|ResolvePRWatchBranchForWatch)' -count=1)
|
||||
(cd apps/backend && go test -race ./internal/orchestrator -run 'Test(PushDiscoveryDiagnostics|ExistingWatchDiscoveryDiagnostics)' -count=1)
|
||||
(cd apps/backend && go test ./internal/github -run 'Test(ClassifyPRDiscoveryError|PRDiscovery)' -count=1)
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/backend/internal/orchestrator/event_handlers_github.go`
|
||||
- `apps/backend/internal/orchestrator/event_handlers_github_push_diagnostics_test.go (new)`
|
||||
- `apps/backend/internal/orchestrator/event_handlers_github_test.go (mock seam if needed)`
|
||||
- `apps/backend/internal/github/service_pr_discovery_health.go`
|
||||
- `apps/backend/internal/github/service_pr_discovery_health_test.go`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None. Execute in the plan order by default.
|
||||
|
||||
## Risks
|
||||
|
||||
Error and empty results can alternate. Historical errors must not override later successful outcomes.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- [Requirement](../../specs/integrations/requirements/github-pr-discovery-health.md)
|
||||
- [System design](../../specs/integrations/system-design/github-pr-discovery-health.md)
|
||||
- [Plan evidence and exclusions](plan.md)
|
||||
- Backend AGENTS.md and TDD backend test guidance.
|
||||
|
||||
## Results
|
||||
|
||||
Implemented a bounded exported GitHub discovery classifier and separated
|
||||
successful empty lookups, provider failures, found PRs, and cancellation in
|
||||
post-push and existing-watch paths. Failed provider attempts now log only the
|
||||
workspace, task, repository identity, branch, attempt, and safe category.
|
||||
Empty attempts and retry exhaustion are debug logs with outcome counts. The
|
||||
existing association and watch reuse paths remain unchanged after a found PR.
|
||||
|
||||
The first regression run failed at compile time because the retry wait seam was
|
||||
not yet present. After implementation, verification passed:
|
||||
|
||||
```text
|
||||
go test ./internal/orchestrator -run 'Test(PushDiscoveryDiagnostics|ExistingWatchDiscoveryDiagnostics|DetectPushAndAssociatePR|GitHubPushAssociation|PushAssociation|ResolvePRWatchBranchForWatch)' -count=1
|
||||
go test -race ./internal/orchestrator -run 'Test(PushDiscoveryDiagnostics|ExistingWatchDiscoveryDiagnostics)' -count=1
|
||||
go test ./internal/github -run 'Test(ClassifyPRDiscoveryError|PRDiscovery)' -count=1
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Review remediation
|
||||
|
||||
PR discovery now uses the caller context state to decide whether to stop. A
|
||||
live caller treats wrapped provider deadlines and cancellations as failed
|
||||
attempts, so post-push retries and existing-watch diagnostics remain active.
|
||||
Focused coverage covers wrapped provider deadlines in both paths and retains
|
||||
actual caller-cancellation coverage. Builds and tests were not rerun for this
|
||||
review remediation, so the verification above describes the pre-remediation
|
||||
implementation state at that time.
|
||||
|
||||
## PR fixup verification
|
||||
|
||||
The focused orchestrator and GitHub regression commands passed after the review
|
||||
fixes, and the base-relative backend lint completed with zero issues:
|
||||
|
||||
```text
|
||||
go test ./internal/orchestrator -run 'Test(PushDiscoveryDiagnostics|ExistingWatchDiscoveryDiagnostics|DetectPushAndAssociatePR|GitHubPushAssociation|PushAssociation|ResolvePRWatchBranchForWatch)' -count=1
|
||||
go test ./internal/github -run 'Test(ClassifyPRDiscoveryError|PRDiscovery)' -count=1
|
||||
golangci-lint run ./... --new-from-rev='195530e5766abf7f44f08e2d5ad436b22c28d748' --timeout=5m
|
||||
```
|
||||
|
||||
The later CI remediation reran base-relative lint with a ten-minute analyzer
|
||||
budget and it reported zero issues. The changed lifecycle and websocket
|
||||
packages, including race-enabled runs, pass locally. The full backend build
|
||||
now passes; a complete local test run remains nonzero on the documented
|
||||
environment-sensitive process-tree, home-config, and launcher tests, plus one
|
||||
lifecycle package failure under aggregate scheduling; the lifecycle package
|
||||
passed when rerun alone. CI's E2E matrix remained green, so this work order has
|
||||
no E2E changes.
|
||||
@@ -0,0 +1,97 @@
|
||||
---
|
||||
id: "03-setup-log-severity"
|
||||
title: "Quiet empty setup scripts"
|
||||
status: done
|
||||
wave: 1
|
||||
depends_on: []
|
||||
plan: "plan.md"
|
||||
requirements:
|
||||
- REQ-PLATFORM-DIAGNOSTIC-LOGGING-001
|
||||
acceptance_criteria:
|
||||
- AC-PLATFORM-DIAGNOSTIC-LOGGING-001.12
|
||||
system_design:
|
||||
- ../../specs/platform/system-design/diagnostic-logging-01.md
|
||||
---
|
||||
|
||||
# Task 03: Quiet empty setup scripts
|
||||
|
||||
## Summary
|
||||
|
||||
Record intentional comment-only setup omissions at debug level. Preserve
|
||||
setup resolution and actual script-failure warnings.
|
||||
|
||||
## In scope
|
||||
|
||||
- Change the omission log in `resolvePreparerSetupScript` from Warn to Debug.
|
||||
- Preserve identity fields and omit script contents.
|
||||
- Cover default and explicit comment-only scripts without changing execution.
|
||||
|
||||
## Out of scope
|
||||
|
||||
Other work orders, terminal behavior, UI, schema, retry policy, and live-instance changes.
|
||||
|
||||
## Acceptance
|
||||
|
||||
1. Comment-only scripts return an empty result without a warning.
|
||||
2. Debug logs retain useful identity fields and contain no script content.
|
||||
3. Executable scripts and actual setup failures retain their existing behavior.
|
||||
|
||||
## Regression evidence
|
||||
|
||||
Extend `preparer_script_test.go` with
|
||||
`TestResolvePreparerSetupScriptDiagnosticSeverity`. Observe the logger while
|
||||
resolving default and explicit comment-only scripts. The warning assertion must
|
||||
fail before the change. Include blank and shebang-only inputs and an executable
|
||||
script control. Restore the package logger with cleanup and do not run this test
|
||||
in parallel. Retain existing script execution failure tests.
|
||||
|
||||
## Verification
|
||||
|
||||
Run the regression first and record its expected failure. After implementation,
|
||||
run these commands from the repository root:
|
||||
|
||||
```bash
|
||||
(cd apps/backend && go test ./internal/agent/runtime/lifecycle -run 'Test(ResolvePreparerSetupScript|IsScriptEffectivelyEmpty|.*SetupScript.*)' -count=1)
|
||||
(cd apps/backend && go test -race ./internal/agent/runtime/lifecycle -run 'TestResolvePreparerSetupScriptDiagnosticSeverity' -count=1)
|
||||
git diff --check
|
||||
```
|
||||
|
||||
## Files likely touched
|
||||
|
||||
- `apps/backend/internal/agent/runtime/lifecycle/preparer_script.go`
|
||||
- `apps/backend/internal/agent/runtime/lifecycle/preparer_script_test.go`
|
||||
|
||||
## Dependencies
|
||||
|
||||
None. Execute in the plan order by default.
|
||||
|
||||
## Risks
|
||||
|
||||
Package logger replacement needs serial tests and cleanup.
|
||||
|
||||
## Parallelism
|
||||
|
||||
`sequential`
|
||||
|
||||
## Inputs
|
||||
|
||||
- [Requirement](../../specs/platform/requirements/diagnostic-logging.md)
|
||||
- [System design](../../specs/platform/system-design/diagnostic-logging-01.md)
|
||||
- [Plan evidence and exclusions](plan.md)
|
||||
- Backend AGENTS.md and TDD backend test guidance.
|
||||
|
||||
## Results
|
||||
|
||||
Changed the intentional comment-only setup omission log from WARN to DEBUG.
|
||||
Resolution remains empty for default, explicit, blank, and shebang-only scripts;
|
||||
executable scripts remain executable. The observer regression confirms identity
|
||||
fields remain present and script contents remain absent.
|
||||
|
||||
The warning assertion failed before the production change because the existing
|
||||
log level was WARN. Verification passed:
|
||||
|
||||
```text
|
||||
go test ./internal/agent/runtime/lifecycle -run 'Test(ResolvePreparerSetupScript|IsScriptEffectivelyEmpty|.*SetupScript.*)' -count=1
|
||||
go test -race ./internal/agent/runtime/lifecycle -run 'TestResolvePreparerSetupScriptDiagnosticSeverity' -count=1
|
||||
git diff --check
|
||||
```
|
||||
@@ -36,6 +36,12 @@ and provider status. Task and UI systems consume its results.
|
||||
A confirmed rename may update that target; missing or ambiguous branch
|
||||
information shall not redirect it to a sibling target.
|
||||
|
||||
- **AC-INTEGRATIONS-GITHUB-PR-DISCOVERY-001.5:** Post-push diagnostics shall
|
||||
distinguish a provider error from a successful search without a PR.
|
||||
Provider errors shall include a credential-safe category and target identity.
|
||||
Successful empty searches shall not produce warnings. Cancellation shall not
|
||||
report provider failure. A later success shall not retain a failed final outcome.
|
||||
|
||||
### REQ-INTEGRATIONS-GITHUB-PR-DISCOVERY-HEALTH-001: Explain discovery failures
|
||||
|
||||
#### Acceptance criteria
|
||||
|
||||
@@ -149,6 +149,37 @@ They must assert the phone drawer, not a tooltip opened by a simulated tap.
|
||||
No live task is repaired by this package. After deployment, normal discovery
|
||||
can link eligible PRs. Waiting for quota reset cannot repair invalid code.
|
||||
|
||||
## Post-push diagnostic outcomes
|
||||
|
||||
Discovery ACs .3 and .5 also cover `event_handlers_github.go` in the
|
||||
orchestrator. `detectPushAndAssociatePRWithIdentity` currently merges provider
|
||||
errors and empty results. `searchPRForExistingWatch` silently discards lookup
|
||||
errors. Both paths must distinguish `found`, `empty`, `failed`, and `canceled`.
|
||||
|
||||
For a failed lookup, log a warning with the operation, workspace, task, session,
|
||||
resolved repository ID, owner, repository name, branch, and safe category.
|
||||
Reuse `classifyPRDiscoveryError` through a narrow exported classifier in the
|
||||
GitHub package. Do not log `err.Error()`, response bodies, or request URLs.
|
||||
Retain `invalid_query`, `rate_limited`, and `unavailable` as categories.
|
||||
Do not invent an authentication diagnosis from an unclassified error.
|
||||
|
||||
Each successful empty lookup is debug-level evidence. At retry exhaustion,
|
||||
report attempt, error, and empty counts plus the last outcome at debug level.
|
||||
Failed attempts already have warnings. An empty result after an error records
|
||||
an empty final outcome. A found result follows the existing association path.
|
||||
Cancellation returns without a failure or exhausted-retry warning.
|
||||
|
||||
Keep the production retry schedule, service admission, watch identity,
|
||||
association ownership, and health recovery rules unchanged. Factor a private
|
||||
retry runner with an injected wait function if needed for deterministic tests.
|
||||
Production waits remain context-aware with delays of zero, 30, and 60 seconds.
|
||||
Tests exercise the runner through the orchestration entry point without sleeps.
|
||||
No public status fields or frontend changes are necessary.
|
||||
|
||||
Delivery: [Recovery diagnostic fixes](../../../plans/recovery-diagnostics/plan.md).
|
||||
The completed discovery-health and watch-reconciliation packages retain their
|
||||
historical scope and results.
|
||||
|
||||
## References
|
||||
|
||||
- [GitHub Repository schema](https://docs.github.com/en/graphql/reference/objects#repository)
|
||||
|
||||
@@ -41,6 +41,11 @@ Users can see frontend failures that leave no backend evidence, and support cann
|
||||
than 15 seconds. A browser wall-clock offset shall not change this duration.
|
||||
The backend shall remain authoritative for job expiry.
|
||||
|
||||
- **AC-PLATFORM-DIAGNOSTIC-LOGGING-001.12:** When a resolved setup script
|
||||
contains only comments, a shebang, or blank lines, its omission shall not
|
||||
produce a warning. Debug diagnostics can record the omission. Executable
|
||||
script failures shall retain their existing warning behavior.
|
||||
|
||||
## System design
|
||||
|
||||
The migrated technical source is split into [part 1](../system-design/diagnostic-logging-01.md), [part 2](../system-design/diagnostic-logging-02.md).
|
||||
|
||||
@@ -331,3 +331,17 @@ acp/session-01/normalized-acp.jsonl
|
||||
description. The bootstrap route never waits for frontend collection.
|
||||
The task-context copy retains the existing 24-hour cleanup window so a newly
|
||||
launched agent can read it.
|
||||
|
||||
## Expected setup-script omissions
|
||||
|
||||
AC .12 applies the logging severity contract to
|
||||
`internal/agent/runtime/lifecycle/preparer_script.go`. Change the comment-only
|
||||
branch in `resolvePreparerSetupScript` to debug severity. Retain its current
|
||||
structured identity fields and omit script content. This includes both default
|
||||
and explicit comment-only scripts.
|
||||
|
||||
Keep `isScriptEffectivelyEmpty`, placeholder resolution, returned values, and
|
||||
`runSetupScriptStep` failure warnings unchanged. No logger-wide filter or
|
||||
terminal HTTP suppression is part of this change.
|
||||
|
||||
Delivery: [Recovery diagnostic fixes](../../../plans/recovery-diagnostics/plan.md).
|
||||
|
||||
@@ -82,6 +82,13 @@ local branches without publishing them first.
|
||||
classification. Authentication, network, timeout, cancellation, divergent,
|
||||
uncertain, and other unproven failures remain fatal for pull-request tasks.
|
||||
|
||||
- **AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.14:** When a base refresh fails,
|
||||
backend diagnostics shall identify the repository, operation, branch, and
|
||||
failure category. Known causes shall have a fixed diagnostic explanation.
|
||||
Unknown causes shall remain explicitly unknown. Diagnostics shall exclude
|
||||
credentials, remote URLs, and raw command output. Existing refresh and
|
||||
fallback decisions shall remain unchanged.
|
||||
|
||||
## Compatibility
|
||||
|
||||
This requirement replaces the universal fail-closed refresh behavior from
|
||||
|
||||
@@ -37,6 +37,7 @@ change Git commands that an agent runs after launch.
|
||||
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.11` | [Pull-request base reconciliation](#pull-request-base-reconciliation) |
|
||||
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.12` | [Pull-request base reconciliation](#pull-request-base-reconciliation) |
|
||||
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.13` | [Missing-base fallback](#missing-base-fallback) |
|
||||
| `AC-WORKSPACES-WORKTREE-BASE-REFRESH-001.14` | [Safe refresh diagnostics](#safe-refresh-diagnostics) |
|
||||
|
||||
## Components and responsibilities
|
||||
|
||||
@@ -211,6 +212,39 @@ Local fallback reports completion with a warning, not a launch error.
|
||||
Structured logs record repository identity, refresh route, failure class, and
|
||||
selected fallback ref. Logs exclude credential material and raw remote URLs.
|
||||
|
||||
### Safe refresh diagnostics
|
||||
|
||||
AC .14 extends the existing observability contract. `manager_git.go` captures
|
||||
combined command output, but failure paths retain only coarse reasons or an
|
||||
exit status. Inspect that output in memory and emit fixed diagnostic values.
|
||||
Do not append raw output to logs, returned errors, or progress events.
|
||||
|
||||
Keep the existing policy `reason` separate from a diagnostic-only
|
||||
`diagnostic_code`. Add an allow-listed classifier in
|
||||
`internal/worktree/refresh_diagnostics.go`. It recognizes authentication,
|
||||
SSH public-key rejection, SSH host verification, DNS, connection failure,
|
||||
TLS verification, missing remote ref, non-fast-forward rejection, repository
|
||||
access failure, and lock contention. Unknown output maps to `unknown`.
|
||||
Context cancellation and execution timeout take precedence over text matches.
|
||||
Each code maps to a fixed English explanation. No explanation includes
|
||||
substrings from Git output. Repository access failure does not assert that
|
||||
credentials are invalid, and lock contention does not assert a stale lock.
|
||||
|
||||
Emit one structured diagnostic per failed fetch or pull with `operation`,
|
||||
`repository_path`, `branch`, `reason`, `diagnostic_code`, `detail`, and an exit
|
||||
code when available. The local repository path identifies the checkout without
|
||||
reading its remote URL. Existing failure or fallback logs can carry these fields
|
||||
instead of adding a duplicate diagnostic. Cover both required and best-effort
|
||||
paths, including a failed configured fallback branch.
|
||||
|
||||
Classification is observational. Do not feed new diagnostic categories into
|
||||
missing-base fallback eligibility or alter `syncFailureCause` identities.
|
||||
Preserve context errors and current progress/error payloads. Correct the
|
||||
`syncFailureCause` comment that implies raw output reaches internal logs.
|
||||
No new logging configuration, subprocess, retry, or persistence is required.
|
||||
|
||||
Delivery: [Recovery diagnostic fixes](../../../plans/recovery-diagnostics/plan.md).
|
||||
|
||||
## Related decisions
|
||||
|
||||
- [Local Worktree Refresh Is Best Effort](../../../decisions/2026-08-31-local-worktree-refresh-best-effort.md)
|
||||
|
||||
Reference in New Issue
Block a user