Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
14 changes: 9 additions & 5 deletions .agents/skills/monitor-ci/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ The decision script returns one of the following statuses. This table defines th

```
cycle_count = 0 # Only incremented for agent-initiated cycles (counted against --max-cycles)
start_time = now()
start_time = now() # Passed to the decision script as --elapsed-seconds on every poll to enforce --timeout across attempts
no_progress_count = 0
local_verify_count = 0
env_rerun_count = 0
Expand Down Expand Up @@ -180,8 +180,9 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-cipe-url <last_cipe_url>] \
[--expected-sha <expected_commit_sha>] \
[--prev-status <prev_status>] \
[--timeout <timeout_seconds>] \
[--new-cipe-timeout <new_cipe_timeout_seconds>] \
[--timeout <timeout_minutes>] \
[--new-cipe-timeout <new_cipe_timeout_minutes>] \
[--elapsed-seconds <seconds_since_start_time>] \
[--env-rerun-count <env_rerun_count>] \
[--no-progress-count <no_progress_count>] \
[--prev-cipe-status <prev_cipe_status>] \
Expand All @@ -190,6 +191,8 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-failure-classification <prev_failure_classification>]
```

Pass `--timeout` and `--new-cipe-timeout` in **minutes** (the values from Configuration Defaults) — the script converts to seconds internally. Pass `--elapsed-seconds` as the whole seconds elapsed since `start_time` (`now() - start_time`); this is what enforces `--timeout` as a **total** monitor budget across every poll and attempt, so it must be supplied on every call once monitoring has started.

The script outputs a single JSON line: `{ action, code, message, delay?, noProgressCount, envRerunCount, fields?, newCipeDetected?, verifiableTaskIds? }`

