Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
5f06e56
fix: check out task output branch for workspace dispatch
raphaeltm Jul 29, 2026
6ebee8c
fix(deploy): fail closed on workers.dev setup failure
raphaeltm Jul 29, 2026
b18ec91
task: archive safe output branch dispatch
raphaeltm Jul 29, 2026
21059fe
fix: keep wrangler sync env parity
raphaeltm Jul 29, 2026
4517b3e
chore: refresh CI after PR body correction
raphaeltm Jul 29, 2026
35d3510
task: add atomic bootstrap token redemption
raphaeltm Jul 29, 2026
a6cbc03
task: activate atomic bootstrap token redemption
raphaeltm Jul 29, 2026
1270432
fix: make bootstrap token redemption atomic
raphaeltm Jul 29, 2026
469ea02
test: cover bootstrap callback ledger path
raphaeltm Jul 29, 2026
17894bd
docs: reconcile worker secret inventory
raphaeltm Jul 29, 2026
f57e099
task: add vm-agent shutdown idempotency
raphaeltm Jul 29, 2026
26d5905
task: add configurable container max instances
raphaeltm Jul 29, 2026
0f1b825
fix: use integer bootstrap consume timestamps
raphaeltm Jul 29, 2026
b1fb159
task: activate strict CTO remediation integration
raphaeltm Jul 29, 2026
6733fb7
task: archive worker secret inventory drift
raphaeltm Jul 29, 2026
463ba42
task: activate vm-agent shutdown idempotency
raphaeltm Jul 29, 2026
5ea8560
task: mark container max instances active
raphaeltm Jul 29, 2026
3ae0ab6
task: add project data list row isolation
raphaeltm Jul 29, 2026
7e536e8
task: archive atomic bootstrap token redemption
raphaeltm Jul 29, 2026
9a7f524
Merge remote-tracking branch 'origin/pr-1689' into sam/execute-task-u…
raphaeltm Jul 29, 2026
1e50b55
chore: harden worker secret inventory check
raphaeltm Jul 29, 2026
6db3cad
fix: make vm-agent shutdown idempotent
raphaeltm Jul 29, 2026
88ea020
fix: make container max instances configurable
raphaeltm Jul 29, 2026
9a11ab9
fix: isolate malformed project data message rows
raphaeltm Jul 29, 2026
c9b69d4
chore: save agent work
raphaeltm Jul 29, 2026
3c78aaf
Merge remote-tracking branch 'origin/pr-1690' into sam/execute-task-u…
raphaeltm Jul 29, 2026
60a2ac1
fix: satisfy sonar for wrangler inventory check
raphaeltm Jul 29, 2026
1d5d061
task: archive vm-agent shutdown idempotency
raphaeltm Jul 29, 2026
2f79afc
task: archive container max instances config
raphaeltm Jul 29, 2026
6f2c607
task: archive project data row isolation
raphaeltm Jul 29, 2026
787b2db
chore: retrigger CI after PR body update
raphaeltm Jul 29, 2026
2ab2433
Merge remote-tracking branch 'origin/pr-1691' into sam/execute-task-u…
raphaeltm Jul 29, 2026
54648fe
Merge remote-tracking branch 'origin/pr-1692' into sam/execute-task-u…
raphaeltm Jul 29, 2026
48af270
Merge remote-tracking branch 'origin/pr-1693' into sam/execute-task-u…
raphaeltm Jul 29, 2026
968a0cc
Merge remote-tracking branch 'origin/pr-1694' into sam/execute-task-u…
raphaeltm Jul 29, 2026
caf2675
Merge remote-tracking branch 'origin/pr-1695' into sam/execute-task-u…
raphaeltm Jul 29, 2026
750460d
Merge remote-tracking branch 'origin/pr-1696' into sam/execute-task-u…
raphaeltm Jul 29, 2026
3f06b8d
task: update integration validation tracking
raphaeltm Jul 29, 2026
102125f
fix: avoid YAML anchors in deploy workflow
raphaeltm Jul 29, 2026
bf33e51
fix: reconcile remediation with current deployment config
raphaeltm Aug 5, 2026
1733035
Merge remote-tracking branch 'origin/main' into sam/execute-task-usin…
raphaeltm Aug 6, 2026
8e769c3
fix: pass deployedMigrationTag in container max_instances tests
raphaeltm Aug 6, 2026
d022d5f
task: add wrangler sync env-parity test coverage gap
raphaeltm Aug 6, 2026
da9e1b6
chore: retrigger CI with fresh PR payload after label removal
raphaeltm Aug 6, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions .claude/rules/07-env-and-urls.md
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@ The CI quality check (`pnpm quality:wrangler-bindings`) verifies:
1. No `[env.*]` sections exist in checked-in `wrangler.toml` files
2. All required binding types are present at the top level

