Skip to content

Commit ba3670c

Browse files
Merge branch 'main' into feat/bootstrap-action-map
2 parents c5b7401 + 629882e commit ba3670c

9 files changed

Lines changed: 66 additions & 24 deletions

File tree

.github/workflows/build.yml

Lines changed: 14 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

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/mise.toml

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ run = "yarn eslint"
1717

1818
[tasks.test]
1919
description = "Jest tests"
20+
depends = [":compile"]
2021
run = "yarn test"
2122

2223
[tasks.synth]
@@ -25,6 +26,7 @@ run = "yarn synth"
2526

2627
[tasks."synth:quiet"]
2728
description = "cdk synth (quiet)"
29+
depends = [":compile"]
2830
run = "yarn synth:quiet"
2931

3032
[tasks.deploy]
@@ -54,5 +56,5 @@ description = "No-op bundle step for cdk/cdk.json (NodejsFunction bundles at syn
5456
run = "node -e \"process.exit(0)\""
5557

5658
[tasks.build]
57-
description = "compile, test, eslint, synth (quiet)"
58-
depends = [":compile", ":test", ":eslint", ":synth:quiet"]
59+
description = "compile, test, eslint, synth (quiet) — DAG: compile first, then test+synth+eslint in parallel"
60+
depends = [":test", ":eslint", ":synth:quiet"]

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

mise.toml

Lines changed: 4 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -151,16 +151,11 @@ run = [
151151
##### BUILD #####
152152
##################
153153

154-
# Agent code is packaged by CDK (Docker asset); validate Python before heavy JS/CDK work.
155-
# Multiple `depends` run in parallel, so agent is a sole prerequisite, then packages run in order.
154+
# All packages run in parallel via DAG. Each resolves its own internal dependencies.
155+
# Agent quality runs alongside CDK/CLI/docs — no sequential bottleneck.
156156
[tasks.build]
157-
description = "Monorepo build (agent quality, then cdk, cli, docs)"
158-
depends = ["//agent:quality"]
159-
run = [
160-
"MISE_EXPERIMENTAL=1 mise //cdk:build",
161-
"MISE_EXPERIMENTAL=1 mise //cli:build",
162-
"MISE_EXPERIMENTAL=1 mise //docs:build",
163-
]
157+
description = "Monorepo build (agent, cdk, cli, docs in parallel)"
158+
depends = ["//agent:quality", "//cdk:build", "//cli:build", "//docs:build"]
164159

165160
[tasks.default]
166161
description = "Install + build"

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)