#### 2c. Process script output
Expand Down Expand Up @@ -262,9 +265,10 @@ node <skill_dir>/scripts/ci-state-update.mjs cycle-check \
--env-rerun-count <env_rerun_count>
```

The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, message }`. Update tracking state from the output.
The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, limitReached, message }`. Update tracking state from the output.

- If `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If `limitReached` → the `--max-cycles` budget is exhausted. Print `message` and **stop monitoring** (do not handle the code or start another cycle). This is a hard stop, not advisory.
- Else if `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If previous cycle was NOT agent-triggered (human pushed), log that human-initiated push was detected

#### Progress Tracking
Expand Down
26 changes: 22 additions & 4 deletions .agents/skills/monitor-ci/scripts/ci-poll-decide.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
* Usage:
* node ci-poll-decide.mjs '<ci_info_json>' <poll_count> <verbosity> \
* [--wait-mode] [--prev-cipe-url <url>] [--expected-sha <sha>] \
* [--prev-status <status>] [--timeout <seconds>] [--new-cipe-timeout <seconds>] \
* [--env-rerun-count <n>] [--no-progress-count <n>] \
* [--prev-status <status>] [--timeout <minutes>] [--new-cipe-timeout <minutes>] \
* [--elapsed-seconds <n>] [--env-rerun-count <n>] [--no-progress-count <n>] \
* [--prev-cipe-status <status>] [--prev-sh-status <status>] \
* [--prev-verification-status <status>] [--prev-failure-classification <status>]
*
* Note: --timeout and --new-cipe-timeout are accepted in MINUTES (matching the
* skill's documented flags) and converted to seconds internally. --elapsed-seconds
* is the wall-clock time since monitoring began, carried across attempts by the
* orchestrator, and is the authoritative signal for the --timeout budget.
*/

// --- Arg parsing ---
Expand All @@ -39,8 +44,13 @@ const waitMode = getFlag("--wait-mode");
const prevCipeUrl = getArg("--prev-cipe-url");
const expectedSha = getArg("--expected-sha");
const prevStatus = getArg("--prev-status");
const timeoutSeconds = parseInt(getArg("--timeout") || "0", 10);
const newCipeTimeoutSeconds = parseInt(getArg("--new-cipe-timeout") || "0", 10);
// Flags are documented in minutes; convert to seconds for internal comparison.
const timeoutSeconds = parseInt(getArg("--timeout") || "0", 10) * 60;
const newCipeTimeoutSeconds = parseInt(getArg("--new-cipe-timeout") || "0", 10) * 60;
// Wall-clock seconds since monitoring began (carried across attempts); null when
// the orchestrator doesn't supply it (see isTimedOut for that fallback).
const elapsedArg = getArg("--elapsed-seconds");
const elapsedSeconds = elapsedArg !== null ? parseInt(elapsedArg, 10) : null;
const envRerunCount = parseInt(getArg("--env-rerun-count") || "0", 10);
const inputNoProgressCount = parseInt(getArg("--no-progress-count") || "0", 10);
const prevCipeStatus = getArg("--prev-cipe-status");
Expand Down Expand Up @@ -120,6 +130,10 @@ function hasStateChanged() {

function isTimedOut() {
if (timeoutSeconds <= 0) return false;
// Prefer real wall-clock elapsed (carried across attempts) so --timeout caps
// total monitor duration, not a single invocation.
if (elapsedSeconds !== null && !Number.isNaN(elapsedSeconds)) return elapsedSeconds >= timeoutSeconds;
// Fallback: estimate elapsed from poll cadence within this invocation.
const avgDelay = pollCount === 0 ? 0 : backoff(Math.floor(pollCount / 2));
return pollCount * avgDelay >= timeoutSeconds;
}
Expand Down Expand Up @@ -171,6 +185,10 @@ function classify() {
// --- Wait mode ---
if (waitMode) {
if (isNewCipe()) return { action: "poll", code: "new_cipe_detected" };
// The total --timeout budget also caps time spent waiting for a new CI
// Attempt, so it must win over --new-cipe-timeout here; otherwise a long
// wait (or a sequence of apply→wait cycles) could run past --timeout.
if (isTimedOut()) return { action: "done", code: "polling_timeout" };
if (isWaitTimedOut()) return { action: "done", code: "no_new_cipe" };
return { action: "wait", code: "waiting_for_cipe" };
}
Expand Down
11 changes: 9 additions & 2 deletions .agents/skills/monitor-ci/scripts/ci-state-update.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,22 @@ function cycleCheck() {
// Reset env_rerun_count on non-environment status
if (status !== "environment_issue") envRerunCount = 0;

// Approaching limit gate
// Cycle limit gates. limitReached is a terminal stop; approachingLimit is an
// advisory warning emitted in the two cycles before the cap.
const limitReached = cycleCount >= maxCycles;
const approachingLimit = cycleCount >= maxCycles - 2;

output({
cycleCount,
agentTriggered: false,
envRerunCount,
approachingLimit,
message: approachingLimit ? `Approaching cycle limit (${cycleCount}/${maxCycles})` : null,
limitReached,
message: limitReached
? `Cycle limit reached (${cycleCount}/${maxCycles}). Stopping.`
: approachingLimit
? `Approaching cycle limit (${cycleCount}/${maxCycles})`
: null,
});
}

Expand Down
7 changes: 7 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,12 @@
},
"enabledPlugins": {
"nx@nx-claude-plugins": true
},
"sandbox": {
"network": {
"allowedDomains": [
"www.google-analytics.com"
]
}
}
}
14 changes: 9 additions & 5 deletions .gemini/commands/monitor-ci.toml
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ The decision script returns one of the following statuses. This table defines th

```
cycle_count = 0 # Only incremented for agent-initiated cycles (counted against --max-cycles)
start_time = now()
start_time = now() # Passed to the decision script as --elapsed-seconds on every poll to enforce --timeout across attempts
no_progress_count = 0
local_verify_count = 0
env_rerun_count = 0
Expand Down Expand Up @@ -177,8 +177,9 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-cipe-url <last_cipe_url>] \\
[--expected-sha <expected_commit_sha>] \\
[--prev-status <prev_status>] \\
[--timeout <timeout_seconds>] \\
[--new-cipe-timeout <new_cipe_timeout_seconds>] \\
[--timeout <timeout_minutes>] \\
[--new-cipe-timeout <new_cipe_timeout_minutes>] \\
[--elapsed-seconds <seconds_since_start_time>] \\
[--env-rerun-count <env_rerun_count>] \\
[--no-progress-count <no_progress_count>] \\
[--prev-cipe-status <prev_cipe_status>] \\
Expand All @@ -187,6 +188,8 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-failure-classification <prev_failure_classification>]
```

Pass `--timeout` and `--new-cipe-timeout` in **minutes** (the values from Configuration Defaults) — the script converts to seconds internally. Pass `--elapsed-seconds` as the whole seconds elapsed since `start_time` (`now() - start_time`); this is what enforces `--timeout` as a **total** monitor budget across every poll and attempt, so it must be supplied on every call once monitoring has started.

The script outputs a single JSON line: `{ action, code, message, delay?, noProgressCount, envRerunCount, fields?, newCipeDetected?, verifiableTaskIds? }`

#### 2c. Process script output
Expand Down Expand Up @@ -259,9 +262,10 @@ node <skill_dir>/scripts/ci-state-update.mjs cycle-check \\
--env-rerun-count <env_rerun_count>
```