### Required action when adding a Worker var consumed by sync-wrangler-config

If `scripts/deploy/sync-wrangler-config.ts` reads a GitHub Environment variable from `process.env` to generate Worker `[vars]`, add it to the centralized `wrangler_sync_env` mapping in `.github/workflows/deploy-reusable.yml`. Do not add ad hoc per-step sync env blocks. First deploys run the sync script twice (initial sync, then tail-consumer re-sync), and both invocations must receive identical optional Worker env inputs so the second sync cannot silently drop operator overrides or restore script defaults.

### Why this architecture

Wrangler does NOT inherit bindings (D1, KV, R2, DO, AI, tail_consumers) from top-level into `[env.*]` sections. Previously, this required manually duplicating every binding 3x (top-level + staging + production). Now the sync script generates complete env sections, eliminating duplication and making the config fork-friendly.
Expand Down
44 changes: 42 additions & 2 deletions .github/workflows/deploy-reusable.yml
Original file line number Diff line number Diff line change
Expand Up @@ -357,6 +357,7 @@ jobs:
BASE_DOMAIN: ${{ vars.BASE_DOMAIN }}
RESOURCE_PREFIX: ${{ steps.prefix.outputs.value }}
REQUIRE_APPROVAL: ${{ vars.REQUIRE_APPROVAL }}
SETUP_FORCE: ${{ vars.SETUP_FORCE }}
HETZNER_BASE_IMAGE: ${{ vars.HETZNER_BASE_IMAGE }}
PLATFORM_FEEDBACK_PROJECT_ID: ${{ vars.PLATFORM_FEEDBACK_PROJECT_ID }}
REPORT_ISSUE_TITLE_MAX_LENGTH: ${{ vars.REPORT_ISSUE_TITLE_MAX_LENGTH }}
Expand All @@ -373,11 +374,16 @@ jobs:
DO_MIGRATION_STATE_PROBE_RETRY_DELAY_MS: ${{ vars.DO_MIGRATION_STATE_PROBE_RETRY_DELAY_MS }}
CF_CONTAINER_ENABLED: ${{ vars.CF_CONTAINER_ENABLED }}
CF_CONTAINER_SLEEP_AFTER: ${{ vars.CF_CONTAINER_SLEEP_AFTER }}
CF_CONTAINER_ACTIVE_WORK_MAX_MS: ${{ vars.CF_CONTAINER_ACTIVE_WORK_MAX_MS }}
CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS: ${{ vars.CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS }}
CF_CONTAINER_PORT_READY_TIMEOUT_MS: ${{ vars.CF_CONTAINER_PORT_READY_TIMEOUT_MS }}
CF_CONTAINER_WAKE_TIMEOUT_MS: ${{ vars.CF_CONTAINER_WAKE_TIMEOUT_MS }}
CF_CONTAINER_RECOVERY_MAX_ATTEMPTS: ${{ vars.CF_CONTAINER_RECOVERY_MAX_ATTEMPTS }}
CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS: ${{ vars.CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS }}
CF_CONTAINER_CLONE_FILTER: ${{ vars.CF_CONTAINER_CLONE_FILTER }}
CF_CONTAINER_VM_AGENT_PORT: ${{ vars.CF_CONTAINER_VM_AGENT_PORT }}
SANDBOX_CONTAINER_MAX_INSTANCES: ${{ vars.SANDBOX_CONTAINER_MAX_INSTANCES }}
VM_AGENT_CONTAINER_MAX_INSTANCES: ${{ vars.VM_AGENT_CONTAINER_MAX_INSTANCES }}
SANDBOX_ENABLED: ${{ vars.SANDBOX_ENABLED }}
MAX_CONCURRENT_SETUP_SESSIONS: ${{ vars.MAX_CONCURRENT_SETUP_SESSIONS }}
SETUP_SESSION_TTL_MS: ${{ vars.SETUP_SESSION_TTL_MS }}
Expand Down Expand Up @@ -447,8 +453,10 @@ jobs:
elif [ "$HTTP_CODE" -eq 409 ]; then
echo "workers.dev subdomain already configured (OK)"
else
echo "::warning::Failed to set workers.dev subdomain (HTTP ${HTTP_CODE}): ${BODY}"
echo "Cron triggers may not work. Initialize manually: CF Dashboard > Workers & Pages > Settings > Domains & Routes"
echo "::error::Failed to set workers.dev subdomain (HTTP ${HTTP_CODE}): ${BODY}"
echo "Deployment cannot continue because Cloudflare cron triggers require the workers.dev subdomain prerequisite."
echo "Initialize manually: CF Dashboard > Workers & Pages > Settings > Domains & Routes"
exit 1
fi

