improve agent CI matrix, token permissions, and waitlist follower backfill (#313)

* add workflows permission to git token and waitlist improvements

- add `workflows` to `InstallationTokenPermissions` type in both action and API token routes
- include `workflows: write` in the git token so agents can push workflow file changes
- add `githubFollowers` field to WaitlistSignup schema with migration
- add script to populate waitlist followers from GitHub API
- add frog-green-square-border logo asset

Co-authored-by: Cursor <cursoragent@cursor.com>

* improve CI, agent logging, token permissions, and delegation guardrails

- add format check and build step to root CI job
- standardize agent model/effort log lines across all agents
- fix GitHub App permissions types to match OpenAPI schema (workflows is write-only)
- improve delegation error message to prevent subagent recursion
- demote noisy OpenCode stderr to debug level
- add subagent delegation rules to resolved instructions

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix graphql partial error handling, update delegation message, add workflow_run fixtures

Co-authored-by: Cursor <cursoragent@cursor.com>

* remove module-level env var throws that break CI build

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix logging bug and type hole from PR review

- use batch-local notFound counter so per-batch log doesn't undercount
- add workflows to WorkflowTokenPermissions so wire type matches what action sends

Co-authored-by: Cursor <cursoragent@cursor.com>

* lazy-init appOctokit to fix next build without env vars

Co-authored-by: Cursor <cursoragent@cursor.com>

* drop pnpm build from CI test workflow

Co-authored-by: Cursor <cursoragent@cursor.com>

* fix delegate-effort test regex to match actual log format, disable fail-fast for agnostic tests

the test was matching `running \w+ with effort=auto` but the actual log
line from shared.ts is `» effort:  auto`. also temporarily set
fail-fast: false on action-agnostic so all failures surface at once.

Co-authored-by: Cursor <cursoragent@cursor.com>

* disable fail-fast in action workflow too, relax ci.test.ts to match

both workflow files now use fail-fast: false for agnostic tests so all
matrix jobs run to completion. the ci consistency test now checks that
the two workflows agree rather than requiring true.

Co-authored-by: Cursor <cursoragent@cursor.com>

* restore fail-fast: true now that all agnostic tests pass

Co-authored-by: Cursor <cursoragent@cursor.com>

* skip agent tests in CI when agent harness file didn't change

adds action/test/changed-agents.sh which reads the PR diff (via
dorny/paths-filter) and outputs only agents whose harness file was
modified. the action-agents matrix now uses this dynamic list instead
of a hardcoded array, so e.g. a PR touching only cursor.ts runs 6
jobs instead of 30.

Co-authored-by: Cursor <cursoragent@cursor.com>

* update ci.test.ts to validate dynamic agent matrix

the test now checks that the matrix references the changes job output
and that changed-agents.sh correctly discovers all agents.

Co-authored-by: Cursor <cursoragent@cursor.com>

* parallelize action jobs and use claude canary fallback for shared changes

runs action-agents in parallel with action-agnostic after root/changes, and updates changed-agents logic so shared or non-harness action runtime changes run only claude while harness-specific edits run only those changed agents.

Co-authored-by: Cursor <cursoragent@cursor.com>

---------

Co-authored-by: Cursor <cursoragent@cursor.com>
This commit is contained in:
Colin McDonnell
2026-02-16 22:36:29 +00:00
committed by pullfrog[bot]
parent 37dcea86b9
commit b6658ddbc1
15 changed files with 199 additions and 109 deletions
+25 -25
View File
@@ -72,7 +72,9 @@ export const opencode = agent({
const modelOverride = process.env.OPENCODE_MODEL;
if (modelOverride) {
args.push("--model", modelOverride);
log.info(`» using model override: ${modelOverride}`);
log.info(`» model: ${modelOverride} (override)`);
} else {
log.info(`» model: auto-selected by OpenCode`);
}
process.env.HOME = tempHome;
@@ -186,13 +188,10 @@ export const opencode = agent({
lastProviderError = providerError;
log.error(`» provider error detected (${providerError}): ${trimmed.substring(0, 500)}`);
} else {
// try to parse as JSON for structured logging, fall back to warning
try {
const parsed = JSON.parse(trimmed);
log.debug(JSON.stringify(parsed, null, 2));
} catch {
log.warning(trimmed);
}
// OpenCode's --print-logs output goes to stderr. demote internal
// INFO/DEBUG bus traffic to debug so it doesn't drown out tool
// call logs in the GitHub Actions step output.
log.debug(trimmed);
}
},
});
@@ -325,7 +324,7 @@ function configureOpenCode(ctx: AgentRunContext): void {
}
log.info(`» OpenCode config written to ${configPath}`);
log.info(
log.debug(
`» OpenCode permissions: edit=${permission.edit}, bash=${permission.bash}, webfetch=${permission.webfetch}`
);
log.debug(`OpenCode config contents:\n${configJson}`);
@@ -558,27 +557,28 @@ const messageHandlers = {
const status = event.part?.state?.status;
const output = event.part?.state?.output;
// debug log all tool_use events to diagnose missing bash/MCP tool calls
if (!toolName || !toolId) {
log.debug(`» tool_use event missing toolName or toolId: ${JSON.stringify(event)}`);
// surface dropped tool_use events visibly so missing tool calls are diagnosable
log.info(
`» tool_use event missing toolName or toolId: ${JSON.stringify(event).substring(0, 500)}`
);
return;
}
if (toolName && toolId) {
// track tool call in current step
if (stepHistory.length > 0) {
stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
}
// track tool call in current step
if (stepHistory.length > 0) {
stepHistory[stepHistory.length - 1].toolCalls.push(toolName);
}
thinkingTimer.markToolCall();
log.toolCall({
toolName,
input: parameters || {},
});
thinkingTimer.markToolCall();
log.toolCall({
toolName,
input: parameters || {},
});
// if tool already completed (status in same event), log output
if (status === "completed" && output) {
log.debug(` output: ${output}`);
}
// if tool already completed (status in same event), log output
if (status === "completed" && output) {
log.debug(` output: ${output}`);
}
},
tool_result: (event: OpenCodeToolResultEvent, thinkingTimer: ThinkingTimer) => {