The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, message }`. Update tracking state from the output.
The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, limitReached, message }`. Update tracking state from the output.

- If `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If `limitReached` → the `--max-cycles` budget is exhausted. Print `message` and **stop monitoring** (do not handle the code or start another cycle). This is a hard stop, not advisory.
- Else if `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If previous cycle was NOT agent-triggered (human pushed), log that human-initiated push was detected

#### Progress Tracking
Expand Down
14 changes: 9 additions & 5 deletions .github/prompts/monitor-ci.prompt.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ The decision script returns one of the following statuses. This table defines th

```
cycle_count = 0 # Only incremented for agent-initiated cycles (counted against --max-cycles)
start_time = now()
start_time = now() # Passed to the decision script as --elapsed-seconds on every poll to enforce --timeout across attempts
no_progress_count = 0
local_verify_count = 0
env_rerun_count = 0
Expand Down Expand Up @@ -180,8 +180,9 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-cipe-url <last_cipe_url>] \
[--expected-sha <expected_commit_sha>] \
[--prev-status <prev_status>] \
[--timeout <timeout_seconds>] \
[--new-cipe-timeout <new_cipe_timeout_seconds>] \
[--timeout <timeout_minutes>] \
[--new-cipe-timeout <new_cipe_timeout_minutes>] \
[--elapsed-seconds <seconds_since_start_time>] \
[--env-rerun-count <env_rerun_count>] \
[--no-progress-count <no_progress_count>] \
[--prev-cipe-status <prev_cipe_status>] \
Expand All @@ -190,6 +191,8 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-failure-classification <prev_failure_classification>]
```

Pass `--timeout` and `--new-cipe-timeout` in **minutes** (the values from Configuration Defaults) — the script converts to seconds internally. Pass `--elapsed-seconds` as the whole seconds elapsed since `start_time` (`now() - start_time`); this is what enforces `--timeout` as a **total** monitor budget across every poll and attempt, so it must be supplied on every call once monitoring has started.

The script outputs a single JSON line: `{ action, code, message, delay?, noProgressCount, envRerunCount, fields?, newCipeDetected?, verifiableTaskIds? }`

