npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@alma-harness/schedule

v0.12.0

Published

Alma's clock: what is due, and the tick an external scheduler calls to run it — no process kept alive.

Readme

@alma-harness/schedule

The clock of Alma, as a tick: what is due, and the call an external scheduler makes to run it. No process is kept alive (spec: clock-tick).

Status: pre-1.0. The API is still moving; see the roadmap for where it stands.

What it owns

  • createSchedule({ routines, runs, runner })tick({ now? }) reads the routine store, decides what is due, runs it through the routine runner with the FIRE's instant as the run id, and reports { at, due, runs, errors }. plan(now?) answers without running. runner may be a resolver, (routine) => RoutineRunner, for a deployment with an agent per tenant — list crosses every scope. A routine that cannot be planned (a zone typo, a bad cron) is one entry in errors; the others run.
  • register / cancel — register THROUGH the schedule: it asks the runner to validate and reads the schedule once, so what the tick could not run is refused at registration, not at 8h with nobody watching.
  • What is due: the latest fire at or before now — a cron's previous occurrence in its tz (UTC when absent), every anchored on the routine's registration, at once — when it is later than the routine's last run. A job routine whose submission is still open is collected on every tick. Missed fires are not replayed: after downtime a daily routine runs once.
  • latestFire, nextFire, validateSchedule — pure schedule calculations. nextFire(schedule, registeredAt, after) returns the first ISO occurrence strictly after the cursor and at or after registration, or undefined when exhausted. Intervals stay anchored on registration. Hosts may enqueue an external wake-up at that time, then revalidate current configuration and use native planning/claims on wake. This helper grants no execution authority; hosts must also arrange wake-ups for missed-fire recovery and pending native batch collection.
  • createGovernedConversationRoutineRunner runs a triggered conversation; createGovernedBatchRoutineRunner submits and collects background batches. Both implement RoutineRunner, claim before work, enforce the routine's narrowed cap and daily ceiling, and make at most one delivery attempt per retained fire. The legacy createRoutineRunner and RoutineRunnerConfig are removed (spec: remove-legacy-routine-runner).

Wiring

One endpoint the scheduler calls on a cadence. Cloud Scheduler every minute against a Cloud Run service at zero minimum instances is the shape it was written for; anything that can call a URL on a cron is the clock.

import { createSchedule } from "@alma-harness/schedule";

const schedule = createSchedule({ routines, runs, runner: (routine) => runnerFor(routine.scope) });
await schedule.register(briefing);

// Node's http, Hono, Express — the product's choice. A shared secret, or
// the platform's OIDC, decides who may tick.
app.post("/tick", async (req, res) => {
  if (req.headers["x-tick-secret"] !== process.env.TICK_SECRET) return res.status(401).end();
  res.json(await schedule.tick());
});

Claims and recovery

The governed runners require atomic RoutineRunStore.claim. A retained (org, uid, routineId, runId) has one execution owner across instances, including batch submission and collection. Pending tick plans carry collectRunId, which custom runner wrappers must forward: it collects only that existing run, never a new submission if the plan becomes stale. Losing callers return its current record; running can mean active work or an interrupted execution. A turn may take tens of seconds, so the host request timeout must allow it.

A claim is persisted before work starts. Before calling the sink, the runner also records the known session, turn, cost and delivery hash. If a process dies or persistence fails, running is never expired or automatically retried, even when an execution deadline passes. This deliberately favors avoiding duplicates over automatic recovery. Sink errors become failed and are also not automatically retried: an error may follow a successful external action.

Reconcile unresolved runs against the session, provider batch and destination before recording a final outcome through record. Age alone cannot prove that the original worker has stopped or that a delivery did not happen. Recording a final outcome does not resend that run. Any replacement action requires an explicit operator decision after reconciliation; do not delete claims to enable retries. External exactly-once delivery would require an idempotent destination and is not guaranteed here.

Text suppression compares only with the last completed delivery. It does not serialize different fire ids or enforce a global once-ever rule for equal text. Daily ceilings likewise do not reserve capacity atomically across different fire ids. Retention preserves running and submitted; removing completed records ends their replay protection. Use a durable store in production; the in-memory reference survives only its process.

Upgrade: drain old runners, run migrateRoutineRunStore to expand the outcome constraint (existing records are preserved), and deploy matching core, schedule and Postgres packages. Custom stores must implement the atomic claim contract and callers must accept running. Run the shared describeRoutineRunStoreContract against each custom backend. Do not mix old runners that write only after delivery with the new claim protocol.

