Rename CI state vocabulary
Aligns runs and jobs with standard CI terms: pending→queued,
complete→succeeded, superseded→canceled. Also drops the unused
pending/skipped/aborted job states from the CHECK — only active,
succeeded, and failed have producers today.
Assisted-by: Claude Opus 4.7 via Claude Code
diff --git a/docs/CI-STATE.md b/docs/CI-STATE.md
index 0791cb5..bd74d04 100644
--- a/docs/CI-STATE.md
+++ b/docs/CI-STATE.md
@@ -15,26 +15,25 @@ A run owns its jobs; jobs FK on `(run_id, job_id)` and cascade delete.
```mermaid
stateDiagram-v2
- [*] --> pending : Runs.create
+ [*] --> queued : Runs.create
- pending --> active : bootstrap endpoint
- pending --> superseded : supersede_existing
- pending --> failed : reconcile_orphans
+ queued --> active : bootstrap endpoint
+ queued --> canceled : cancel_existing
+ queued --> failed : reconcile_orphans
- active --> complete : transition Complete
- active --> failed : pipeline-failure
- active --> failed : process-crashed
- active --> superseded : supersede_existing
- active --> failed : reconcile_orphans
+ active --> succeeded : transition Succeeded
+ active --> failed : pipeline-failure
+ active --> failed : process-crashed
+ active --> canceled : cancel_existing
+ active --> failed : reconcile_orphans
- complete --> [*]
- failed --> [*]
- superseded --> [*]
+ succeeded --> [*]
+ failed --> [*]
+ canceled --> [*]
- note right of pending
+ note right of queued
started_at_ms IS NULL
finished_at_ms IS NULL
- container_id IS NULL
end note
note right of active
@@ -42,9 +41,8 @@ stateDiagram-v2
finished_at_ms still NULL
end note
- note right of complete
+ note right of succeeded
started_at_ms, finished_at_ms set
- container_id cleared
end note
```
@@ -52,36 +50,36 @@ stateDiagram-v2
| From → To | Where | When | `failure_kind` |
| --- | --- | --- | --- |
-| `[*] → pending` | `Runs::create` (`quire-server/src/ci/run.rs:103`) | A push event arrives and a `runs` row is inserted. | — |
-| `pending → active` | Bootstrap endpoint (`api.rs`), called when `quire-ci` fetches bootstrap data | `quire-ci` connects to the server and marks the run active. Stamps `started_at_ms`. | — |
-| `active → complete` | `Run::transition`, called from `Run::execute` | `quire-ci` exited 0 and `RunFinished { outcome: Success }` was ingested. Stamps `finished_at_ms`, clears `container_id`. | — |
+| `[*] → queued` | `Runs::create` (`quire-server/src/ci/run.rs`) | A push event arrives and a `runs` row is inserted. | — |
+| `queued → active` | Bootstrap endpoint (`api.rs`), called when `quire-ci` fetches bootstrap data | `quire-ci` connects to the server and marks the run active. Stamps `started_at_ms`. | — |
+| `active → succeeded` | `Run::transition`, called from `Run::execute` | `quire-ci` exited 0 and `RunFinished { outcome: Succeeded }` was ingested. Stamps `finished_at_ms`. | — |
| `active → failed` | `Run::execute` | `quire-ci` exited 0 and `RunFinished { outcome: PipelineFailure }` was ingested — a job's run-fn returned an error. | `"pipeline-failure"` |
| `active → failed` | `Run::execute` | `quire-ci` exited non-zero, or exited 0 but emitted no `RunFinished` event (process crash or panic). | `"process-crashed"` |
-| `{pending, active} → superseded` | `Runs::supersede_existing` (`run.rs:155`) via raw SQL, **bypassing `transition`** | A new `Runs::create` for the same `(repo, ref)` arrived. Both pending and active rows are flipped directly. | — |
-| `{pending, active} → failed` | `reconcile_orphans` (`run.rs:205`) via raw SQL, **bypassing `transition`** | Startup-time cleanup of rows left behind by a previous `quire serve` instance. | `"orphaned"` |
+| `{queued, active} → canceled` | `Runs::cancel_existing` via raw SQL, **bypassing `transition`** | A new `Runs::create` for the same `(repo, ref)` arrived. Both queued and active rows are flipped directly. | — |
+| `{queued, active} → failed` | `reconcile_orphans` via raw SQL, **bypassing `transition`** | Startup-time cleanup of rows left behind by a previous `quire serve` instance. | `"orphaned"` |
`Run::transition(to, failure_kind)`'s allowed-transition match:
```
-(Pending, Active) | (Pending, Complete) | (Pending, Superseded) |
-(Active, Complete) | (Active, Failed) | (Active, Superseded)
+(Queued, Active) | (Queued, Succeeded) | (Queued, Canceled) |
+(Active, Succeeded) | (Active, Failed) | (Active, Canceled)
```
-In practice only `(Active, Complete)` and `(Active, Failed)` are exercised via `transition` — the `Pending → Active` edge is owned by the bootstrap endpoint (api.rs), and the supersede edges go through raw SQL (`supersede_existing`), not `transition`. The other edges are gated for defensive consistency, in case a future caller routes supersede through the typed API. Anything else — `Pending → Failed`, `Active → Pending`, or any transition out of a terminal state — returns `InvalidTransition`.
+In practice only `(Active, Succeeded)` and `(Active, Failed)` are exercised via `transition` — the `Queued → Active` edge is owned by the bootstrap endpoint (api.rs), and the cancel edges go through raw SQL (`cancel_existing`), not `transition`. The other edges are gated for defensive consistency, in case a future caller routes cancellation through the typed API. Anything else — `Queued → Failed`, `Active → Queued`, or any transition out of a terminal state — returns `InvalidTransition`.
-`failure_kind` is recorded only when `to == Failed`; it's ignored for `Active`, `Complete`, and `Superseded`.
+`failure_kind` is recorded only when `to == Failed`; it's ignored for `Active`, `Succeeded`, and `Canceled`.
### Database invariants
-The DB enforces shape per state via a `CHECK` constraint (see `migrations/0007_schema_cleanup.sql`):
+The DB enforces shape per state via a `CHECK` constraint (see `migrations/0009_rename_ci_vocab.sql`):
| State | `started_at_ms` | `finished_at_ms` |
| --- | --- | --- |
-| `pending` | NULL | NULL |
+| `queued` | NULL | NULL |
| `active` | set | NULL |
-| `complete` | set | set |
+| `succeeded` | set | set |
| `failed` | (any) | set |
-| `superseded` | (any) | set |
+| `canceled` | (any) | set |
Plus monotonicity: `started_at_ms >= queued_at_ms`, `finished_at_ms >= started_at_ms`. `started_at_ms`, `finished_at_ms`, and `failure_kind` are stamped at most once each, via `COALESCE` in the `UPDATE`.
@@ -95,7 +93,7 @@ Nullable column populated by `Run::transition` when entering `Failed`, plus `rec
| `"process-crashed"` | `Run::execute`: `quire-ci` exited non-zero, or exited 0 but never emitted a `RunFinished` event (panic or unexpected termination). |
| `"orphaned"` | `reconcile_orphans` on startup. |
-Successful and superseded runs leave `failure_kind` NULL. The set is open — UI consumers should not assume it's exhaustive.
+Succeeded and canceled runs leave `failure_kind` NULL. The set is open — UI consumers should not assume it's exhaustive.
## Job state machine
@@ -103,43 +101,35 @@ Successful and superseded runs leave `failure_kind` NULL. The set is open — UI
```mermaid
stateDiagram-v2
- [*] --> complete : JobFinished complete
- [*] --> failed : JobFinished failed
+ [*] --> succeeded : JobFinished succeeded
+ [*] --> failed : JobFinished failed
- complete --> [*]
- failed --> [*]
+ succeeded --> [*]
+ failed --> [*]
- pending --> [*] : no producer yet
- active --> [*] : no producer yet
- skipped --> [*] : no producer yet
- aborted --> [*] : no producer yet
+ active --> [*] : no producer yet
```
### Transitions in code
-There is only one writer of `jobs` rows: `Run::ingest_events` (`run.rs:399`). It reads `events.jsonl` after the `quire-ci` subprocess exits and, for each `JobStarted`/`JobFinished` pair, inserts **one row directly in the terminal state**. The intermediate `active` state is held in an in-memory `pending_jobs` map during ingest and never persisted.
+There is only one writer of `jobs` rows: `Run::ingest_events`. It reads `events.jsonl` after the `quire-ci` subprocess exits and, for each `JobStarted`/`JobFinished` pair, inserts **one row directly in the terminal state**. The intermediate `active` state is held in an in-memory `inflight_jobs` map during ingest and never persisted.
| From → To | Where | When |
| --- | --- | --- |
-| `[*] → complete` | `Run::ingest_events` (`run.rs:399`) | `JobFinished { outcome: complete }` paired with a buffered `JobStarted`. |
+| `[*] → succeeded` | `Run::ingest_events` | `JobFinished { outcome: succeeded }` paired with a buffered `JobStarted`. |
| `[*] → failed` | `Run::ingest_events` | `JobFinished { outcome: failed }` paired with a buffered `JobStarted`. |
Consequence: while `quire-ci` is running, **no `jobs` rows exist for this run**. They all materialize at ingest time. Live progress is visible via `events.jsonl` or per-`sh` log files on disk, not via SQL.
### Database invariants
-`migrations/0001_initial.sql` allows six job states (`pending`, `active`, `complete`, `failed`, `skipped`, `aborted`) with these shape rules:
+`migrations/0009_rename_ci_vocab.sql` allows three job states (`active`, `succeeded`, `failed`) with these shape rules:
| State | `started_at_ms` | `finished_at_ms` |
| --- | --- | --- |
-| `pending` | NULL | NULL |
| `active` | set | NULL |
-| `complete` | set | set |
+| `succeeded` | set | set |
| `failed` | set | set |
-| `skipped` | NULL | set |
-| `aborted` | (any) | set |
-
-`skipped` carries `finished_at_ms` but not `started_at_ms` — the row exists to record "this job never ran" with a timestamp anchoring it to the run.
### Stop-on-first-failure inside `quire-ci`
@@ -152,7 +142,7 @@ if let Err(e) = result {
}
```
-`JobStarted`/`JobFinished` are only emitted for jobs that actually ran. **Jobs downstream of the failure produce no events, so no `jobs` row at all** — not `skipped`, not anything. See Gaps below.
+`JobStarted`/`JobFinished` are only emitted for jobs that actually ran. **Jobs downstream of the failure produce no events, so no `jobs` row at all.** See Gaps below.
## Event flow: Process executor
@@ -180,8 +170,8 @@ sequenceDiagram
Run->>Run: ingest_events(events.jsonl)
Run->>DB: INSERT jobs (pass 1)
Run->>DB: INSERT sh (pass 2)
- alt RunFinished(Success) + exit 0
- Run->>DB: UPDATE runs SET state='complete'
+ alt RunFinished(Succeeded) + exit 0
+ Run->>DB: UPDATE runs SET state='succeeded'
else RunFinished(PipelineFailure) + exit 0
Run->>DB: UPDATE runs SET state='failed' (failure_kind='pipeline-failure')
else exit nonzero or no RunFinished
@@ -192,7 +182,7 @@ sequenceDiagram
Wire events (`quire-core/src/ci/event.rs`):
* `JobStarted { job_id }`
-* `JobFinished { job_id, outcome: complete | failed }` — `JobOutcome` is the closed set, not the full job-state enum.
+* `JobFinished { job_id, outcome: succeeded | failed }` — `JobOutcome` is the closed set, not the full job-state enum.
* `ShStarted { job_id, cmd }` / `ShFinished { job_id, exit_code }`
`Run::ingest_events` reads the file in two passes (jobs first to satisfy the FK on `(run_id, job_id)`, then sh). Ingest failures are logged but never demote the run's own outcome — a partial DB write is preferable to losing the pass/fail signal.
@@ -213,12 +203,12 @@ sequenceDiagram
Hook->>Listener: PushEvent JSON over /var/quire/server.sock
Listener->>Trigger: trigger(quire, &event)
loop per updated ref
- Trigger->>DB: supersede_existing (Pending|Active → Superseded for same repo/ref)
- Trigger->>DB: INSERT runs (state=pending)
+ Trigger->>DB: cancel_existing (Queued|Active → Canceled for same repo/ref)
+ Trigger->>DB: INSERT runs (state=queued)
Trigger->>FS: create run dir + workspace
Trigger->>FS: git archive | tar -x (materialize workspace)
Trigger->>Exec: execute()
- Exec->>DB: active → complete|failed (via bootstrap endpoint + ingest_events)
+ Exec->>DB: active → succeeded|failed (via bootstrap endpoint + ingest_events)
end
```
@@ -234,15 +224,15 @@ Two things in `CI.md` that the code does *not* yet implement at this layer:
| Column | Written by | Read by |
| --- | --- | --- |
| `id` | `Runs::create` | everywhere |
-| `repo` | `Runs::create` | `supersede_existing`, web handlers |
-| `ref_name` | `Runs::create` | `supersede_existing`, web handlers, bootstrap response |
+| `repo` | `Runs::create` | `cancel_existing`, web handlers |
+| `ref_name` | `Runs::create` | `cancel_existing`, web handlers, bootstrap response |
| `sha` | `Runs::create` | `read_meta`, bootstrap response, web handlers |
| `pushed_at_ms` | `Runs::create` | `read_meta`, web handlers |
-| `state` | `Runs::create` (→ `pending`) + every transition | everywhere |
+| `state` | `Runs::create` (→ `queued`) + every transition | everywhere |
| `failure_kind` | `Run::transition(Failed, …)`, `reconcile_orphans` | web handlers |
| `queued_at_ms` | `Runs::create` | web handlers |
-| `started_at_ms` | `transition(Active)`, also stamped as fallback in `Complete/Failed/Superseded` | `read_started_at`, web handlers |
-| `finished_at_ms` | `transition(Complete/Failed/Superseded)` | `read_finished_at`, web handlers |
+| `started_at_ms` | `transition(Active)`, also stamped as fallback in `Succeeded/Failed/Canceled` | `read_started_at`, web handlers |
+| `finished_at_ms` | `transition(Succeeded/Failed/Canceled)` | `read_finished_at`, web handlers |
| `run_token` | `Runs::create` (API sessions only) | `verify_run_token` middleware |
| `git_dir` | `Run::store_bootstrap_data` (API sessions only) | bootstrap endpoint |
| `traceparent` | `Run::store_bootstrap_data` (API sessions only) | bootstrap endpoint |
@@ -253,7 +243,7 @@ Migration 0007 dropped eight columns that carried no live data with the Process
All six columns (`run_id`, `job_id`, `state`, `exit_code`, `started_at_ms`, `finished_at_ms`) are written by `Run::ingest_events` and read by the web detail view. All live.
-The schema permits six states (`pending`, `active`, `complete`, `failed`, `skipped`, `aborted`) but `ingest_events` only writes `complete` and `failed`. The other four states have no producer today — see Gaps below.
+The schema permits three states (`active`, `succeeded`, `failed`) but `ingest_events` only writes `succeeded` and `failed`. `active` has no producer today — see Gaps below.
### `sh` table
@@ -266,9 +256,7 @@ States the schema admits — or `CI.md` commits to — that no code path produce
| Gap | Schema/spec | Producer needed |
| --- | --- | --- |
| Job `active` rows during execution | Schema-allowed | `ingest_events` inserts one row per job at JobFinished time. While `quire-ci` is running, the `jobs` table has nothing for this run. Live UI of "currently running job" needs an active-row writer — either eager ingest, or a separate writer inside `quire-ci`. |
-| Job `pending` rows | Schema-allowed | Useful for "queued jobs in topo order" UI. Today jobs go straight from no-row to a terminal row. |
-| Job `skipped` rows for dependents of a failed job | Schema-allowed | `quire-ci`'s loop `break`s on first failure and emits no events for downstream jobs. To populate `skipped`, either `quire-ci` would emit `JobSkipped` events for unrun topo-order jobs, or the ingester would compute them from the pipeline graph + the surviving JobFinished rows. |
-| Job `aborted` rows | Schema-allowed | Needed when a run is killed mid-flight — e.g. by `supersede_existing` `docker kill`-ing the container. Today the run row flips to `superseded`, but no `jobs` rows are written for the work that was in flight. |
+| Job `skipped` outcome for dependents of a failed job | Tracked in ranger `wwpxzuvq` | `quire-ci`'s loop `break`s on first failure and emits no events for downstream jobs. Would need `skipped` re-added to the jobs CHECK constraint; producer would emit `JobSkipped` events from `quire-ci` or compute them in the ingester from the pipeline graph. |
| `:allow-failure` job flag | Documented in `CI.md` as v1 | Not implemented anywhere in `quire-core`, `quire-ci`, or `quire-server`. The structural validator doesn't recognize the key; the executor treats every job error as fatal. |
| Queue + Notify wakeup | `CI.md` "Communication" section | `trigger` runs synchronously on the listener task. No queue scan, no Notify, no separate runner task. |
diff --git a/quire-ci/src/main.rs b/quire-ci/src/main.rs
index 29066a2..5179ad3 100644
--- a/quire-ci/src/main.rs
+++ b/quire-ci/src/main.rs
@@ -520,7 +520,7 @@ fn run_pipeline(
sink.emit(Event {
at_ms: jiff::Timestamp::now().as_millisecond(),
kind: EventKind::RunFinished {
- outcome: RunOutcome::Success,
+ outcome: RunOutcome::Succeeded,
},
})
.expect("emit run_finished");
@@ -613,7 +613,7 @@ fn run_pipeline(
runtime.leave_job();
let outcome = if result.is_ok() {
- JobOutcome::Complete
+ JobOutcome::Succeeded
} else {
JobOutcome::Failed
};
@@ -642,7 +642,7 @@ fn run_pipeline(
tracing::warn!(job = %job_id, error = err as &(dyn std::error::Error + 'static), "job run-fn failed");
RunOutcome::PipelineFailure
} else {
- RunOutcome::Success
+ RunOutcome::Succeeded
};
sink.borrow_mut()
diff --git a/quire-core/src/ci/event.rs b/quire-core/src/ci/event.rs
index 5c56e86..9a2db15 100644
--- a/quire-core/src/ci/event.rs
+++ b/quire-core/src/ci/event.rs
@@ -15,15 +15,15 @@ use serde::{Deserialize, Serialize};
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum JobOutcome {
- Complete,
+ Succeeded,
Failed,
}
-/// Outcome of the complete pipeline run, carried by [`EventKind::RunFinished`].
+/// Outcome of the pipeline run, carried by [`EventKind::RunFinished`].
#[derive(Clone, Copy, Debug, Serialize, Deserialize, PartialEq, Eq)]
#[serde(rename_all = "snake_case")]
pub enum RunOutcome {
- Success,
+ Succeeded,
PipelineFailure,
}
@@ -42,7 +42,7 @@ pub struct Event {
pub enum EventKind {
/// A job's run-fn is about to fire.
JobStarted { job_id: String },
- /// A job's run-fn returned. `outcome` is `complete` if the run-fn
+ /// A job's run-fn returned. `outcome` is `succeeded` if the run-fn
/// returned `Ok`, else `failed`.
JobFinished { job_id: String, outcome: JobOutcome },
/// An sh process is about to spawn.
@@ -80,13 +80,13 @@ mod tests {
at_ms: 250,
kind: EventKind::JobFinished {
job_id: "build".into(),
- outcome: JobOutcome::Complete,
+ outcome: JobOutcome::Succeeded,
},
};
let json = serde_json::to_string(&event).unwrap();
assert_eq!(
json,
- r#"{"at_ms":250,"type":"job_finished","job_id":"build","outcome":"complete"}"#
+ r#"{"at_ms":250,"type":"job_finished","job_id":"build","outcome":"succeeded"}"#
);
}
diff --git a/quire-server/migrations/0009_rename_ci_vocab.sql b/quire-server/migrations/0009_rename_ci_vocab.sql
new file mode 100644
index 0000000..1087489
--- /dev/null
+++ b/quire-server/migrations/0009_rename_ci_vocab.sql
@@ -0,0 +1,94 @@
+-- Rename CI state vocabulary on both runs and jobs.
+--
+-- runs: pending → queued, complete → succeeded, superseded → canceled
+-- jobs: complete → succeeded, plus drop the unused pending/skipped/aborted
+-- states from the CHECK constraint (no producer writes them today;
+-- skipped support is tracked separately and would re-add it with a
+-- real producer).
+--
+-- SQLite can't ALTER CHECK constraints, so both tables are rebuilt. The
+-- INSERT…SELECT rewrites the state column inline. Pattern matches
+-- migration 0007.
+
+CREATE TABLE runs_new (
+ id TEXT PRIMARY KEY,
+ repo TEXT NOT NULL,
+ ref_name TEXT NOT NULL,
+ sha TEXT NOT NULL,
+ pushed_at_ms INTEGER NOT NULL,
+ state TEXT NOT NULL,
+ failure_kind TEXT,
+ queued_at_ms INTEGER NOT NULL,
+ started_at_ms INTEGER,
+ finished_at_ms INTEGER,
+ run_token TEXT,
+ git_dir TEXT,
+ traceparent TEXT,
+
+ CHECK (state IN ('queued', 'active', 'succeeded', 'failed', 'canceled')),
+
+ CHECK (started_at_ms IS NULL OR started_at_ms >= queued_at_ms),
+ CHECK (finished_at_ms IS NULL OR finished_at_ms >= queued_at_ms),
+ CHECK (finished_at_ms IS NULL OR started_at_ms IS NULL
+ OR finished_at_ms >= started_at_ms),
+
+ CHECK (CASE state
+ WHEN 'queued' THEN started_at_ms IS NULL AND finished_at_ms IS NULL
+ WHEN 'active' THEN started_at_ms IS NOT NULL AND finished_at_ms IS NULL
+ WHEN 'succeeded' THEN started_at_ms IS NOT NULL AND finished_at_ms IS NOT NULL
+ WHEN 'failed' THEN finished_at_ms IS NOT NULL
+ WHEN 'canceled' THEN finished_at_ms IS NOT NULL
+ END)
+);
+
+INSERT INTO runs_new
+ SELECT id, repo, ref_name, sha, pushed_at_ms,
+ CASE state
+ WHEN 'pending' THEN 'queued'
+ WHEN 'complete' THEN 'succeeded'
+ WHEN 'superseded' THEN 'canceled'
+ ELSE state
+ END,
+ failure_kind, queued_at_ms, started_at_ms, finished_at_ms,
+ run_token, git_dir, traceparent
+ FROM runs;
+
+DROP TABLE runs;
+ALTER TABLE runs_new RENAME TO runs;
+
+CREATE INDEX runs_repo_pushed_at ON runs(repo, pushed_at_ms DESC);
+CREATE INDEX runs_state ON runs(state);
+
+CREATE TABLE jobs_new (
+ run_id TEXT NOT NULL REFERENCES runs(id) ON DELETE CASCADE,
+ job_id TEXT NOT NULL,
+ state TEXT NOT NULL,
+ exit_code INTEGER,
+ started_at_ms INTEGER,
+ finished_at_ms INTEGER,
+
+ CHECK (state IN ('active', 'succeeded', 'failed')),
+
+ CHECK (started_at_ms IS NULL OR finished_at_ms IS NULL
+ OR finished_at_ms >= started_at_ms),
+
+ CHECK (CASE state
+ WHEN 'active' THEN started_at_ms IS NOT NULL AND finished_at_ms IS NULL
+ WHEN 'succeeded' THEN started_at_ms IS NOT NULL AND finished_at_ms IS NOT NULL
+ WHEN 'failed' THEN started_at_ms IS NOT NULL AND finished_at_ms IS NOT NULL
+ END),
+
+ PRIMARY KEY (run_id, job_id)
+);
+
+INSERT INTO jobs_new
+ SELECT run_id, job_id,
+ CASE state
+ WHEN 'complete' THEN 'succeeded'
+ ELSE state
+ END,
+ exit_code, started_at_ms, finished_at_ms
+ FROM jobs;
+
+DROP TABLE jobs;
+ALTER TABLE jobs_new RENAME TO jobs;
diff --git a/quire-server/src/bin/quire/commands/dev.rs b/quire-server/src/bin/quire/commands/dev.rs
index ba780c9..edf7247 100644
--- a/quire-server/src/bin/quire/commands/dev.rs
+++ b/quire-server/src/bin/quire/commands/dev.rs
@@ -9,8 +9,8 @@ use quire::Quire;
/// Seed a tempdir with realistic CI run data and return a `Quire` pointing at it.
///
/// Creates a fresh tempdir under `std::env::temp_dir()`, inserts a fixed corpus
-/// of runs covering every interesting state (complete, failed, active, pending,
-/// superseded) with matching on-disk log artifacts. Idempotent — same input,
+/// of runs covering every interesting state (succeeded, failed, active, queued,
+/// canceled) with matching on-disk log artifacts. Idempotent — same input,
/// same output.
pub fn seed() -> Result<Quire> {
Seeder::new()?.run()
@@ -133,7 +133,7 @@ impl Seeder {
.into_diagnostic()?;
let Some(run_started_at) = started_at else {
- return Ok(()); // pending run; no jobs to insert.
+ return Ok(()); // queued run; no jobs to insert.
};
let logs_base = self
@@ -198,9 +198,9 @@ impl Seeder {
fn build_runs() -> Vec<SeedRun> {
vec![
- // Run 1 — complete, all jobs passed.
+ // Run 1 — succeeded, all jobs passed.
SeedRun {
- state: "complete",
+ state: "succeeded",
sha: "deadbeefdeadbeefdeadbeefdeadbeefdeadbeef",
ref_name: "refs/heads/main",
pushed_delta_ms: 0,
@@ -209,7 +209,7 @@ fn build_runs() -> Vec<SeedRun> {
jobs: vec![
SeedJob {
job_id: "build",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 0,
duration_ms: Some(2000),
@@ -232,7 +232,7 @@ fn build_runs() -> Vec<SeedRun> {
},
SeedJob {
job_id: "test",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 2000,
duration_ms: Some(2000),
@@ -257,7 +257,7 @@ fn build_runs() -> Vec<SeedRun> {
jobs: vec![
SeedJob {
job_id: "build",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 0,
duration_ms: Some(2000),
@@ -294,9 +294,9 @@ fn build_runs() -> Vec<SeedRun> {
},
],
},
- // Run 3 — superseded, pushed then rebased.
+ // Run 3 — canceled, pushed then rebased.
SeedRun {
- state: "superseded",
+ state: "canceled",
sha: "1111111111111111111111111111111111111111",
ref_name: "refs/heads/feature",
pushed_delta_ms: -1_200_000,
@@ -304,7 +304,7 @@ fn build_runs() -> Vec<SeedRun> {
duration_ms: Some(1000),
jobs: vec![SeedJob {
job_id: "build",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 0,
duration_ms: Some(1000),
@@ -349,9 +349,9 @@ fn build_runs() -> Vec<SeedRun> {
],
}],
},
- // Run 5 — pending, queued but not started.
+ // Run 5 — queued but not started.
SeedRun {
- state: "pending",
+ state: "queued",
sha: "3333333333333333333333333333333333333333",
ref_name: "refs/heads/main",
pushed_delta_ms: -1000,
@@ -359,9 +359,9 @@ fn build_runs() -> Vec<SeedRun> {
duration_ms: None,
jobs: vec![],
},
- // Run 6 — complete, multi-job: lint + build + test.
+ // Run 6 — succeeded, multi-job: lint + build + test.
SeedRun {
- state: "complete",
+ state: "succeeded",
sha: "4444444444444444444444444444444444444444",
ref_name: "refs/heads/v2",
pushed_delta_ms: -3_600_000,
@@ -370,7 +370,7 @@ fn build_runs() -> Vec<SeedRun> {
jobs: vec![
SeedJob {
job_id: "lint",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 0,
duration_ms: Some(2000),
@@ -384,7 +384,7 @@ fn build_runs() -> Vec<SeedRun> {
},
SeedJob {
job_id: "build",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 2000,
duration_ms: Some(4000),
@@ -398,7 +398,7 @@ fn build_runs() -> Vec<SeedRun> {
},
SeedJob {
job_id: "test",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 6000,
duration_ms: Some(4000),
@@ -423,7 +423,7 @@ fn build_runs() -> Vec<SeedRun> {
jobs: vec![
SeedJob {
job_id: "build",
- state: "complete",
+ state: "succeeded",
exit_code: Some(0),
started_delta_ms: 0,
duration_ms: Some(3000),
diff --git a/quire-server/src/ci/mod.rs b/quire-server/src/ci/mod.rs
index 9277ed7..f7097c5 100644
--- a/quire-server/src/ci/mod.rs
+++ b/quire-server/src/ci/mod.rs
@@ -428,7 +428,7 @@ while [ $# -gt 0 ]; do
esac
done
if [ -n "$events" ] && [ "$events" != "null" ]; then
- printf '{"at_ms":0,"type":"run_finished","outcome":"success"}\n' > "$events"
+ printf '{"at_ms":0,"type":"run_finished","outcome":"succeeded"}\n' > "$events"
fi
exit 0
"#
@@ -477,7 +477,7 @@ exit 0
}
#[test]
- fn run_ref_inner_drives_run_to_complete_with_fake_quire_ci() {
+ fn run_ref_inner_drives_run_to_succeeded_with_fake_quire_ci() {
let source = r#"(local ci (require :quire.ci))
(ci.job :build [:quire/push] (fn [] nil))"#;
let (_dir, quire, name) = bare_repo_with_ci(source);
@@ -506,7 +506,7 @@ exit 0
trigger_result.expect("trigger_ref should succeed with fake quire-ci");
- // The run should have reached complete.
+ // The run should have reached succeeded.
let conn = crate::db::open(&quire.db_path()).expect("db");
let state: String = conn
.query_row(
@@ -516,19 +516,19 @@ exit 0
)
.expect("should have a run");
assert_eq!(
- state, "complete",
- "run should be complete after fake quire-ci exits 0"
+ state, "succeeded",
+ "run should be succeeded after fake quire-ci exits 0"
);
- // No pending or active rows left behind.
+ // No queued or active rows left behind.
let count: i64 = conn
.query_row(
- "SELECT COUNT(*) FROM runs WHERE state IN ('pending', 'active')",
+ "SELECT COUNT(*) FROM runs WHERE state IN ('queued', 'active')",
[],
|row| row.get(0),
)
.expect("count");
- assert_eq!(count, 0, "run should be complete, not orphaned");
+ assert_eq!(count, 0, "run should be succeeded, not orphaned");
}
#[test]
diff --git a/quire-server/src/ci/run.rs b/quire-server/src/ci/run.rs
index 38a04d7..90e3caf 100644
--- a/quire-server/src/ci/run.rs
+++ b/quire-server/src/ci/run.rs
@@ -30,21 +30,21 @@ pub enum Executor {
/// The state of a CI run.
#[derive(Clone, Copy, Debug, PartialEq, Eq)]
pub enum RunState {
- Pending,
+ Queued,
Active,
- Complete,
+ Succeeded,
Failed,
- Superseded,
+ Canceled,
}
impl RunState {
pub fn as_str(&self) -> &'static str {
match self {
- RunState::Pending => "pending",
+ RunState::Queued => "queued",
RunState::Active => "active",
- RunState::Complete => "complete",
+ RunState::Succeeded => "succeeded",
RunState::Failed => "failed",
- RunState::Superseded => "superseded",
+ RunState::Canceled => "canceled",
}
}
}
@@ -54,11 +54,11 @@ impl std::str::FromStr for RunState {
fn from_str(s: &str) -> std::result::Result<Self, Self::Err> {
match s {
- "pending" => Some(RunState::Pending),
+ "queued" => Some(RunState::Queued),
"active" => Some(RunState::Active),
- "complete" => Some(RunState::Complete),
+ "succeeded" => Some(RunState::Succeeded),
"failed" => Some(RunState::Failed),
- "superseded" => Some(RunState::Superseded),
+ "canceled" => Some(RunState::Canceled),
_ => None,
}
.ok_or(())
@@ -86,9 +86,9 @@ impl Runs {
}
}
- /// Create a new run record in the `pending` state.
+ /// Create a new run record in the `queued` state.
///
- /// Before inserting, supersedes any existing `pending` or `active`
+ /// Before inserting, cancels any existing `queued` or `active`
/// run for the same `(repo, ref)`.
///
/// Inserts a row into `runs` and creates the run directory for
@@ -109,14 +109,14 @@ impl Runs {
let db = crate::db::open(&self.db_path)?;
- // Supersede any existing pending or active run for (repo, ref).
+ // Cancel any existing queued or active run for (repo, ref).
// Do this before inserting the new run so the new run is never
- // caught by its own supersede query.
- self.supersede_existing(&db, &meta.r#ref)?;
+ // caught by its own cancel query.
+ self.cancel_existing(&db, &meta.r#ref)?;
db.execute(
"INSERT INTO runs (id, repo, ref_name, sha, pushed_at_ms, state, queued_at_ms, run_token)
- VALUES (?1, ?2, ?3, ?4, ?5, 'pending', ?6, ?7)",
+ VALUES (?1, ?2, ?3, ?4, ?5, 'queued', ?6, ?7)",
rusqlite::params![
&id,
&self.repo,
@@ -135,14 +135,14 @@ impl Runs {
Ok(Run {
db_path: self.db_path.clone(),
id,
- state: RunState::Pending,
+ state: RunState::Queued,
base_dir: self.base_dir.clone(),
})
}
- /// Supersede any existing `pending` or `active` run for
+ /// Cancel any existing `queued` or `active` run for
/// `(repo, ref)`. Different refs are unaffected.
- fn supersede_existing(&self, db: &rusqlite::Connection, ref_name: &str) -> Result<()> {
+ fn cancel_existing(&self, db: &rusqlite::Connection, ref_name: &str) -> Result<()> {
let now = Timestamp::now().as_millisecond();
let active_ids: Vec<String> = db
@@ -155,26 +155,26 @@ impl Runs {
for run_id in &active_ids {
db.execute(
- "UPDATE runs SET state = 'superseded', finished_at_ms = ?1 WHERE id = ?2",
+ "UPDATE runs SET state = 'canceled', finished_at_ms = ?1 WHERE id = ?2",
rusqlite::params![now, run_id],
)?;
- tracing::info!(run_id = %run_id, "superseded active run");
+ tracing::info!(run_id = %run_id, "canceled active run");
}
- let pending_count = db.execute(
- "UPDATE runs SET state = 'superseded', finished_at_ms = ?1
- WHERE repo = ?2 AND ref_name = ?3 AND state = 'pending'",
+ let queued_count = db.execute(
+ "UPDATE runs SET state = 'canceled', finished_at_ms = ?1
+ WHERE repo = ?2 AND ref_name = ?3 AND state = 'queued'",
rusqlite::params![now, &self.repo, ref_name],
)?;
- if pending_count > 0 {
- tracing::info!(count = pending_count, "superseded pending run(s)");
+ if queued_count > 0 {
+ tracing::info!(count = queued_count, "canceled queued run(s)");
}
Ok(())
}
}
-/// Move every `pending` or `active` run to `failed` with
+/// Move every `queued` or `active` run to `failed` with
/// `failure_kind = 'orphaned'`. Called once at server startup to clean
/// up runs left behind by a prior instance. Operates across all repos —
/// orphans aren't a per-repo concern.
@@ -183,7 +183,7 @@ pub fn reconcile_orphans(db_path: &Path) -> Result<()> {
let db = crate::db::open(db_path)?;
let count = db.execute(
"UPDATE runs SET state = 'failed', finished_at_ms = ?1, failure_kind = 'orphaned'
- WHERE state IN ('pending', 'active')",
+ WHERE state IN ('queued', 'active')",
rusqlite::params![now],
)?;
if count > 0 {
@@ -252,7 +252,7 @@ impl Run {
/// * `jobs/<job>/sh-<n>.log` — per-sh CRI logs, written by quire-ci
/// via `--out-dir`.
///
- /// Run finishes `Complete` on exit 0, `Failed` otherwise. The DB
+ /// Run finishes `Succeeded` on exit 0, `Failed` otherwise. The DB
/// rows are written even on failure so the web UI can render
/// partial progress.
pub fn execute(
@@ -264,11 +264,11 @@ impl Run {
session: Option<&ApiSession>,
) -> Result<()> {
// For API runs the GET /api/run/bootstrap endpoint owns the
- // pending → active transition (it sets started_at_ms when quire-ci
+ // queued → active transition (it sets started_at_ms when quire-ci
// fetches the payload). Calling transition() here would set state =
// 'active' in the DB before quire-ci connects, causing the endpoint
// to return 410 Gone. Update local state only so the later
- // transition(Complete/Failed) call passes the state-machine check.
+ // transition(Succeeded/Failed) call passes the state-machine check.
if session.is_some() {
self.state = RunState::Active;
} else {
@@ -349,8 +349,8 @@ impl Run {
// quire-ci exited cleanly but never reached the terminal event —
// treat that as a crash too.
match run_outcome {
- Some(quire_core::ci::event::RunOutcome::Success) => {
- self.transition(RunState::Complete, None)?;
+ Some(quire_core::ci::event::RunOutcome::Succeeded) => {
+ self.transition(RunState::Succeeded, None)?;
}
Some(quire_core::ci::event::RunOutcome::PipelineFailure) => {
self.transition(RunState::Failed, Some("pipeline-failure"))?;
@@ -392,17 +392,17 @@ impl Run {
let db = crate::db::open(&self.db_path)?;
// Pass 1: jobs rows. Pair JobStarted with JobFinished by job_id.
- let mut pending_jobs: HashMap<&str, i64> = HashMap::new();
+ let mut inflight_jobs: HashMap<&str, i64> = HashMap::new();
let mut run_outcome: Option<RunOutcome> = None;
for event in &events {
match &event.kind {
EventKind::JobStarted { job_id } => {
- pending_jobs.insert(job_id.as_str(), event.at_ms);
+ inflight_jobs.insert(job_id.as_str(), event.at_ms);
}
EventKind::JobFinished { job_id, outcome } => {
- let started_at = pending_jobs.remove(job_id.as_str()).unwrap_or(event.at_ms);
+ let started_at = inflight_jobs.remove(job_id.as_str()).unwrap_or(event.at_ms);
let state = match outcome {
- JobOutcome::Complete => "complete",
+ JobOutcome::Succeeded => "succeeded",
JobOutcome::Failed => "failed",
};
db.execute(
@@ -421,14 +421,14 @@ impl Run {
// Pass 2: sh rows. Pair ShStarted with ShFinished by job_id
// (sequential within a run-fn, so a single buffer slot per job
// is enough).
- let mut pending_sh: HashMap<&str, (i64, &str)> = HashMap::new();
+ let mut inflight_sh: HashMap<&str, (i64, &str)> = HashMap::new();
for event in &events {
match &event.kind {
EventKind::ShStarted { job_id, cmd } => {
- pending_sh.insert(job_id.as_str(), (event.at_ms, cmd.as_str()));
+ inflight_sh.insert(job_id.as_str(), (event.at_ms, cmd.as_str()));
}
EventKind::ShFinished { job_id, exit_code } => {
- let Some((started_at, cmd)) = pending_sh.remove(job_id.as_str()) else {
+ let Some((started_at, cmd)) = inflight_sh.remove(job_id.as_str()) else {
continue;
};
db.execute(
@@ -470,12 +470,12 @@ impl Run {
///
/// Allowed edges (see `docs/CI-STATE.md`):
///
- /// * `Pending → Active`
- /// * `Pending → Complete`
- /// * `Pending → Superseded`
- /// * `Active → Complete`
- /// * `Active → Failed`
- /// * `Active → Superseded`
+ /// * `Queued → Active`
+ /// * `Queued → Succeeded`
+ /// * `Queued → Canceled`
+ /// * `Active → Succeeded`
+ /// * `Active → Failed`
+ /// * `Active → Canceled`
///
/// `failure_kind` is recorded only when transitioning to
/// `Failed`; it is ignored for other targets. Pass a short tag
@@ -487,12 +487,12 @@ impl Run {
use RunState::*;
let allowed = matches!(
(self.state, to),
- (Pending, Active)
- | (Pending, Complete)
- | (Pending, Superseded)
- | (Active, Complete)
+ (Queued, Active)
+ | (Queued, Succeeded)
+ | (Queued, Canceled)
+ | (Active, Succeeded)
| (Active, Failed)
- | (Active, Superseded)
+ | (Active, Canceled)
);
if !allowed {
return Err(Error::InvalidTransition {
@@ -512,7 +512,7 @@ impl Run {
rusqlite::params![now, &self.id],
)?;
}
- Complete | Superseded => {
+ Succeeded | Canceled => {
db.execute(
"UPDATE runs SET state = ?1, \
started_at_ms = COALESCE(started_at_ms, ?2), \
@@ -531,7 +531,7 @@ impl Run {
rusqlite::params![now, now, failure_kind, &self.id],
)?;
}
- Pending => unreachable!("transition to Pending is not valid"),
+ Queued => unreachable!("transition to Queued is not valid"),
}
self.state = to;
@@ -759,11 +759,11 @@ mod tests {
#[test]
fn run_state_round_trips() {
for state in [
- RunState::Pending,
+ RunState::Queued,
RunState::Active,
- RunState::Complete,
+ RunState::Succeeded,
RunState::Failed,
- RunState::Superseded,
+ RunState::Canceled,
] {
assert!(state.as_str().parse::<RunState>().is_ok());
}
@@ -825,14 +825,14 @@ mod tests {
}
#[test]
- fn create_writes_row_in_pending_state() {
+ fn create_writes_row_in_queued_state() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let run = runs
.create(&test_meta(), Some(&test_session()))
.expect("create");
- assert_eq!(run.state(), RunState::Pending);
+ assert_eq!(run.state(), RunState::Queued);
// Verify workspace directory was created.
let workspace = run.path().join("workspace");
@@ -887,7 +887,7 @@ mod tests {
}
#[test]
- fn transition_stamps_finished_at_on_complete_and_failed() {
+ fn transition_stamps_finished_at_on_succeeded_and_failed() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
@@ -898,8 +898,8 @@ mod tests {
.transition(RunState::Active, None)
.expect("to active");
completed
- .transition(RunState::Complete, None)
- .expect("to complete");
+ .transition(RunState::Succeeded, None)
+ .expect("to succeed");
assert!(completed.read_finished_at().expect("read").is_some());
let mut failed = runs
@@ -966,7 +966,7 @@ mod tests {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
- // Pending -> Failed is not allowed (must go via Active).
+ // Queued -> Failed is not allowed (must go via Active).
let mut run = runs
.create(&test_meta(), Some(&test_session()))
.expect("create");
@@ -980,8 +980,8 @@ mod tests {
.transition(RunState::Active, None)
.expect("to active");
completed
- .transition(RunState::Complete, None)
- .expect("to complete");
+ .transition(RunState::Succeeded, None)
+ .expect("to succeed");
assert!(completed.transition(RunState::Active, None).is_err());
assert!(completed.transition(RunState::Failed, None).is_err());
}
@@ -997,8 +997,8 @@ mod tests {
run.transition(RunState::Active, None).expect("to active");
let started = run.read_started_at().expect("read started_at");
- run.transition(RunState::Complete, None)
- .expect("to complete");
+ run.transition(RunState::Succeeded, None)
+ .expect("to succeed");
assert_eq!(
run.read_started_at().expect("read"),
started,
@@ -1015,14 +1015,14 @@ mod tests {
.expect("create");
run.transition(RunState::Active, None).expect("to active");
- run.transition(RunState::Complete, None)
- .expect("to complete");
+ run.transition(RunState::Succeeded, None)
+ .expect("to succeed");
- assert_eq!(run.state(), RunState::Complete);
+ assert_eq!(run.state(), RunState::Succeeded);
}
#[test]
- fn reconcile_fails_pending_orphans() {
+ fn reconcile_fails_queued_orphans() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let run = runs
@@ -1053,21 +1053,21 @@ mod tests {
}
#[test]
- fn reconcile_leaves_complete_runs_alone() {
+ fn reconcile_leaves_succeeded_runs_alone() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let mut run = runs
.create(&test_meta(), Some(&test_session()))
.expect("create");
run.transition(RunState::Active, None).expect("to active");
- run.transition(RunState::Complete, None)
- .expect("to complete");
+ run.transition(RunState::Succeeded, None)
+ .expect("to succeed");
let id = run.id().to_string();
reconcile_orphans(&quire.db_path()).expect("reconcile");
let reopened = Run::open(quire.db_path(), id, runs.base_dir.clone()).expect("reopen");
- assert_eq!(reopened.state(), RunState::Complete);
+ assert_eq!(reopened.state(), RunState::Succeeded);
}
#[test]
@@ -1112,7 +1112,7 @@ mod tests {
at_ms: 200,
kind: EventKind::JobFinished {
job_id: "build".into(),
- outcome: JobOutcome::Complete,
+ outcome: JobOutcome::Succeeded,
},
},
Event {
@@ -1163,7 +1163,7 @@ mod tests {
assert_eq!(
jobs,
vec![
- ("build".to_string(), "complete".to_string(), 100, 200),
+ ("build".to_string(), "succeeded".to_string(), 100, 200),
("test".to_string(), "failed".to_string(), 210, 220),
]
);
@@ -1218,7 +1218,7 @@ mod tests {
}
#[test]
- fn create_supersedes_pending_run_on_same_ref() {
+ fn create_cancels_queued_run_on_same_ref() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
@@ -1227,9 +1227,9 @@ mod tests {
.create(&test_meta(), Some(&test_session()))
.expect("create run1");
let run1_id = run1.id().to_string();
- assert_eq!(run1.state(), RunState::Pending);
+ assert_eq!(run1.state(), RunState::Queued);
- // Create second run for same (repo, ref) — should supersede the first.
+ // Create second run for same (repo, ref) — should cancel the first.
let meta2 = RunMeta {
sha: "def456".to_string(),
r#ref: "refs/heads/main".to_string(),
@@ -1238,19 +1238,19 @@ mod tests {
let run2 = runs
.create(&meta2, Some(&test_session()))
.expect("create run2");
- assert_eq!(run2.state(), RunState::Pending);
+ assert_eq!(run2.state(), RunState::Queued);
- // First run should now be superseded.
+ // First run should now be canceled.
let reopened = Run::open(quire.db_path(), run1_id, runs.base_dir.clone()).expect("reopen");
- assert_eq!(reopened.state(), RunState::Superseded);
+ assert_eq!(reopened.state(), RunState::Canceled);
assert!(
reopened.read_finished_at().expect("read").is_some(),
- "superseded run should have finished_at"
+ "canceled run should have finished_at"
);
}
#[test]
- fn create_supersedes_active_run_on_same_ref() {
+ fn create_cancels_active_run_on_same_ref() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
@@ -1270,19 +1270,19 @@ mod tests {
let run2 = runs
.create(&meta2, Some(&test_session()))
.expect("create run2");
- assert_eq!(run2.state(), RunState::Pending);
+ assert_eq!(run2.state(), RunState::Queued);
- // First run should be superseded.
+ // First run should be canceled.
let reopened = Run::open(quire.db_path(), run1_id, runs.base_dir.clone()).expect("reopen");
- assert_eq!(reopened.state(), RunState::Superseded);
+ assert_eq!(reopened.state(), RunState::Canceled);
assert!(
reopened.read_finished_at().expect("read").is_some(),
- "superseded run should have finished_at"
+ "canceled run should have finished_at"
);
}
#[test]
- fn create_does_not_supersede_different_ref() {
+ fn create_does_not_cancel_different_ref() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
@@ -1302,24 +1302,24 @@ mod tests {
.create(&meta2, Some(&test_session()))
.expect("create run2");
- // First run should still be pending.
+ // First run should still be queued.
let reopened = Run::open(quire.db_path(), run1_id, runs.base_dir.clone()).expect("reopen");
- assert_eq!(reopened.state(), RunState::Pending);
+ assert_eq!(reopened.state(), RunState::Queued);
}
#[test]
- fn create_does_not_supersede_complete_or_failed_runs() {
+ fn create_does_not_cancel_succeeded_or_failed_runs() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
- // Create and complete first run.
+ // Drive first run to succeeded.
let mut run1 = runs
.create(&test_meta(), Some(&test_session()))
.expect("create run1");
let run1_id = run1.id().to_string();
run1.transition(RunState::Active, None).expect("to active");
- run1.transition(RunState::Complete, None)
- .expect("to complete");
+ run1.transition(RunState::Succeeded, None)
+ .expect("to succeed");
// Create second run for same (repo, ref).
let meta2 = RunMeta {
@@ -1331,38 +1331,36 @@ mod tests {
.create(&meta2, Some(&test_session()))
.expect("create run2");
- // First run should still be complete.
+ // First run should still be succeeded.
let reopened = Run::open(quire.db_path(), run1_id, runs.base_dir.clone()).expect("reopen");
- assert_eq!(reopened.state(), RunState::Complete);
+ assert_eq!(reopened.state(), RunState::Succeeded);
}
#[test]
- fn transition_allows_pending_to_superseded() {
+ fn transition_allows_queued_to_canceled() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let mut run = runs
.create(&test_meta(), Some(&test_session()))
.expect("create");
- run.transition(RunState::Superseded, None)
- .expect("to superseded");
- assert_eq!(run.state(), RunState::Superseded);
+ run.transition(RunState::Canceled, None).expect("to cancel");
+ assert_eq!(run.state(), RunState::Canceled);
}
#[test]
- fn transition_allows_active_to_superseded() {
+ fn transition_allows_active_to_canceled() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let mut run = runs
.create(&test_meta(), Some(&test_session()))
.expect("create");
run.transition(RunState::Active, None).expect("to active");
- run.transition(RunState::Superseded, None)
- .expect("to superseded");
- assert_eq!(run.state(), RunState::Superseded);
+ run.transition(RunState::Canceled, None).expect("to cancel");
+ assert_eq!(run.state(), RunState::Canceled);
}
#[test]
- fn supersede_sets_finished_at() {
+ fn cancel_sets_finished_at() {
let (_dir, quire) = tmp_quire();
let runs = test_runs(&quire);
let mut run = runs
@@ -1372,14 +1370,13 @@ mod tests {
assert!(
run.read_finished_at().expect("read").is_none(),
- "should not have finished_at before supersede"
+ "should not have finished_at before cancel"
);
- run.transition(RunState::Superseded, None)
- .expect("to superseded");
+ run.transition(RunState::Canceled, None).expect("to cancel");
assert!(
run.read_finished_at().expect("read").is_some(),
- "superseded run should have finished_at"
+ "canceled run should have finished_at"
);
}
}
diff --git a/quire-server/src/db.rs b/quire-server/src/db.rs
index fa79e34..b7a047d 100644
--- a/quire-server/src/db.rs
+++ b/quire-server/src/db.rs
@@ -20,6 +20,7 @@ static MIGRATIONS: std::sync::LazyLock<Migrations<'static>> = std::sync::LazyLoc
M::up(include_str!("../migrations/0006_traceparent.sql")),
M::up(include_str!("../migrations/0007_schema_cleanup.sql")),
M::up(include_str!("../migrations/0008_rename_sh.sql")),
+ M::up(include_str!("../migrations/0009_rename_ci_vocab.sql")),
])
});
diff --git a/quire-server/src/quire/web/api.rs b/quire-server/src/quire/web/api.rs
index a18e6cf..ac8a879 100644
--- a/quire-server/src/quire/web/api.rs
+++ b/quire-server/src/quire/web/api.rs
@@ -168,7 +168,7 @@ async fn get_bootstrap(
.next()
.ok_or(rusqlite::Error::QueryReturnedNoRows)??;
- if row.state != "pending" {
+ if row.state != "queued" {
return Err(ApiError::Gone);
}
diff --git a/quire-server/src/quire/web/format.rs b/quire-server/src/quire/web/format.rs
index 920260f..e2ee112 100644
--- a/quire-server/src/quire/web/format.rs
+++ b/quire-server/src/quire/web/format.rs
@@ -83,7 +83,7 @@ fn format_ms_duration(ms: i64) -> String {
/// don't each carry their own identical match.
pub fn state_class(state: &str) -> &'static str {
match state {
- "complete" => "c-ok",
+ "succeeded" => "c-ok",
"failed" => "c-bad",
_ => "c-muted",
}
@@ -171,8 +171,8 @@ mod tests {
}
#[test]
- fn state_class_complete() {
- assert_eq!(state_class("complete"), "c-ok");
+ fn state_class_succeeded() {
+ assert_eq!(state_class("succeeded"), "c-ok");
}
#[test]
@@ -182,7 +182,7 @@ mod tests {
#[test]
fn state_class_unknown_falls_through() {
- assert_eq!(state_class("pending"), "c-muted");
+ assert_eq!(state_class("queued"), "c-muted");
assert_eq!(state_class("active"), "c-muted");
assert_eq!(state_class(""), "c-muted");
}
diff --git a/quire-server/src/quire/web/handlers.rs b/quire-server/src/quire/web/handlers.rs
index 1198460..59e6066 100644
--- a/quire-server/src/quire/web/handlers.rs
+++ b/quire-server/src/quire/web/handlers.rs
@@ -393,7 +393,7 @@ mod tests {
let env = TestEnv::new();
env.insert_run(
UUID1,
- "complete",
+ "succeeded",
SHA1,
"refs/heads/main",
1000,
@@ -426,14 +426,14 @@ mod tests {
let env = TestEnv::new();
env.insert_run(
UUID1,
- "complete",
+ "succeeded",
SHA1,
"refs/heads/main",
1000,
Some(2000),
Some(3000),
);
- env.insert_job(UUID1, "build", "complete", Some(0), Some(2000), Some(3000));
+ env.insert_job(UUID1, "build", "succeeded", Some(0), Some(2000), Some(3000));
let app = env.app();
let req = Request::builder()
.uri(&format!("/example/ci/{UUID1}"))
diff --git a/quire-server/src/quire/web/templates.rs b/quire-server/src/quire/web/templates.rs
index c72e957..fc0fc90 100644
--- a/quire-server/src/quire/web/templates.rs
+++ b/quire-server/src/quire/web/templates.rs
@@ -166,7 +166,7 @@ impl DetailRun {
}
pub fn is_terminal(&self) -> bool {
- self.state == "complete" || self.state == "failed"
+ self.state == "succeeded" || self.state == "failed"
}
pub fn duration_display(&self) -> String {