Commit 3cc65b5
* feat(agent): blocker taxonomy + agent_blocked event contract (#251)
Phase 0 of #251 — the foundation contract, no behavior change.
- agent/src/progress_writer.py: closed BLOCKER_KINDS set,
format_blocker_reason() producing the canonical BLOCKED[<kind>]: <detail>
(+ resource) terminal-reason string (single source of truth, decision D),
and write_agent_blocked() emitting a distinct event_type=agent_blocked.
- cdk error-classifier: new BLOCKED category + per-kind remedy builder that
keys on the BLOCKED[<kind>] prefix and extracts the resource so remedies
name the exact secret/host.
- docs: CEDAR_HITL_GATES §13.16 taxonomy + event/reason contract; COMPUTE.md
result-contract cross-link; regenerated Starlight mirrors.
- tests: progress_writer event shape/kinds/truncation + reason round-trip;
classifier per-kind + resource extraction + carry-path.
* feat(agent): detect + surface + remediate environmental blockers (#251)
Phases 1-3 of #251.
Phase 1 — detection at the three fault points:
- policy.py: structured fail_closed flag on PolicyDecision (decision E),
set True only at engine-error/unavailable deny sites.
- hooks.py: PreToolUse emits policy_fail_closed on fail-closed deny (branches
on the flag, never a reason string; hard-denies never emit). PostToolUse
scans tool output for egress-denial signatures and emits egress_denied with
the host. A process-lifetime latch carries the canonical reason into the
terminal error via pipeline._resolve_overall_task_status.
- context-hydration.ts: resolveGitHubToken throws MissingSecretError with the
canonical BLOCKED[missing_secret] reason on ResourceNotFoundException/empty
secret; propagated (not degraded) so failTask + classifyError surface it.
Phase 2 — bounded self-remediation (safe-by-default):
- shell.py: run_cmd_with_backoff — capped exponential backoff, transient-only
retry, injectable sleep + audit callback (no hooks/progress dep).
- repo.py: clone + PR-branch fetch retry transient failures; on exhaustion
report dependency_unreachable and raise. Scope-preserving by construction.
Phase 3 — surface + consume:
- CLI watch renders agent_blocked (⛔ + kind/resource/hint); status snapshot
shows the latest blocker. Notifications flow via the existing classifier path.
Tests across agent/cdk/cli; docs §13.16 updated + synced.
* fix(agent): address PR review findings for #251 blocker signal
- repo.py: reclassify a retry-exhausted setup failure that names a host
(could not resolve host / name-resolution) as non-retryable egress_denied
instead of retryable dependency_unreachable — the setup path and the
PostToolUse egress detector now reach the same verdict for identical stderr,
so users get the allowlist remedy rather than a fruitless 'retry the task'.
- context-hydration.ts: add SecretUnreadableError (auth_failure) for a token
secret that exists but can't be read (AccessDenied/throttling). Propagate it
through hydration + issue/PR fetch instead of silently degrading to minimal
context, which stranded the task with no token.
- tests: egress-vs-transient reclassification, PR-branch fetch retry resource,
exact retry+exhaustion event count, unreadable-secret propagation + cause,
latest-blocker-wins in status snapshot; update preflight assertion.
- docs: note the egress-over-transient precedence in CEDAR_HITL_GATES §13.16.
Findings adjudicated not-a-defect: blocker latch cross-task leak (AgentCore
runs one task per Firecracker MicroVM — COMPUTE.md; latch follows the existing
per-process pattern) and host-less egress event (documented heuristic signal).
* fix(agent,cdk,cli): address second-round PR review for #251
Major — repo setup no longer wastes backoff on named-host failures:
run_cmd_with_backoff now bails immediately when stderr names a host
(could not resolve host: <h>), so a firewalled/non-existent endpoint is
not retried and emits no misleading dependency_unreachable events before
_fail_setup_command reclassifies it to non-retryable egress_denied. A
host-less name-resolution blip stays retryable.
Minor fixes:
- error-classifier: auth_failure remedy branches on a Secrets Manager ARN
resource — IAM/GetSecretValue advice instead of PAT-scope advice.
- preflight: token-secret failures now report GITHUB_TOKEN_SECRET_MISSING /
GITHUB_TOKEN_SECRET_UNREADABLE instead of the misleading GITHUB_UNREACHABLE
(classifyError still routes on the BLOCKED[...] detail; this fixes the
failureReason label + priority ordering).
- format.ts: suppress the historical Blocker: line on COMPLETED tasks so a
self-remediated success is not shown as blocked.
- workflow/runner: thread progress into setup_repo so clone/fetch backoff
blocker events reach the live stream on the workflow path.
Tests: named-host no-retry, host-less-still-retryable, SM-ARN auth remedy,
new preflight reasons, COMPLETED blocker suppression, workflow stub arity.
Docs: §13.16 updated for the bail-before-retry behavior + synced mirror.
Nit (latch thread-safety) left as-is — safe under one-task-per-MicroVM.
* fix(agent): narrow retry-bail to DNS-only + close review coverage gaps (#251)
Third review round found one regression I introduced + two test gaps:
- REGRESSION FIX (shell.py): the round-2 bail reused detect_egress_denial,
which also matches 'Failed to connect to <host>' — a transient TCP timeout
to an ALLOWLISTED host that git emits on 'Connection timed out'. That made
backoff fast-fail a genuinely-retryable failure as egress_denied. Replaced
with a DNS-only predicate (_UNRESOLVABLE_HOST_RE: could not resolve host /
getaddrinfo ENOTFOUND|EAI_AGAIN / Failed to resolve). TCP timeouts now retry;
a persistent one is still reclassified to egress_denied at exhaustion. Also
removes the shell→hooks call-time import (F2: no detector can raise mid-retry).
- COVERAGE (test_repo.py): add an end-to-end test driving the REAL
run_cmd_with_backoff (patching shell.run_cmd, not the fake) — a named-host DNS
failure bails on the first clone (no retry), emits zero dependency_unreachable
events, exactly one non-retryable egress_denied.
- COVERAGE (test_shell.py): add TCP-connect-timeout-still-retries (the
regression guard) alongside the DNS-bail test.
- COVERAGE (test_workflow_runner.py): assert progress is actually threaded
through to setup_repo (was arity-only; a revert to setup_repo(config) would
have passed silently).
- COVERAGE (format-status-snapshot.test.ts): FAILED task still shows the
Blocker line (guards the COMPLETED-only suppression against broadening).
Docs §13.16 corrected: DNS-names-host bails, TCP-connect stays retryable.
Not changed: GovCloud ARN partition in the auth_failure remedy (no producer
today) and preflight priority-ordering test (token failure early-returns, so
the ordering is defensive-only) — both noted as low/future by the review.
---------
Co-authored-by: bgagent <bgagent@noreply.github.com>
1 parent 6340aba commit 3cc65b5
29 files changed
Lines changed: 1916 additions & 41 deletions
File tree
- agent
- src
- workflow
- tests
- cdk
- cli
- src
- commands
- test
- commands
- docs
- design
- src/content/docs/architecture
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
99 | 99 | | |
100 | 100 | | |
101 | 101 | | |
| 102 | + | |
| 103 | + | |
| 104 | + | |
| 105 | + | |
| 106 | + | |
| 107 | + | |
| 108 | + | |
| 109 | + | |
| 110 | + | |
| 111 | + | |
| 112 | + | |
| 113 | + | |
| 114 | + | |
| 115 | + | |
| 116 | + | |
| 117 | + | |
| 118 | + | |
| 119 | + | |
| 120 | + | |
| 121 | + | |
| 122 | + | |
| 123 | + | |
| 124 | + | |
| 125 | + | |
| 126 | + | |
| 127 | + | |
| 128 | + | |
| 129 | + | |
| 130 | + | |
| 131 | + | |
| 132 | + | |
| 133 | + | |
| 134 | + | |
| 135 | + | |
| 136 | + | |
| 137 | + | |
| 138 | + | |
| 139 | + | |
| 140 | + | |
| 141 | + | |
| 142 | + | |
| 143 | + | |
| 144 | + | |
| 145 | + | |
| 146 | + | |
| 147 | + | |
| 148 | + | |
| 149 | + | |
| 150 | + | |
| 151 | + | |
| 152 | + | |
| 153 | + | |
| 154 | + | |
| 155 | + | |
| 156 | + | |
| 157 | + | |
| 158 | + | |
| 159 | + | |
| 160 | + | |
| 161 | + | |
| 162 | + | |
| 163 | + | |
| 164 | + | |
| 165 | + | |
| 166 | + | |
| 167 | + | |
| 168 | + | |
| 169 | + | |
| 170 | + | |
| 171 | + | |
| 172 | + | |
| 173 | + | |
| 174 | + | |
| 175 | + | |
| 176 | + | |
| 177 | + | |
| 178 | + | |
| 179 | + | |
| 180 | + | |
| 181 | + | |
| 182 | + | |
| 183 | + | |
| 184 | + | |
| 185 | + | |
| 186 | + | |
| 187 | + | |
| 188 | + | |
| 189 | + | |
| 190 | + | |
| 191 | + | |
| 192 | + | |
| 193 | + | |
| 194 | + | |
| 195 | + | |
| 196 | + | |
| 197 | + | |
102 | 198 | | |
103 | 199 | | |
104 | 200 | | |
| |||
214 | 310 | | |
215 | 311 | | |
216 | 312 | | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
| 320 | + | |
| 321 | + | |
| 322 | + | |
| 323 | + | |
| 324 | + | |
| 325 | + | |
| 326 | + | |
| 327 | + | |
| 328 | + | |
| 329 | + | |
| 330 | + | |
| 331 | + | |
| 332 | + | |
| 333 | + | |
| 334 | + | |
| 335 | + | |
| 336 | + | |
217 | 337 | | |
218 | 338 | | |
219 | 339 | | |
| |||
918 | 1038 | | |
919 | 1039 | | |
920 | 1040 | | |
| 1041 | + | |
921 | 1042 | | |
922 | 1043 | | |
923 | 1044 | | |
924 | 1045 | | |
925 | 1046 | | |
926 | 1047 | | |
927 | 1048 | | |
| 1049 | + | |
| 1050 | + | |
| 1051 | + | |
| 1052 | + | |
| 1053 | + | |
928 | 1054 | | |
929 | 1055 | | |
930 | 1056 | | |
| |||
950 | 1076 | | |
951 | 1077 | | |
952 | 1078 | | |
| 1079 | + | |
| 1080 | + | |
| 1081 | + | |
| 1082 | + | |
| 1083 | + | |
| 1084 | + | |
| 1085 | + | |
| 1086 | + | |
| 1087 | + | |
| 1088 | + | |
| 1089 | + | |
| 1090 | + | |
| 1091 | + | |
| 1092 | + | |
| 1093 | + | |
| 1094 | + | |
| 1095 | + | |
| 1096 | + | |
| 1097 | + | |
| 1098 | + | |
| 1099 | + | |
| 1100 | + | |
| 1101 | + | |
| 1102 | + | |
| 1103 | + | |
| 1104 | + | |
| 1105 | + | |
| 1106 | + | |
| 1107 | + | |
| 1108 | + | |
| 1109 | + | |
| 1110 | + | |
| 1111 | + | |
| 1112 | + | |
| 1113 | + | |
| 1114 | + | |
| 1115 | + | |
953 | 1116 | | |
954 | 1117 | | |
955 | 1118 | | |
| |||
1404 | 1567 | | |
1405 | 1568 | | |
1406 | 1569 | | |
1407 | | - | |
| 1570 | + | |
| 1571 | + | |
| 1572 | + | |
1408 | 1573 | | |
1409 | 1574 | | |
1410 | 1575 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
469 | 469 | | |
470 | 470 | | |
471 | 471 | | |
| 472 | + | |
| 473 | + | |
| 474 | + | |
| 475 | + | |
| 476 | + | |
| 477 | + | |
| 478 | + | |
| 479 | + | |
| 480 | + | |
| 481 | + | |
472 | 482 | | |
473 | 483 | | |
474 | 484 | | |
| |||
480 | 490 | | |
481 | 491 | | |
482 | 492 | | |
| 493 | + | |
| 494 | + | |
| 495 | + | |
| 496 | + | |
| 497 | + | |
483 | 498 | | |
484 | 499 | | |
485 | 500 | | |
486 | 501 | | |
| 502 | + | |
| 503 | + | |
487 | 504 | | |
488 | 505 | | |
489 | 506 | | |
| |||
669 | 686 | | |
670 | 687 | | |
671 | 688 | | |
| 689 | + | |
| 690 | + | |
| 691 | + | |
| 692 | + | |
| 693 | + | |
| 694 | + | |
| 695 | + | |
| 696 | + | |
672 | 697 | | |
673 | 698 | | |
674 | 699 | | |
| |||
794 | 819 | | |
795 | 820 | | |
796 | 821 | | |
797 | | - | |
| 822 | + | |
798 | 823 | | |
799 | 824 | | |
800 | 825 | | |
| |||
| Original file line number | Diff line number | Diff line change | |
|---|---|---|---|
| |||
205 | 205 | | |
206 | 206 | | |
207 | 207 | | |
| 208 | + | |
208 | 209 | | |
209 | 210 | | |
210 | 211 | | |
| |||
223 | 224 | | |
224 | 225 | | |
225 | 226 | | |
| 227 | + | |
226 | 228 | | |
227 | 229 | | |
228 | 230 | | |
| |||
244 | 246 | | |
245 | 247 | | |
246 | 248 | | |
| 249 | + | |
| 250 | + | |
| 251 | + | |
| 252 | + | |
| 253 | + | |
| 254 | + | |
| 255 | + | |
| 256 | + | |
| 257 | + | |
| 258 | + | |
247 | 259 | | |
248 | 260 | | |
249 | 261 | | |
| |||
296 | 308 | | |
297 | 309 | | |
298 | 310 | | |
299 | | - | |
300 | | - | |
| 311 | + | |
| 312 | + | |
| 313 | + | |
| 314 | + | |
| 315 | + | |
| 316 | + | |
| 317 | + | |
| 318 | + | |
| 319 | + | |
301 | 320 | | |
302 | 321 | | |
303 | 322 | | |
| |||
1153 | 1172 | | |
1154 | 1173 | | |
1155 | 1174 | | |
| 1175 | + | |
1156 | 1176 | | |
1157 | 1177 | | |
1158 | 1178 | | |
| |||
1174 | 1194 | | |
1175 | 1195 | | |
1176 | 1196 | | |
| 1197 | + | |
1177 | 1198 | | |
1178 | 1199 | | |
1179 | 1200 | | |
| |||
1306 | 1327 | | |
1307 | 1328 | | |
1308 | 1329 | | |
| 1330 | + | |
1309 | 1331 | | |
1310 | 1332 | | |
1311 | 1333 | | |
| |||
0 commit comments