Skip to content

Commit 00eb831

Browse files
Merge branch 'main' into fix/201-mise-parallel
2 parents fb5931a + 2e0c534 commit 00eb831

6 files changed

Lines changed: 44 additions & 11 deletions

File tree

agent/src/hooks.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,7 @@
3030
import task_state
3131
from nudge_reader import _xml_escape
3232
from output_scanner import scan_tool_output
33-
from policy import APPROVAL_RATE_LIMIT, Outcome
33+
from policy import APPROVAL_RATE_LIMIT, FLOOR_TIMEOUT_S, Outcome
3434
from progress_writer import _generate_ulid
3535
from shell import log, log_error_cw
3636

@@ -42,7 +42,7 @@
4242
# Chunk 3 constants (§6.5 pseudocode)
4343
# ---------------------------------------------------------------------------
4444

45-
FLOOR_30S: int = 30 # §6 decision #6: approval floor on effective timeout
45+
FLOOR_30S: int = FLOOR_TIMEOUT_S # §6 decision #6: sourced from contracts/constants.json
4646
CLEANUP_MARGIN_120S: int = 120 # §6.5 lifetime-margin reserve for cleanup
4747
# Poll cadence per §3 decision #3 and IMPL-12: 2s for the first 30s, 5s
4848
# thereafter. Exact counts vary with ``timeout_s``; these pin the

agent/src/policy.py

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -75,9 +75,7 @@
7575
# Constants (§3, §5.2, §12.9)
7676
# ---------------------------------------------------------------------------
7777

78-
FLOOR_TIMEOUT_S: int = 30 # §6 decision #6: rejected below this at load
7978
WARN_TIMEOUT_S: int = 120 # IMPL-25: sub-120s emits WARN on blueprint load
80-
DEFAULT_TASK_TIMEOUT_S: int = 300 # §6 decision #6 default
8179

8280

8381
def _load_shared_constants() -> dict:
@@ -109,6 +107,9 @@ def _load_shared_constants() -> dict:
109107
DEFAULT_APPROVAL_GATE_CAP: int = int(_AGC["default"]) # decision #13 default
110108
APPROVAL_GATE_CAP_MIN: int = int(_AGC["min"])
111109
APPROVAL_GATE_CAP_MAX: int = int(_AGC["max"])
110+
_ATS = _SHARED_CONSTANTS["approval_timeout_s"]
111+
FLOOR_TIMEOUT_S: int = int(_ATS["min"]) # §6 decision #6: rejected below this at load
112+
DEFAULT_TASK_TIMEOUT_S: int = int(_ATS["default"]) # §6 decision #6 default
112113
CACHE_MAX_ENTRIES: int = 50 # §12.9: decoupled from approvalGateCap
113114
CACHE_TTL_S: float = 60.0 # §12.8 sliding-window TTL on DENIED/TIMED_OUT
114115
POLICIES_MAX_BYTES: int = 64 * 1024 # finding #12: reject blueprints > 64 KB

cdk/src/handlers/shared/types.ts

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1022,15 +1022,18 @@ export const INITIAL_APPROVALS_MAX_ENTRIES = 20;
10221022
/** Maximum per-entry length for an `initial_approvals` scope string. */
10231023
export const INITIAL_APPROVALS_MAX_ENTRY_LENGTH = 128;
10241024

1025-
/** Floor for `approval_timeout_s` (§6 decision #6). */
1026-
export const APPROVAL_TIMEOUT_S_MIN = 30;
1025+
/** Floor for `approval_timeout_s` (§6 decision #6).
1026+
* Sourced from ``contracts/constants.json`` (S9). */
1027+
export const APPROVAL_TIMEOUT_S_MIN = sharedConstants.approval_timeout_s.min;
10271028

10281029
/** Absolute ceiling for `approval_timeout_s` before the
1029-
* `maxLifetime - 300` clip is applied (§7.3). */
1030-
export const APPROVAL_TIMEOUT_S_MAX = 3600;
1030+
* `maxLifetime - 300` clip is applied (§7.3).
1031+
* Sourced from ``contracts/constants.json`` (S9). */
1032+
export const APPROVAL_TIMEOUT_S_MAX = sharedConstants.approval_timeout_s.max;
10311033

