Questify

2.14.1

Released

Fixes

  • Every investigation the newly deployed worker ran failed against OpenAI with Invalid 'max_output_tokens': integer below minimum value. Expected a value >= 16, but got 12 instead. The worker asked for a 12-token answer because AI_MAX_RESPONSE_TOKENS is set as a Fly secret on questify-worker — an app that predates this worker and still carries the secrets of the service that held its name — and a Fly secret takes precedence over the same key in apps/worker/fly.toml, which says 1200. OpenAI is the only provider that refuses a budget that small; Anthropic, DeepSeek and OpenRouter would each have returned a truncated fragment for an investigation the user had already been charged for, with nothing marking the row failed and nothing refunding it. getWorkerEnv() now floors the budget at the same 1200 the narrative route already floors it at, and says so in the log when it does. (#579)

Technical changes· 5 entries for database, tooling, CI, tests and docs

CI/CD

  • The worker deploy now refuses to ship when a Fly secret shadows a key the Fly config's [env] block declares. The machine gives no sign of which value it used, so a shadowed key leaves the config describing a deployment that does not exist — which is how AI_MAX_RESPONSE_TOKENS read 1200 in the repository and 12 on the machine. It names the key and the one command that removes it. (#579)

  • The Release workflow no longer fails a worker deploy that succeeded. apps/worker/fly.toml asks Fly for the immediate deploy strategy, which is right for a one-machine poller with no traffic to drain and means flyctl deploy returns once Fly has accepted the update rather than once the machine is running — so the check that followed it read the machine mid-swap, in replacing, and called that "no machine is in the 'started' state". It failed the 2.14.0 release on a worker that reached started seconds later, and it would have failed every release the same way. The check now reads the machine list until one is started — twenty readings fifteen seconds apart — and still fails when none ever is, because a worker that silently never runs is the whole of #556. (#579)

Tests

  • Covered getWorkerEnv(), which nothing had: the response budget defaults to the floor, a larger configured value is kept, and a value below the floor — or an empty one, which the schema turns into undefined despite its default — is raised and warned about rather than sent to a provider. (#579)

  • Pinned the deploy's shadow guard to the config it guards: that [env] is where AI_MAX_RESPONSE_TOKENS is declared, that the guarded keys are read out of $FLY_CONFIG rather than listed in the workflow, and that a shadowed key exits non-zero instead of warning. (#579)

  • Pinned that the machine check reports the wait it actually performs. The loop takes its first reading immediately and sleeps only between attempts, so attempts * interval overstated the budget by one interval in the one message anyone reads when the gate fails. (#579)