Skip to content

1.2.59

Choose a tag to compare

@github-actions github-actions released this 29 Sep 22:38
· 62 commits to main since this release
Immutable release. Only release title and notes can be modified.
  • WinGet installs and upgrades use signed setup executables that display
    the full cronstable logo in User Account Control (UAC) prompts. Windows
    executables and installed application entries use the same logo. The setup
    installers support upgrades from existing MSI installations. Standalone
    MSI packages are also available.
  • MCP tool results carry their data as JSON in a second text block after
    the one-line summary, so clients that pass only content to the model,
    such as Claude Desktop, can answer from it. cron_get_status,
    cron_list_jobs, and the three schedule-authoring tools declare an
    outputSchema.
  • The MCP server serves protocol revision 2026-07-28 beside 2025-11-25
    and its predecessors on the same endpoint. A request that carries its
    version in _meta is served statelessly, with server/discover,
    resultType, cache hints, and checks that the MCP-Protocol-Version,
    Mcp-Method, and Mcp-Name headers match the body. The cronstable mcp
    bridge sets those headers for such frames and passes the daemon's JSON-RPC
    errors through; its own transport errors use code -31000.
    cronstable mcp --check reports which era the daemon speaks.
  • A view token opens a read-only MCP session. /mcp requires view, each
    tool requires the scope of its REST route, and tools/list and the
    prompts show only what the presented token can call. Anonymous access
    (web.anonymousScopes) does not reach /mcp.
  • MCP pauses, resumes, and gate decisions record the presented token's label
    as by. A by argument to cron_decide_gate must be a string of at most
    100 characters, and the server appends it to the label.
  • cron_run_job, cron_trigger_dag, cron_backfill_dag,
    cron_recover_dag, and cron_decide_gate report destructiveHint: true
    and openWorldHint: true, so clients that confirm risky calls confirm
    these.
  • MCP resource templates decode percent-encoded values, so
    cronstable://jobs/nightly%20backup reads the job nightly backup.
  • An MCP prompt is served only when every tool it calls is visible to the
    caller: backfill_plan needs readOnly: false, and blast_radius leaves
    out its workflow and state steps when those toolsets are off.
    prompts/get rejects a missing, empty, or non-string argument with
    -32602.
  • The MCP server completes prompt arguments and resource template variables
    from job names, workflow names, and recent run keys, and identifies itself
    with a description, a website, and the dashboard's icon.
  • The MCP server rejects tool arguments a tool does not accept, request IDs
    that are not strings or integers, and JSON-RPC responses posted by a
    client, and its responses echo the negotiated MCP-Protocol-Version.
    cron_pause_job refuses a Boolean durationSeconds, and the recovery
    tools apply the REST checks to mode, plan_token, and
    allow_config_change.
  • Every 401 from the web API carries
    WWW-Authenticate: Bearer realm="cronstable".
  • A reload that restarts the web API keeps each http:// listener's socket
    open and passes new connections to the replacement server. Another local
    account therefore cannot bind the port during the restart and collect the
    bearer token that the dashboard, cronstable tui, and cronstable mcp
    send. The running server stops only after its replacement is built, so a
    replacement that fails to build leaves it serving. https:// and
    unix:// listeners close and bind again.
  • Saving or clearing the dashboard token rewrites both calendar feed links
    and empties the token field and the pairing sheet, so a shared tab keeps
    no copy of the previous token.
  • A DAG run's configurationRevision covers the names of task environment
    variables and leaves out their values, so a view reader cannot test
    guesses for a secret against it. Recovering a run after an environment
    value changes does not require allowConfigChange. Runs whose recorded
    revision includes environment values report a configuration change when
    recovered.

Performance compared with 1.2.58

Regressions accepted for this release. The following changes exceeded their regression thresholds:

  • mcp.handle_200 increased by 16.3% (91.54 ms to 106.42 ms; declared limit 15%; noise: ±0.9%)