#### 2c. Process script output
Expand Down Expand Up @@ -262,9 +265,10 @@ node <skill_dir>/scripts/ci-state-update.mjs cycle-check \
--env-rerun-count <env_rerun_count>
```

The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, message }`. Update tracking state from the output.
The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, limitReached, message }`. Update tracking state from the output.

- If `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If `limitReached` → the `--max-cycles` budget is exhausted. Print `message` and **stop monitoring** (do not handle the code or start another cycle). This is a hard stop, not advisory.
- Else if `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If previous cycle was NOT agent-triggered (human pushed), log that human-initiated push was detected

#### Progress Tracking
Expand Down
14 changes: 9 additions & 5 deletions .github/skills/monitor-ci/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -143,7 +143,7 @@ The decision script returns one of the following statuses. This table defines th

```
cycle_count = 0 # Only incremented for agent-initiated cycles (counted against --max-cycles)
start_time = now()
start_time = now() # Passed to the decision script as --elapsed-seconds on every poll to enforce --timeout across attempts
no_progress_count = 0
local_verify_count = 0
env_rerun_count = 0
Expand Down Expand Up @@ -180,8 +180,9 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-cipe-url <last_cipe_url>] \
[--expected-sha <expected_commit_sha>] \
[--prev-status <prev_status>] \
[--timeout <timeout_seconds>] \
[--new-cipe-timeout <new_cipe_timeout_seconds>] \
[--timeout <timeout_minutes>] \
[--new-cipe-timeout <new_cipe_timeout_minutes>] \
[--elapsed-seconds <seconds_since_start_time>] \
[--env-rerun-count <env_rerun_count>] \
[--no-progress-count <no_progress_count>] \
[--prev-cipe-status <prev_cipe_status>] \
Expand All @@ -190,6 +191,8 @@ node <skill_dir>/scripts/ci-poll-decide.mjs '<subagent_result_json>' <poll_count
[--prev-failure-classification <prev_failure_classification>]
```

Pass `--timeout` and `--new-cipe-timeout` in **minutes** (the values from Configuration Defaults) — the script converts to seconds internally. Pass `--elapsed-seconds` as the whole seconds elapsed since `start_time` (`now() - start_time`); this is what enforces `--timeout` as a **total** monitor budget across every poll and attempt, so it must be supplied on every call once monitoring has started.

The script outputs a single JSON line: `{ action, code, message, delay?, noProgressCount, envRerunCount, fields?, newCipeDetected?, verifiableTaskIds? }`

#### 2c. Process script output
Expand Down Expand Up @@ -262,9 +265,10 @@ node <skill_dir>/scripts/ci-state-update.mjs cycle-check \
--env-rerun-count <env_rerun_count>
```

The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, message }`. Update tracking state from the output.
The script returns `{ cycleCount, agentTriggered, envRerunCount, approachingLimit, limitReached, message }`. Update tracking state from the output.

- If `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If `limitReached` → the `--max-cycles` budget is exhausted. Print `message` and **stop monitoring** (do not handle the code or start another cycle). This is a hard stop, not advisory.
- Else if `approachingLimit` → ask user whether to continue (with 5 or 10 more cycles) or stop monitoring
- If previous cycle was NOT agent-triggered (human pushed), log that human-initiated push was detected