# Verify Pages custom domain is active (must run BEFORE Worker deploys wildcard routes)
Expand Down Expand Up @@ -686,10 +694,42 @@ jobs:
BASE_DOMAIN: ${{ vars.BASE_DOMAIN }}
RESOURCE_PREFIX: ${{ steps.prefix.outputs.value }}
REQUIRE_APPROVAL: ${{ vars.REQUIRE_APPROVAL }}
SETUP_FORCE: ${{ vars.SETUP_FORCE }}
HETZNER_BASE_IMAGE: ${{ vars.HETZNER_BASE_IMAGE }}
PLATFORM_FEEDBACK_PROJECT_ID: ${{ vars.PLATFORM_FEEDBACK_PROJECT_ID }}
REPORT_ISSUE_TITLE_MAX_LENGTH: ${{ vars.REPORT_ISSUE_TITLE_MAX_LENGTH }}
REPORT_ISSUE_DESCRIPTION_MAX_LENGTH: ${{ vars.REPORT_ISSUE_DESCRIPTION_MAX_LENGTH }}
REPORT_ISSUE_CONTENT_MAX_LENGTH: ${{ vars.REPORT_ISSUE_CONTENT_MAX_LENGTH }}
RATE_LIMIT_REPORT_ISSUE_POST: ${{ vars.RATE_LIMIT_REPORT_ISSUE_POST }}
PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES: ${{ vars.PLATFORM_FEEDBACK_TRIAGE_WINDOW_MINUTES }}
PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT: ${{ vars.PLATFORM_FEEDBACK_TRIAGE_ERROR_LIMIT }}
PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT: ${{ vars.PLATFORM_FEEDBACK_TRIAGE_GROUP_LIMIT }}
PLATFORM_FEEDBACK_TRIAGE_EVIDENCE_LIMIT: ${{ vars.PLATFORM_FEEDBACK_TRIAGE_EVIDENCE_LIMIT }}
PLATFORM_FEEDBACK_TRIAGE_CLAIM_TTL_MS: ${{ vars.PLATFORM_FEEDBACK_TRIAGE_CLAIM_TTL_MS }}
ARTIFACTS_BINDING_ENABLED: ${{ vars.ARTIFACTS_BINDING_ENABLED }}
DO_MIGRATION_STATE_PROBE_ATTEMPTS: ${{ vars.DO_MIGRATION_STATE_PROBE_ATTEMPTS }}
DO_MIGRATION_STATE_PROBE_RETRY_DELAY_MS: ${{ vars.DO_MIGRATION_STATE_PROBE_RETRY_DELAY_MS }}
CF_CONTAINER_ENABLED: ${{ vars.CF_CONTAINER_ENABLED }}
CF_CONTAINER_SLEEP_AFTER: ${{ vars.CF_CONTAINER_SLEEP_AFTER }}
CF_CONTAINER_ACTIVE_WORK_MAX_MS: ${{ vars.CF_CONTAINER_ACTIVE_WORK_MAX_MS }}
CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS: ${{ vars.CF_CONTAINER_KEEPALIVE_RENEW_INTERVAL_MS }}
CF_CONTAINER_PORT_READY_TIMEOUT_MS: ${{ vars.CF_CONTAINER_PORT_READY_TIMEOUT_MS }}
CF_CONTAINER_WAKE_TIMEOUT_MS: ${{ vars.CF_CONTAINER_WAKE_TIMEOUT_MS }}
CF_CONTAINER_RECOVERY_MAX_ATTEMPTS: ${{ vars.CF_CONTAINER_RECOVERY_MAX_ATTEMPTS }}
CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS: ${{ vars.CF_CONTAINER_CREATE_WORKSPACE_TIMEOUT_MS }}
CF_CONTAINER_CLONE_FILTER: ${{ vars.CF_CONTAINER_CLONE_FILTER }}
CF_CONTAINER_VM_AGENT_PORT: ${{ vars.CF_CONTAINER_VM_AGENT_PORT }}
SANDBOX_CONTAINER_MAX_INSTANCES: ${{ vars.SANDBOX_CONTAINER_MAX_INSTANCES }}
VM_AGENT_CONTAINER_MAX_INSTANCES: ${{ vars.VM_AGENT_CONTAINER_MAX_INSTANCES }}
SANDBOX_ENABLED: ${{ vars.SANDBOX_ENABLED }}
MAX_CONCURRENT_SETUP_SESSIONS: ${{ vars.MAX_CONCURRENT_SETUP_SESSIONS }}
SETUP_SESSION_TTL_MS: ${{ vars.SETUP_SESSION_TTL_MS }}
SETUP_SESSION_CAPTURE_POLL_MS: ${{ vars.SETUP_SESSION_CAPTURE_POLL_MS }}
CODEX_DEVICE_AUTH_REQUEST_TIMEOUT_MS: ${{ vars.CODEX_DEVICE_AUTH_REQUEST_TIMEOUT_MS }}
SETUP_SESSION_SWEEP_MAX_CANDIDATES: ${{ vars.SETUP_SESSION_SWEEP_MAX_CANDIDATES }}
POOL_LEASE_BUFFER_MS: ${{ vars.POOL_LEASE_BUFFER_MS }}
SANDBOX_EXEC_TIMEOUT_MS: ${{ vars.SANDBOX_EXEC_TIMEOUT_MS }}
SANDBOX_VM_AGENT_PORT: ${{ vars.SANDBOX_VM_AGENT_PORT }}