1032-
/** Default `approval_timeout_s` when the submit payload omits it. */
1033-
export const APPROVAL_TIMEOUT_S_DEFAULT = 300;
1034+
/** Default `approval_timeout_s` when the submit payload omits it.
1035+
* Sourced from ``contracts/constants.json`` (S9). */
1036+
export const APPROVAL_TIMEOUT_S_DEFAULT = sharedConstants.approval_timeout_s.default;
10341037

10351038
/**
10361039
* Cedar HITL: bounds + platform default for the per-task approval-gate cap

contracts/constants.json

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,5 +3,10 @@
33
"min": 1,
44
"max": 500,
55
"default": 50
6+
},
7+
"approval_timeout_s": {
8+
"min": 30,
9+
"max": 3600,
10+
"default": 300
611
}
712
}

contracts/constants.md

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,11 @@ JSON at TypeScript compile time via `resolveJsonModule`.
3636
"min": 1,
3737
"max": 500,
3838
"default": 50
39+
},
40+
"approval_timeout_s": {
41+
"min": 30,
42+
"max": 3600,
43+
"default": 300
3944
}
4045
}
4146
```
@@ -49,6 +54,14 @@ JSON at TypeScript compile time via `resolveJsonModule`.
4954
- **`approval_gate_cap.default`** — value applied when a blueprint omits
5055
the field. 50 is the design-decision default (see
5156
`docs/design/CEDAR_HITL_GATES.md` decision #13).
57+
- **`approval_timeout_s.min`** — floor for `approval_timeout_s` (§6
58+
decision #6). 30 seconds — below this, humans cannot realistically
59+
respond to an approval prompt.
60+
- **`approval_timeout_s.max`** — absolute ceiling for `approval_timeout_s`
61+
before the `maxLifetime - 300` clip is applied (§7.3). 3600 seconds
62+
(1 hour).
63+
- **`approval_timeout_s.default`** — value applied when the submit payload
64+
omits `approval_timeout_s`. 300 seconds (5 minutes) per §6 decision #6.
5265

5366
## Adding new constants
5467

scripts/check-constants-sync.ts

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,6 +59,8 @@ const OWNED_PYTHON_PATTERNS: ReadonlyArray<{ name: string; regex: RegExp }> = [
5959
{ name: 'DEFAULT_APPROVAL_GATE_CAP', regex: /^\s*DEFAULT_APPROVAL_GATE_CAP\s*(?::\s*int)?\s*=\s*-?\d+\b/m },
6060
{ name: 'APPROVAL_GATE_CAP_MIN', regex: /^\s*APPROVAL_GATE_CAP_MIN\s*(?::\s*int)?\s*=\s*-?\d+\b/m },
6161
{ name: 'APPROVAL_GATE_CAP_MAX', regex: /^\s*APPROVAL_GATE_CAP_MAX\s*(?::\s*int)?\s*=\s*-?\d+\b/m },
62+
{ name: 'FLOOR_TIMEOUT_S', regex: /^\s*FLOOR_TIMEOUT_S\s*(?::\s*int)?\s*=\s*-?\d+\b/m },
63+
{ name: 'DEFAULT_TASK_TIMEOUT_S', regex: /^\s*DEFAULT_TASK_TIMEOUT_S\s*(?::\s*int)?\s*=\s*-?\d+\b/m },
6264
];
6365

6466
interface Drift {
@@ -83,7 +85,10 @@ function findDriftInPython(filePath: string): Drift[] {
8385

8486
function main(): number {
8587
// Sanity: confirm the JSON is parseable and shaped as expected.
86-
let json: { approval_gate_cap?: { min: number; max: number; default: number } };
88+
let json: {
89+
approval_gate_cap?: { min: number; max: number; default: number };
90+
approval_timeout_s?: { min: number; max: number; default: number };
91+
};
8792
try {
8893
json = JSON.parse(fs.readFileSync(CONSTANTS_JSON, 'utf-8'));
8994
} catch (err) {
@@ -97,6 +102,12 @@ function main(): number {
97102
return 1;
98103
}
99104

105+
const ats = json.approval_timeout_s;
106+
if (!ats || typeof ats.min !== 'number' || typeof ats.max !== 'number' || typeof ats.default !== 'number') {
107+
console.error(`${CONSTANTS_JSON} is missing approval_timeout_s.{min,max,default}`);
108+
return 1;
109+
}
110+
100111
const drifts = findDriftInPython(POLICY_PY);
101112

102113
if (drifts.length > 0) {

0 commit comments

Comments
 (0)