Expand description
shigoto (仕事) — the typed job-system primitive.
Umbrella crate. Re-exports the public surface of every sibling so
consumers depend on shigoto = "0.1" and reach the full algebra
via use shigoto::{Job, JobId, Scheduler, ...}; without naming
every sub-crate.
Canonical spec: theory/SHIGOTO.md.
Theory frame: theory/THEORY.md §IV (Motion).
Re-exports§
pub use shigoto_gate;
Structs§
- AllUpstreams
Terminal AllUpstreamsTerminal— every direct DAG predecessor has reached a terminal phase ({Succeeded, Skipped, Deadlettered}). The scheduler implicitly applies this gate to enforce DAG edge semantics; consumers don’t normally register it explicitly.- Budget
Spec - Budget
Tree - Three-dimension budget envelope. Allocation checks every applicable limit (min-intersection): a job runs iff all three have slack.
- Dag
- Typed DAG of JobIds. Edges declare “to may not start until from
reaches a terminal phase” (Succeeded | Skipped | Deadlettered).
Acyclic by construction at edge-add time would require an O(V+E)
check on every insert; instead, cycles are detected at
toposort()/waves(), where consumers must run before scheduling anyway. - Failure
Record - Serialize/Deserialize so a
FailureRecordround-trips through a durable store (e.g.shigoto_scheduler::SchedulerStore) across a restart — every field is already serde-safe (FailureKindderives it too), so this is a pure additive derive, not a shape change. - Fixed
Clock - A settable clock for deterministic tests.
Clock::nowtakes&self(the scheduler holds clocks behindArc<dyn Clock>), so the stored instant lives behind aMutexand moves only when the test callsFixedClock::setorFixedClock::advance— never on its own. This is what lets a test prove “job waits out its backoff, then retries” by moving the clock forward programmatically instead of sleeping for real. - FsmDefect
- A single convergence-proof failure, with a message naming the offending state.
- Gate
Context - Everything a Gate needs to make its decision: the job in question,
the FSM snapshot (every other job’s phase), the DAG (so gates
like
AllUpstreamsTerminalcan ask about predecessors). - Illegal
Transition - Rejected transition —
(from, signal)is not a legal FSM cell. Returning Result instead of panicking lets consumers report drift (an operator action attempting an illegal transition) without crashing the scheduler. - InMemory
Sink - In-memory
JobId → Outputmap. The consumer reads viadrain(clears + returns) orsnapshot(clones + keeps) after ticks complete.Arc<Mutex<...>>interior so the same sink can be shared between the Job (recording) and the consumer (reading). - InProcess
Scheduler - Default scheduler — single-process, in-memory FSM state, sequential execution within a wave.
- JobId
- Typed identity for a Job. Stable across cycles + scheduler restarts.
- JobKind
Id - Typed work-class identifier. Stored as
String(not&'static str) so it serializes through serde without lifetime constraints. CheapCloneis fine for the volume we expect (≤ ~100 kinds across the whole scheduler). - Json
File Scheduler Store - A
SchedulerStorebacked by a single JSON file, written atomically (write to a.tmpsibling, thenrenameover the real path) so a crash mid-write never leaves a half-written, unparseable snapshot behind — the same techniqueformigueiro::FilePlanStoreuses for its per-target state file. - Null
Sink - No-op sink — discards every output. Default for Jobs that don’t need their typed Output surfaced.
- Operator
Approved OperatorApproved— pass iff an external operator has flipped a pre-arranged flag. The flag itself lives in the consumer’s state store; this gate is a thin wrapper that holds anArc<AtomicBool>or similar. v0.1 ships with aClosurevariant that takes aFn() -> boolfor tests + ad-hoc cases.- Scheduler
Snapshot - The subset of
InProcessSchedulerstate that survives a restart. See the module doc for what’s deliberately excluded (Jobs, Gates, RetryPolicies — the executable graph, re-registered by the consumer). - Snapshot
- Read-only snapshot of the scheduler’s current FSM map.
- Stored
JobState - One persisted job’s FSM progress.
- System
Clock - Wall-clock time via
chrono::Utc::now(). Default clock forInProcessScheduler::new. - Tick
Receipt - Derived per-tick rollup the scheduler emits on every
tick. - Transition
Event - Unhealed
Drift - Urgency
Weights - Weights for the urgency score. Each term is multiplied and summed; tune to shift the balance between “most stale”, “furthest behind”, “most drifted”, and “longest starved”. Defaults bias toward closing real drift and honoring fairness, with staleness as a gentle background pressure.
Enums§
- Budget
Error - DagError
- Defect
Kind #[non_exhaustive]is deliberate and load-bearing: it forces every downstreammatchto carry a wildcard, so the next defect kind this harness learns to detect is a non-breaking change instead of a fleet migration. It is free today — no crate outside shigoto referencesDefectKindat all (consumers callassert_convergent_fsm, which returnsResult<(), String>).- Gate
Aggregate - Aggregate gate outcome — what the cohort of gates collectively said. Per §III.9 individual gates return Pass / Vacuous / Wait / Skip; the aggregate is the worst outcome (Skip > Wait > Pass) per a typed reducer in shigoto-gate. We carry the rolled-up result here so the FSM stays language-agnostic about how the rollup is computed.
- Gate
Outcome - One gate’s verdict.
- JobPhase
- FSM phase a Job inhabits. See
theory/SHIGOTO.md§III.3 for the transition table. - JobScope
- JobSubject
- Priority
Class - Hard priority tier. Lower discriminant = scheduled first. A higher tier
always precedes a lower one regardless of urgency — tiers express coarse
operator intent;
Schedulable::urgencyis the fine sort within a tier. - Retry
Decision - Retry
Outcome - Retry decision from a
RetryPolicy::decide()call. Same shape that shigoto-retry’sRetryDecisionexposes — duplicated as a typed signal payload so the FSM stays in shigoto-types without a dependency on shigoto-retry. - Retry
Policy - Scheduler
Error - Scheduler
Store Error - Signal
- FSM driver — every legal way a Job’s phase can change. Exhaustive
over the
(JobPhase, Signal)cross-product pertheory/SHIGOTO.md§IV.1; theadvancetable below enumerates every cell. - Skip
Reason - Transition
Reason
Traits§
- Clock
- A source of the current time for
crate::InProcessScheduler. Production code usesSystemClock(the default); tests that need to assert backoff/retry timing without a real sleep useFixedClock. - Convergent
Fsm - A typed finite-state lifecycle whose convergence can be proven mechanically.
- Erased
Job - Trait-object dispatch surface. The scheduler holds
Box<dyn ErasedJob>(Jobitself isn’t object-safe because of the associated types);ErasedJobcollapses the typed Output + Error to()+ boxed error so the scheduler can store heterogeneous jobs in one DAG. - Gate
- One typed precondition. Pure — no IO. Gate impls that “need” IO are antipatterns; the right shape is a Job that emits a typed fact and a downstream gate that checks the fact.
- Job
- The typed Job trait — what every consumer’s domain-specific job
implements. Per
theory/SHIGOTO.md§III.1. - JobError
- JobInput
- Inputs / Outputs / Errors implement these marker traits so the scheduler can serialize across boundaries when persistence lands.
- JobOutput
- Output
Sink - Typed receiver for
Job::Outputvalues. Jobs callrecordon a successfulexecuteso consumers (reconcile receipts, audit trails, dashboards) can read the typed outcomes the scheduler’s phase-tracking discards. - Recording
Job - Convenience trait that captures the most common Job authoring
shape across pleme-io consumers: a Job whose typed Output flows
through an
OutputSinkfor consumer-side capture, and whose identity decomposes into (scope, kind, subject). - Retry
Decider - Schedulable
- A unit of pending work that can be ordered. Implement the eight accessors;
the default
urgency/rank_keygive every consumer the same anti-starvation ordering for free. - Scheduler
- Scheduler
Store - A store for scheduler FSM state across restarts. A trait so the
reference
JsonFileSchedulerStore(workstation / single-process daemon) and a future durable impl (operator CRD / Postgres, per MAGMA-NATIVE’s “in-memory + DB-persisted, never the pod filesystem” destination) share one contract. Mirrorsformigueiro::PlanStore’s shape one repo over. - Transition
Emitter - Receivers of
TransitionEvent. Thin trait over the canonicalshigoto_types::sink::Sink<TransitionEvent>so every consumer writingArc<dyn TransitionEmitter>keeps working unchanged after the theory/CONVERGENCE-ADOPTION.md Phase 0.1 extraction. The blanket impl below means anySink<TransitionEvent>impl auto-satisfiesTransitionEmitter— no per-impl wiring at the consumer side.
Functions§
- advance
- The canonical FSM driver. Pure: same
(from, signal)always produces the same result. Exhaustive overJobPhase × Signal— adding a new phase or signal fails to compile until every cell of the cross-product is decided. - assert_
convergent_ fsm - The full forcing-function: closed-graph + terminal-soundness + no-traps +
universal convergence.
Ok(())iff the FSM is convergent and well-formed;Erraggregates EVERY defect (so one run reports all problems, not the first). - pick
- Like
rank, but return only the topslots— the next wave to dispatch. Pair with [shigoto-budget] for the no-crash admission bound: rank picks the order, budget enforces the count. - rank
- Order every eligible unit in
pending, most-valuable-first. Ineligible units are dropped. Deterministic: equal-urgency units fall back to stable id, so the result is replayable.
Type Aliases§
- Audit
File Emitter - Append-only JSONL audit file. One event per line. Same shape as
tend’s existing
audit.rsso operators can grep both with the same tooling. Alias of the canonicalshigoto_types::sink::AuditFileSink<TransitionEvent>— JSONL serialization is byte-identical (oneserde_json::to_stringper line +writeln!append). - Multi
Emitter - Fan-out emitter — every inner sink receives every event. Alias of
the canonical
shigoto_types::sink::MultiSink<TransitionEvent>; inner sinks areArc<dyn Sink<TransitionEvent>>. - Null
Emitter - No-op emitter — the default for tests + consumers without
observability wired up. Sinks should compose via
MultiEmitterinstead of stubbing this in production. Alias of the canonicalshigoto_types::sink::NullSink<TransitionEvent>.