mirror of
https://github.com/stablyai/orca.git
synced 2026-10-30 16:47:34 +00:00
* feat(relay): watch a reserve cell and flip it back on the first step 5 stop rule A manual, inert-until-dispatched workflow samples one reserve cell every minute for up to 300 minutes against the step 5 §9 stop rules, logs one JSON line per sample, and on the first trip (or N unreadable samples in a row) runs the flag tool's own admitMode=db write under the flag workflow's concurrency group. dry-run logs the flag tool's plan instead. Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): keep the reserve watch's flip back out of the flag group, and baseline 503s before the flip - The flip back holds no concurrency group: GitHub keeps one pending run per group and a newer one cancels it. The flag tool's CAS orders it instead; a generation conflict is re-read and retried (5 tries, backoff) while the switch file still says reserve. - The 503 background comes from the 10 minutes before a required flipped-at, or an explicit value; neither is refused. - A failed or cancelled write-mode watch flips back too. An unreachable cell fails with the exact break-glass inputs. - Missing 503 data (empty series, unpublished minute, unparseable time) is unverifiable. - Seat-behind-row key adds the director instance and cell; six segments share one command. Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): read the reserve watch's 503 publishing front from every response code A quiet fleet has no 503 series, so the watch read it as unverifiable and flipped a healthy cell back within minutes. The 503 read now groups request_count by response code in one query; the newest minute any code has a point for is how far the series is published, so a 503 minute at or before it with no point is zero and minutes past it are left out. Also: the flip-back job's steps run on always(), flipped-at ages against the watch's start in later segments, a pending pre-flip background no longer adds to the unverifiable streak, the flip back retries runtime-status before calling for break glass, and the concurrency rationale lives once in the docs. Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * docs(relay): state what a cancel during the flip-back lease wait can leave, and the 503 publish-skew assumption Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): fail a segment closed on a corrupt watch state file, and keep no checkout token on disk Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): alarm on a failed flip back, refuse a watch under a rollout lease, wait for reserve to apply, and control the duplicate read - A flip back whose write or plan never finished (no ID token, a failed login, a silent cell) raises an ::error:: annotation and a FLIP BACK FAILED summary, as the clock-skew monitor does. - The first segment reads the Cloud SQL rollout lease through the lease action's own reader and refuses to start while any run holds it, or when it cannot be read. - A cell not yet reporting reserve is waited on for up to 10 minutes, judging nothing, before the watch ends refused without a write. - The duplicate-seat read takes the per-minute ledger summary in the same query and window; with no fresh summary in it the sample is unverifiable, not zero duplicates. Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): page on a refused reserve watch or a failed flip back A refused watch can leave a cell in reserve and unwatched, the same as a flip back that did not land. Both now raise an escaped ::error:: annotation through one `alarm` command, and in write mode write orca_relay_reserve_watch_alarm with gcloud logging write. A new log-based metric and alert policy page on any such line, per cell, through relay_alert_notification_channels, and the deploy identity gets roles/logging.logWriter in production to write it. Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * fix(relay): name the log-writer risk, check the alarm's project, and say a failed login cannot page Claude-Session: f2fe8cb5-4467-4a25-8aab-98598b70791f * docs(relay): note a force-cancel can skip the reserve-watch flip-back write
83 lines
3.7 KiB
JavaScript
83 lines
3.7 KiB
JavaScript
import assert from 'node:assert/strict'
|
|
import test from 'node:test'
|
|
import {
|
|
isEntrypoint,
|
|
jobIf,
|
|
jobNeeds,
|
|
jobs,
|
|
readWorkflow,
|
|
workflowFiles
|
|
} from './cloud-sql-rollout-lock-census.mjs'
|
|
import { relayWorkflowFile } from './relay-repository.mjs'
|
|
|
|
// Why: this repository publishes the relay's operate surface next to the desktop app. Three
|
|
// invariants make that safe, and each of them is one careless edit away from being lost.
|
|
const OPERATIONS_GATE = "vars.ORCA_CLOUD_OPERATIONS_ENABLED == 'true'"
|
|
|
|
// Cloud Verify is the only cloud workflow that must run on every pull request.
|
|
const UNGATED = relayWorkflowFile('verify.yml')
|
|
|
|
const relayWorkflows = () => workflowFiles().filter((file) => file !== UNGATED)
|
|
|
|
test('the copy carries every relay workflow', () => {
|
|
assert.equal(relayWorkflows().length, 28)
|
|
})
|
|
|
|
// Why: workflow_run chains match by display name, not filename. Renaming a file is safe; renaming
|
|
// one of these silently breaks the recovery chain with no failing run to notice.
|
|
test('the recovery chain keeps the display names it is matched by', () => {
|
|
const names = Object.fromEntries(
|
|
['prove-relay-staging-capacity.yml', 'recover-relay-staging-c4-image.yml', 'requeue-relay-staging-c4-recovery.yml'].map(
|
|
(name) => [name, /^name: (.+)$/m.exec(readWorkflow(relayWorkflowFile(name)))?.[1]]
|
|
)
|
|
)
|
|
assert.deepEqual(names, {
|
|
'prove-relay-staging-capacity.yml': 'Prove Relay Staging Capacity',
|
|
'recover-relay-staging-c4-image.yml': 'Recover Relay Staging C4 Image',
|
|
'requeue-relay-staging-c4-recovery.yml': 'Requeue Relay Staging C4 Recovery'
|
|
})
|
|
const recover = readWorkflow(relayWorkflowFile('recover-relay-staging-c4-image.yml'))
|
|
const requeue = readWorkflow(relayWorkflowFile('requeue-relay-staging-c4-recovery.yml'))
|
|
assert.ok(recover.includes(`workflows: [${names['prove-relay-staging-capacity.yml']}]`))
|
|
assert.ok(requeue.includes(`workflows: [${names['recover-relay-staging-c4-image.yml']}]`))
|
|
})
|
|
|
|
// Why: this repository holds none of the GCP credentials these workflows would need. Every one
|
|
// authenticates through Workload Identity read from a variable, so any repository secret other
|
|
// than the automatic token would be a credential the owner has to store here.
|
|
test('no cloud workflow reads a repository secret', () => {
|
|
for (const file of workflowFiles()) {
|
|
for (const [, name] of readWorkflow(file).matchAll(/secrets\.([A-Za-z_][A-Za-z0-9_]*)/g)) {
|
|
assert.equal(name, 'GITHUB_TOKEN', `${file} reads secrets.${name}`)
|
|
}
|
|
}
|
|
})
|
|
|
|
// Why: the operations gate is what makes the whole surface inert until the owner enables it. A
|
|
// job that can start without a gated dependency would run the moment someone dispatches it.
|
|
test('every job that can start on its own is gated on the operations variable', () => {
|
|
const reachable = []
|
|
for (const file of relayWorkflows()) {
|
|
const text = readWorkflow(file)
|
|
if (!isEntrypoint(text)) continue
|
|
for (const job of jobs(text)) {
|
|
if (jobNeeds(job.text).length > 0) continue
|
|
reachable.push(`${file}:${job.id}`)
|
|
assert.ok(jobIf(job.text).includes(OPERATIONS_GATE), `${file}:${job.id} is not gated`)
|
|
}
|
|
}
|
|
assert.ok(reachable.length >= 20, `only ${reachable.length} root jobs were checked`)
|
|
})
|
|
|
|
// Why: reusable jobs inherit the caller's gate. Gating them again would be dead configuration
|
|
// that reads as protection, and every caller is already checked above.
|
|
test('reusable workflows carry no gate of their own', () => {
|
|
for (const file of relayWorkflows()) {
|
|
const text = readWorkflow(file)
|
|
if (isEntrypoint(text)) continue
|
|
for (const job of jobs(text)) {
|
|
assert.ok(!jobIf(job.text).includes(OPERATIONS_GATE), `${file}:${job.id} regates a reusable job`)
|
|
}
|
|
}
|
|
})
|