- name: Re-deploy API Worker (with tail_consumers)
if: ${{ inputs.dry_run != true && steps.first_deploy.outputs.is_first == 'true' }}
Expand Down
8 changes: 8 additions & 0 deletions apps/api/src/db/migrations/0105_bootstrap_token_consumes.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
CREATE TABLE IF NOT EXISTS bootstrap_token_consumes (
token_hash TEXT PRIMARY KEY NOT NULL,
created_at INTEGER NOT NULL,
expires_at INTEGER NOT NULL,
consumed_at INTEGER
);
CREATE INDEX IF NOT EXISTS idx_bootstrap_token_consumes_expiry ON bootstrap_token_consumes(expires_at);
CREATE INDEX IF NOT EXISTS idx_bootstrap_token_consumes_consumed ON bootstrap_token_consumes(consumed_at);
35 changes: 32 additions & 3 deletions apps/api/src/durable-objects/project-data/messages.ts
Original file line number Diff line number Diff line change
Expand Up @@ -346,7 +346,7 @@
return size;
}

export function getMessages(

Check failure on line 349 in apps/api/src/durable-objects/project-data/messages.ts

View check run for this annotation

SonarQubeCloud / SonarCloud Code Analysis

Refactor this function to reduce its Cognitive Complexity from 21 to the 15 allowed.

See more on https://sonarcloud.io/project/issues?id=raphaeltm_simple-agent-manager&issues=AZ-vm66b5-endHUTTbIg&open=AZ-vm66b5-endHUTTbIg&pullRequest=1697
sql: SqlStorage,
sessionId: string,
limit: number = 1000,
Expand Down Expand Up @@ -406,10 +406,39 @@
const trimmedRows = candidateRows.slice(0, safeCount);

const orderedRows = order === 'desc' ? trimmedRows.reverse() : trimmedRows;
const messages: Record<string, unknown>[] = [];
let skipped = 0;

for (const row of orderedRows) {
try {
messages.push(
compact ? parseChatMessageRowCompact(row, compactOptions) : parseChatMessageRow(row)
);
} catch (e) {
skipped++;
log.warn('messages.list_row_skipped', {
rowId: typeof row.id === 'string' ? row.id : null,
rowSessionId: typeof row.session_id === 'string' ? row.session_id : null,
requestedSessionId: sessionId,
compact,
error: String(e),
});
}
}

if (skipped > 0) {
log.warn('messages.list_degraded', {
sessionId,
requestedLimit: limit,
fetched: rows.length,
returned: messages.length,
skipped,
compact,
});
}

return {
messages: orderedRows.map((row) =>
compact ? parseChatMessageRowCompact(row, compactOptions) : parseChatMessageRow(row)
),
messages,
hasMore,
};
}
Expand Down
10 changes: 5 additions & 5 deletions apps/api/src/durable-objects/task-runner/workspace-steps.ts
Original file line number Diff line number Diff line change
Expand Up @@ -404,15 +404,15 @@ async function createWorkspaceOnVmAgent(
id: state.projectId,
repoProvider: projectRepo?.repoProvider ?? 'github',
});
const checkoutBranch = state.config.outputBranch || state.config.branch;
const baseBranch =
checkoutBranch === state.config.branch ? state.config.defaultBranch : state.config.branch;