#### Progress Tracking
Expand Down
26 changes: 22 additions & 4 deletions .github/skills/monitor-ci/scripts/ci-poll-decide.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,15 @@
* Usage:
* node ci-poll-decide.mjs '<ci_info_json>' <poll_count> <verbosity> \
* [--wait-mode] [--prev-cipe-url <url>] [--expected-sha <sha>] \
* [--prev-status <status>] [--timeout <seconds>] [--new-cipe-timeout <seconds>] \
* [--env-rerun-count <n>] [--no-progress-count <n>] \
* [--prev-status <status>] [--timeout <minutes>] [--new-cipe-timeout <minutes>] \
* [--elapsed-seconds <n>] [--env-rerun-count <n>] [--no-progress-count <n>] \
* [--prev-cipe-status <status>] [--prev-sh-status <status>] \
* [--prev-verification-status <status>] [--prev-failure-classification <status>]
*
* Note: --timeout and --new-cipe-timeout are accepted in MINUTES (matching the
* skill's documented flags) and converted to seconds internally. --elapsed-seconds
* is the wall-clock time since monitoring began, carried across attempts by the
* orchestrator, and is the authoritative signal for the --timeout budget.
*/

// --- Arg parsing ---
Expand All @@ -39,8 +44,13 @@ const waitMode = getFlag("--wait-mode");
const prevCipeUrl = getArg("--prev-cipe-url");
const expectedSha = getArg("--expected-sha");
const prevStatus = getArg("--prev-status");
const timeoutSeconds = parseInt(getArg("--timeout") || "0", 10);
const newCipeTimeoutSeconds = parseInt(getArg("--new-cipe-timeout") || "0", 10);
// Flags are documented in minutes; convert to seconds for internal comparison.
const timeoutSeconds = parseInt(getArg("--timeout") || "0", 10) * 60;
const newCipeTimeoutSeconds = parseInt(getArg("--new-cipe-timeout") || "0", 10) * 60;
// Wall-clock seconds since monitoring began (carried across attempts); null when
// the orchestrator doesn't supply it (see isTimedOut for that fallback).
const elapsedArg = getArg("--elapsed-seconds");
const elapsedSeconds = elapsedArg !== null ? parseInt(elapsedArg, 10) : null;
const envRerunCount = parseInt(getArg("--env-rerun-count") || "0", 10);
const inputNoProgressCount = parseInt(getArg("--no-progress-count") || "0", 10);
const prevCipeStatus = getArg("--prev-cipe-status");
Expand Down Expand Up @@ -120,6 +130,10 @@ function hasStateChanged() {

function isTimedOut() {
if (timeoutSeconds <= 0) return false;
// Prefer real wall-clock elapsed (carried across attempts) so --timeout caps
// total monitor duration, not a single invocation.
if (elapsedSeconds !== null && !Number.isNaN(elapsedSeconds)) return elapsedSeconds >= timeoutSeconds;
// Fallback: estimate elapsed from poll cadence within this invocation.
const avgDelay = pollCount === 0 ? 0 : backoff(Math.floor(pollCount / 2));
return pollCount * avgDelay >= timeoutSeconds;
}
Expand Down Expand Up @@ -171,6 +185,10 @@ function classify() {
// --- Wait mode ---
if (waitMode) {
if (isNewCipe()) return { action: "poll", code: "new_cipe_detected" };
// The total --timeout budget also caps time spent waiting for a new CI
// Attempt, so it must win over --new-cipe-timeout here; otherwise a long
// wait (or a sequence of apply→wait cycles) could run past --timeout.
if (isTimedOut()) return { action: "done", code: "polling_timeout" };
if (isWaitTimedOut()) return { action: "done", code: "no_new_cipe" };
return { action: "wait", code: "waiting_for_cipe" };
}
Expand Down
11 changes: 9 additions & 2 deletions .github/skills/monitor-ci/scripts/ci-state-update.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -129,15 +129,22 @@ function cycleCheck() {
// Reset env_rerun_count on non-environment status
if (status !== "environment_issue") envRerunCount = 0;

// Approaching limit gate
// Cycle limit gates. limitReached is a terminal stop; approachingLimit is an
// advisory warning emitted in the two cycles before the cap.
const limitReached = cycleCount >= maxCycles;
const approachingLimit = cycleCount >= maxCycles - 2;

output({
cycleCount,
agentTriggered: false,
envRerunCount,
approachingLimit,
message: approachingLimit ? `Approaching cycle limit (${cycleCount}/${maxCycles})` : null,
limitReached,
message: limitReached
? `Cycle limit reached (${cycleCount}/${maxCycles}). Stopping.`
: approachingLimit
? `Approaching cycle limit (${cycleCount}/${maxCycles})`
: null,
});
}

Expand Down
2 changes: 1 addition & 1 deletion .github/workflows/codeql-analysis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ jobs:

steps:
- name: Checkout repository
uses: actions/checkout@v6
uses: actions/checkout@v7

# Initializes the CodeQL tools for scanning.
- name: Initialize CodeQL
Expand Down
Loading
Loading