Both versions were benchmarked in alternating rounds on the same machine. Results use the lowest runtime and median memory usage across rounds. Negative changes mean less time or memory; positive changes mean more.

Benchmark comparisons (99 metrics)

Noise (±) estimates run-to-run variation across both versions. A result fails the regression check only if the increase exceeds its percentage limit, its minimum absolute change, and 2 times the noise estimate. n/a means too few rounds to estimate noise; the other limits still apply.

The Regression limit column accounts for the minimum absolute change, expressed as a percentage of the baseline. Not enforced marks informational metrics.

Startup times include Python startup overhead. Their percentage changes exclude that overhead when it can be measured separately and leaves a positive runtime in both versions.

Benchmark 1.2.58 1.2.59 Change Noise (±) Regression limit
loop.stall_metrics_2000 14.16 ms 17.60 ms +24.3% 2.5% Not enforced
webui.log_count_5k 60.0 us 70.0 us +16.7% 0.0% 3333% (declared 25%)
mcp.handle_200 91.54 ms 106.42 ms +16.3% (regression) 0.9% 15%
startup.import_cronexpr 21.81 ms 22.08 ms +5.0% 0.2% 25%
schedule.lint_250_zoned 4.43 ms 4.54 ms +2.5% 1.8% 225.7% (declared 15%)
dag.finish_fanin_1k 2.32 ms 2.37 ms +1.8% 1.6% 215.2% (declared 25%)
webui.week_walk_500 2.80 ms 2.75 ms -1.8% 1.3% 71.43% (declared 25%)
loop.idle_wake_rate 10.33 ms 10.49 ms +1.6% 2.0% 48.4% (declared 25%)
state.artifact_list_churn 750.1 us 738.5 us -1.5% 4.5% 666.6% (declared 25%)
schedule.next_fires_2k 17.10 ms 17.32 ms +1.3% 1.0% 58.47% (declared 15%)
mem.gc_pause_100k 63.19 ms 63.98 ms +1.3% 1.0% 25%
startup.python_baseline 13.38 ms 13.22 ms -1.2% 0.0% Not enforced
state.depends_on_past_gate 1.50 ms 1.49 ms -1.0% 1.3% 133% (declared 25%)
dag.list_dags_warm 576.6 us 571.2 us -0.9% 3.1% 346.8% (declared 25%)
webui.render_fleet_15x400 19.09 ms 18.92 ms -0.9% 1.2% 25%
cronexpr.occurrences_1k 9.88 ms 9.80 ms -0.9% 0.9% 101.2% (declared 15%)
startup.version 41.22 ms 41.30 ms +0.9% 0.8% 25%
config.reload_gc_100k 1.631 s 1.618 s -0.8% 0.8% 25%
ical.render_500x7d 35.55 ms 35.83 ms +0.8% 1.1% 28.13% (declared 15%)
redact.clean_20k 15.54 ms 15.66 ms +0.8% 0.9% 64.36% (declared 15%)
state.append_1k 44.12 ms 43.78 ms -0.8% 1.9% 113.3% (declared 15%)
schedule.reseed_local_20k 61.86 ms 62.30 ms +0.7% 1.0% 16.16% (declared 15%)
dag.build_layered_10k 37.41 ms 37.17 ms -0.6% 0.4% 26.73% (declared 15%)
schedule.reseed_100k 139.05 ms 139.93 ms +0.6% 1.3% 15%
statsd.emit_2k 7.32 ms 7.36 ms +0.6% 2.2% 136.7% (declared 25%)
webui.render_term_5k 82.60 ms 83.10 ms +0.6% 1.1% 25%
state.boot_rehydrate_populated 21.08 ms 20.95 ms -0.6% 0.6% 47.44% (declared 15%)
schedule.cold_build_100k 339.46 ms 341.43 ms +0.6% 1.4% 15%
state.gc_sweep_2k_streams 16.11 ms 16.20 ms +0.6% 1.0% 93.13% (declared 25%)
job.report_noop_100k 35.45 ms 35.25 ms -0.5% 1.0% 15%
state.list_records_2k 12.60 ms 12.53 ms -0.5% 0.4% 79.34% (declared 15%)
cluster.fleet_view_15x400 64.81 ms 65.15 ms +0.5% 0.9% 25%
webapi.auth_scope_20k 60.59 ms 60.30 ms -0.5% 1.1% 16.5% (declared 15%)
dag.mapped_drain_256 13.29 ms 13.22 ms -0.5% 0.9% 75.27% (declared 25%)
state.list_records_warm 74.27 ms 73.92 ms -0.5% 0.4% 15%
cronexpr.next_complex 10.96 ms 10.91 ms -0.5% 0.5% 91.27% (declared 15%)
webapi.sse_burst_20k 39.28 ms 39.10 ms -0.5% 0.8% 15%
schedule.suggest_slot_5k 2.85 ms 2.84 ms -0.5% 1.6% 351% (declared 15%)
schedule.pressure_20k_48h 10.89 ms 10.85 ms -0.4% 1.3% 91.79% (declared 15%)
state.artifact_get_newest 9.37 ms 9.33 ms -0.4% 2.9% 53.36% (declared 25%)
state.derive_max_cold 11.22 ms 11.18 ms -0.4% 1.5% 89.09% (declared 15%)
cronexpr.next_simple 27.59 ms 27.71 ms +0.4% 1.0% 36.24% (declared 15%)
dag.build_chain_10k 21.11 ms 21.19 ms +0.4% 0.8% 47.36% (declared 15%)
cronexpr.next_dst_2k 28.61 ms 28.71 ms +0.4% 0.6% 34.96% (declared 15%)
tui.log_restyle_5k 22.25 ms 22.33 ms +0.4% 0.9% 44.94% (declared 15%)
startup.import_config 74.01 ms 74.07 ms +0.4% 0.5% 25%
json.roundtrip_orjson_3k 148.41 ms 148.96 ms +0.4% 1.5% 15%
cluster.job_owner_2k 44.09 ms 44.24 ms +0.3% 0.5% 22.68% (declared 15%)
state.lease_renew_200 18.71 ms 18.65 ms -0.3% 1.2% 267.2% (declared 25%)
state.fanout_gather_100 3.85 ms 3.86 ms +0.3% 4.0% 259.6% (declared 25%)
fingerprint.job_set_id_10k 219.51 ms 220.18 ms +0.3% 1.2% 15%
tui.drawer_paint_5k 92.45 ms 92.69 ms +0.3% 0.8% 15%
dag.advance_quiescent_1k 54.49 ms 54.35 ms -0.3% 0.5% 25%
config.parse_crontab_1k 8.56 ms 8.54 ms -0.3% 0.4% 116.8% (declared 15%)
schedule.due_pass_100k 13.00 ms 12.97 ms -0.3% 1.7% 76.92% (declared 25%)
cronexpr.test_match_200k 18.35 ms 18.30 ms -0.2% 0.4% 54.51% (declared 15%)
schedule.duplicates_20k 12.31 ms 12.34 ms +0.2% 1.1% 81.26% (declared 15%)
config.interp_2k 53.48 ms 53.60 ms +0.2% 0.3% 18.7% (declared 15%)
prometheus.render_500 12.72 ms 12.69 ms -0.2% 0.3% 78.61% (declared 15%)
state.derive_max_warm 105.27 ms 105.50 ms +0.2% 0.5% 15%
webui.render_rows_500 13.60 ms 13.63 ms +0.2% 0.8% 25%
cluster.parse_summaries_6k 60.33 ms 60.20 ms -0.2% 0.6% 16.58% (declared 15%)
cronexpr.parse_simple 31.69 ms 31.62 ms -0.2% 0.9% 31.56% (declared 15%)
dag.adopt_scan_500 43.49 ms 43.41 ms -0.2% 0.9% 45.99% (declared 25%)
cronexpr.parse_complex 15.51 ms 15.53 ms +0.2% 0.7% 64.49% (declared 15%)
job.stream_capture_120k 29.89 ms 29.94 ms +0.2% 0.6% 33.45% (declared 15%)
mem.rss_daemon_import 37.35 MB 37.41 MB +0.2% 0.0% 25%
webapi.jobs_payload_500 39.96 ms 40.02 ms +0.2% 0.5% 25.03% (declared 15%)
config.parse_yaml_300 133.29 ms 133.49 ms +0.2% 0.8% 15%
startup.import_daemon 137.58 ms 137.23 ms -0.1% 0.3% 25%
redact.secrets_5k 15.14 ms 15.12 ms -0.1% 0.5% 66.05% (declared 15%)
tui.log_search_20k 11.40 ms 11.42 ms +0.1% 0.5% 87.68% (declared 15%)
config.reload_warm_50 31.31 ms 31.36 ms +0.1% 0.9% 31.93% (declared 25%)
state.list_documents_600 31.96 ms 31.91 ms -0.1% 0.2% 37.55% (declared 15%)
state.mutate_document_1k 97.32 ms 97.19 ms -0.1% 1.2% 51.38% (declared 25%)
dag.plan_claim_10k 10.62 ms 10.61 ms -0.1% 1.6% 94.16% (declared 15%)
state.kv_roundtrip_200 21.62 ms 21.59 ms -0.1% 2.6% 46.26% (declared 15%)
config.parse_yaml_3k 1.338 s 1.337 s -0.1% 1.4% 15%
resources.monitor_stop_100 122.98 ms 123.11 ms +0.1% 0.7% 25%
loop.stall_completions_500 5.62 ms 5.62 ms -0.1% 1.3% 88.91% (declared 25%)
startup.validate_config_100 158.69 ms 158.66 ms +0.1% 0.7% 25%
loop.stall_jobs_500 14.84 ms 14.83 ms -0.1% 0.9% Not enforced
config.reload_warm_include_50 67.72 ms 67.66 ms -0.1% 0.4% 25%
redact.adversarial_10k 25.53 ms 25.55 ms +0.1% 0.4% 39.17% (declared 15%)
mem.jobconfig_2k 1.63 MB 1.63 MB +0.1% 0.1% 30.72% (declared 15%)
startup.job_set_id_100 163.57 ms 163.51 ms +0.1% 0.3% 25%
push.seal_500 77.42 ms 77.47 ms +0.1% 0.4% 15%
dag.advance_quiescent_chain 53.65 ms 53.68 ms +0.1% 0.4% 25%
json.roundtrip_3k 435.09 ms 434.86 ms -0.1% 0.4% 15%
dag.list_runs_warm 6.75 ms 6.75 ms +0.0% 0.6% 29.64% (declared 25%)
tui.dag_graph_paint_2k 194.10 ms 194.15 ms +0.0% 0.2% 15%
mem.rss_version 14.45 MB 14.45 MB +0.0% 0.1% 25%
config.jobconfig_3k 43.23 ms 43.24 ms +0.0% 1.1% 23.13% (declared 15%)
webui.radar_walk_500 290.0 us 290.0 us -0.0% 0.0% 689.7% (declared 25%)
webui.append_line_5k 10.0 us 10.0 us +0.0% 6.3% 25%
webapi.jobs_bytes_500 181.42 KB 181.42 KB +0.0% 0.0% 15%
webapi.jobs_gzip_500 5.19 KB 5.19 KB +0.0% 0.0% 15%
mem.crontab_10k 2.80 MB 2.80 MB +0.0% 0.0% 17.85% (declared 15%)
mem.retired_output_1k 0.17 MB 0.17 MB +0.0% 0.0% 296.4% (declared 15%)

Full Changelog: 1.2.58...1.2.59