Fire Arrow Server 1.13.0
Fire Arrow Server 1.13.0 has been released.
- (feature) Care plan Tasks automatically move to a terminal status when their validity window expires, so
Task?status=readyonly returns currently actionable work - (bugfix) GraphQL search modifiers such as
_tag:notnow work; previously they returned no results - (maintenance) Upstream HAPI FHIR JPA starter sync (release 8.10.0-2) and dependency updates
Task Expiration
Care plan activities that are opted into server-side scheduling produce Task resources. A Task starts in requested, moves to ready when its due time arrives, and is then meant to be picked up by a downstream consumer — a worker polling Task?status=ready, a Subscription dispatcher, or an external orchestrator.
Before 1.13.0, a Task that was never completed stayed in ready indefinitely. A Task?status=ready query therefore returned a growing mix of currently actionable work and stale tasks whose due time had long passed. Consumers had to filter expired work themselves, and a stale Task could be dispatched again long after it was clinically relevant.
1.13.0 adds an expiry sweeper that transitions ready Tasks to a terminal status once their validity window has passed. After the transition, the Task no longer appears in ready queries; polling returns only work that is still actionable. The terminal status defaults to cancelled, which already counts toward CarePlan completion where the server is configured that way.
How the validity window is determined
Each materialized Task carries a restriction.period.end value that marks when it stops being actionable. The expiry sweeper transitions any ready Task whose restriction.period.end is in the past. The end value is set at materialization time, based on the activity's repetition interval:
| Activity shape | How restriction.period.end is set |
|---|---|
| One-off Task (no repeat) | due time + default-validity-period |
| Repeating Task | due time + the validity looked up from task-validity-periods, using the smallest configured interval that is greater than or equal to the activity's repetition interval |
For example, with the default tiers PT1H → PT15M, P1D → PT1H, P1W → PT1D, an activity repeating every two hours uses the daily tier (PT1H of validity); a one-minute activity uses the hourly tier (PT15M); a monthly activity matches no tier and falls back to default-validity-period.
If a Task already carries an explicit restriction.period.end (for example, authored directly rather than materialized), that value is honored as-is.
Configuration
Expiration is on by default wherever CarePlan events are enabled. The behavior is controlled under fire-arrow.careplan-events.task:
| Property | Default | Description |
|---|---|---|
auto-transition-to-terminal | true | Master switch for the expiry sweeper. Set to false to keep the pre-1.13.0 behavior. |
auto-transition-terminal-state | cancelled | Terminal status applied to expired Tasks. Must be a terminal Task status: cancelled, failed, or entered-in-error. |
default-validity-period | PT4H | How long after its due time a Task remains actionable. Used for one-off Tasks and when no tier in task-validity-periods matches. |
task-validity-periods | PT1H: PT15M, P1D: PT1H, P1W: PT1D | Map of scheduling interval to validity period. Lookup uses the ceiling rule described above. |
auto-transition-to-ready | true | Symmetric gate for the existing due-time (requested → ready) transition. |
The expiry sweeper runs on the same schedule as the due-task sweeper, governed by fire-arrow.careplan-events.cleanup.expired-task-check-interval (default PT1M).
To reproduce the pre-1.13.0 behavior without a redeploy, set auto-transition-to-terminal: false. Existing Tasks that are already ready at upgrade time become eligible for expiry on the next sweeper run according to their restriction.period.end.
Per-CarePlan validity overrides (disabled by default)
For deployments that need different validity windows per CarePlan, 1.13.0 introduces a Fire Arrow extension that carries default-validity and a list of validity-period entries (frequency interval + validity, as ISO-8601 durations) directly on a CarePlan:
https://firearrow.io/fhir/StructureDefinition/careplan-task-validity
When enabled, CarePlan-level values fully replace the server-configured ones for that CarePlan — they do not merge with application.yaml. During $apply, the extension is deep-copied from the PlanDefinition onto the resulting CarePlan, so materialization always reads validity from the CarePlan alone; the PlanDefinition is never consulted at runtime. If a CarePlan already carries the extension, $apply does not overwrite it.
This is gated by fire-arrow.careplan-events.task.careplan-validity-extension.enabled, which defaults to false. Enabling it lets CarePlan authors influence server-side scheduling, so it should only be turned on in trusted, single-tenant deployments or where CarePlan authoring is otherwise restricted. Deployments that leave it disabled see no change — all validity is taken from application.yaml as described above.
SearchParameter and upgrade reindex
The expiry sweeper matches Tasks on Task.restriction.period.end. 1.13.0 deploys a new SearchParameter, fa-careplan-task-deadline, that indexes that field. It is deployed alongside the existing fa-careplan-due-tasks SearchParameter (which indexes Task.restriction.period.start).
When the new SearchParameter is created or updated on upgrade, HAPI FHIR automatically reindexes pre-existing Task resources so the deadline field becomes searchable. This is a one-time operation; on a sizeable Task table it may take a few minutes and briefly increase database load during the first startup after upgrade. No operator action is required — the reindex runs in the background and the server starts normally while it completes.
Bug Fixes
GraphQL search modifiers such as _tag:not returned no results
GraphQL search arguments that used a modifier — for example _tag: ":not=system|code" to find resources that do not carry a given tag — returned count: 0 while the equivalent REST search returned the correct results. Queries without a modifier worked normally.
The GraphQL search layer did not parse the :<modifier>=<value> prefix on token, string, and uri parameters. The whole string, including the :not= prefix, was passed to HAPI as a literal token value, so it matched nothing.
1.13.0 parses the modifier prefix and forwards it to HAPI's parameter handling. The following modifiers are now honored in GraphQL:
| Parameter type | Modifiers now supported |
|---|---|
token | :not, :text, :above, :below, :in, :not-in, :of-type |
string | :exact, :contains |
uri | :above, :below |
Queries without a modifier behave identically to before — the no-modifier code path is unchanged, so existing positive matches are unaffected. Only the documented :<modifier>=<value> form is recognized; non-conventional variants such as :not|..., not|..., or -... continue to be treated as literal token values rather than silently inferred as modifiers.
Reference parameter modifiers (:identifier, :[type]) are not affected by this change and remain out of scope.
Dependency Upgrades
1.13.0 syncs the embedded HAPI FHIR JPA starter with upstream hapifhir/hapi-fhir-jpaserver-starter master (release 8.10.0-2). This is a starter-level sync only; the HAPI FHIR core platform stays on 8.10.0, so unlike the 1.11.0 upgrade there is no HAPI JPA schema migration required for this release. Fire Arrow–specific Flyway scripts continue to apply automatically on startup as before.
A handful of additive configuration surfaces come through from upstream. All are optional and ship with safe defaults, so no configuration change is required to upgrade:
hapi.fhir.client— timeouts and connection-pool settings for the outbound FHIR client the server uses when it calls other FHIR endpoints. Exposed for deployments that tune outbound connection behavior.cross_partition_enabled— an optional flag enabling cross-partition Subscription delivery. Defaults tofalse; existing single-partition Subscription behavior is unchanged.- A Docker health check for the distroless server image.
No CVEs are addressed in this batch. The administration UI ships with routine patch updates.