What it must never do

  • Keep a timer. The clock is outside; this package only answers it.
  • Replay missed fires, or run routines in parallel.
  • Hold a vendor scheduler's SDK. The tick IS the adapter.

Financial warning policy

A routine can use budget: { perTurnUsd: { usd: 0.5, onExceeded: "warn" } }. Turn routines require a matching warn policy and an amount no greater than the host threshold (or no per-turn cap on the host); validation rejects widening before claiming a run. A trusted conversation factory composes the exact narrowed caps. Numeric routine caps still block.

Known crossings are persisted in RoutineRun.capsCrossed before delivery and forwarded to the sink. Both per-run and persistent batch warnings come from original governed receipts. A replay returns the metadata without another call, charge or delivery. Deduplicate host alerts by scope, run and cap. Frequency and duplicate guards still apply.

Run migrateRoutineRunStore before deploying these matching core, loop, schedule and Postgres packages; it adds caps_crossed idempotently to existing rows.

Documentation

Docs index · Architecture §8 · Routine runner spec

Apache-2.0

Governed batch routines

createGovernedBatchRoutineRunner implements RoutineRunner for job routines, so pass it directly to createSchedule. Configure a GovernedBatchConfig, host caps, runs, sinks, a synchronous metadata-only plan, runTimeoutMs, submissionDeadlineMs and onBackgroundError. The plan returns model/prices, policy/price/output versions, controls, consumers, resultRetentionMs and lazy loadRequest. The request must match the plan at batch tier without tools; its messages are replaced with the original routine goal. Context loads only after all governed reservations and audit. The routine per-run cap narrows host policy.

Keep batch.configRevision immutable and change it whenever plan, provider account, system/output policy or sink resolution changes. Pending collection requires the original revision and full routine definition. governedRoutineBatchKey(scope, routineId, runId) exposes correlation for accounting-only repair through the batch runner, including when delivery remains unresolved.

A scoped collection may repeat GET and repair finance. It never writes waiting metadata over a completed run. Only the permanent submitted-to-running claim winner may attempt delivery; interrupted submission or delivery is not retried. Retain unresolved records and reconcile externally. Read output again before egress to respect erasure; concurrent erasure during external delivery remains a host boundary. Warnings come from governed financial receipts and retain the answer. Closed failure reasons never contain raw provider or sink errors. Dedupe compares only the last successful text hash; duplicate replies retain their cost. Distinct fire ceilings remain non-atomic. Governed conversational routines are not part of this runner.

Governed conversational routines

createGovernedConversationRoutineRunner handles turn routines through a trusted synchronous conversation(caps) factory returning createConversationRunner with those exact narrowed caps. Supply matching policyVersion, configRevision, resultContractVersion, resultRetentionMs, maxCalls, rootDeadlineMs, runTimeoutMs, host caps, runs, sinks and onBackgroundError. Configuration revision covers the factory's provider account, prompts, prices, tools/profiles, result policy and sink mapping. The factory composes configuration only; it cannot read context or perform effects. Ordinary mutable data is copied by each runner; store, factory and sink implementations are trusted host capabilities.

The permanent routine claim precedes factory and conversation invocation. The conversation's admission/root/output reservations precede the lazy routine goal and all context/effects. An absent profile grants no unattended tools. Main and child costs come only from governed receipts; warnings accompany the single sink attempt. governedRoutineRootKey(scope, routineId, runId) exposes accounting correlation.

Uncertain conversations and late completions retain routine ownership for explicit reconciliation. Replaying a root after routine metadata loss does not grant delivery. Final content is rechecked after writing delivery metadata and immediately before sink access. Interrupted, failed or unacknowledged delivery never retries itself. Register all conversation result namespaces and sessions in erasure/retention; an external destination and concurrent egress still require host reconciliation.

The executable compositions live in the schedule tests on memory/PostgreSQL and in Prumo P1. Session/root IDs use governed hashes for correlation. Invalid routine data can throw synchronously before admission; operational errors expose fixed metadata and do not release uncertain work.

Canonical routine invocation

The conversational adapter now invokes ConversationRunner.runTurn with the existing explicit descriptor (spec: canonical-routine-adoption). The legacy run alias is not called. A canonical not_admitted/routing_unavailable response finishes the fire as refused, with zero cost and no conversation root. Its metadata replay does not retry that fire; repaired configuration can serve a new fire. Uncertain invocation, lost refusal acknowledgement and interrupted delivery still cannot grant new execution or sink authority.

Prumo P1 now composes both governed routine adapters over the canonical runtime's financial stores and spec 128's separate batch-result namespace. Its clock/inbox remain local fixtures; no process, real recipient or production scheduler is added.