const response = await createWorkspaceOnNode(nodeId, rc.env, state.userId, {
workspaceId,
repository: state.config.repository,
branch: state.config.branch,
baseBranch:
state.config.branch === state.config.outputBranch
? state.config.defaultBranch
: state.config.branch,
branch: checkoutBranch,
baseBranch,
defaultBranch: state.config.defaultBranch || 'main',
repoProvider: gitSource.repoProvider,
cloneUrl: gitSource.cloneUrl,
Expand Down
7 changes: 7 additions & 0 deletions apps/api/src/routes/workspaces/runtime.ts
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,7 @@ import {
MessageBatchSchema,
} from '../../schemas';
import { appendBootLog } from '../../services/boot-log';
import { registerBootstrapTokenConsume } from '../../services/bootstrap';
import { syncActiveAgentCredentialSecret } from '../../services/composable-credentials/agent-sync';
import { decrypt, encrypt } from '../../services/encryption';
import { getInstallationToken, getUserInstallationRepositories } from '../../services/github-app';
Expand Down Expand Up @@ -1568,6 +1569,12 @@ runtimeRoutes.post('/:id/bootstrap-token', requireAuth(), requireApproved(), asy
createdAt: now,
};

await registerBootstrapTokenConsume(
c.env.DATABASE,
bootstrapToken,
new Date(Date.now() + 60 * 1000).toISOString()
);

await c.env.KV.put(`bootstrap:${bootstrapToken}`, JSON.stringify(data), {
expirationTtl: 60,
});
Expand Down
Loading
Loading