Skip to content

v0.8.0 - Execution subtrees

Latest

Choose a tag to compare

@satya-anguluri satya-anguluri released this 04 Sep 05:17
ed6b546

Walk a whole capability execution tree in one call, and get the actual tree from the actuator instead of one level of it.

⚠️ Breaking

  • GET /actuator/capabilityexecutions/{id} now nests to full depth. children holds trees rather than executions:
-{ "execution": { … }, "children": [ { … }, { … } ] }
+{ "execution": { … }, "children": [ { "execution": { … }, "children": [ … ] } ] }

A consumer reading children[].executionId must read children[].execution.executionId. An unknown or aged-out id still returns null.

The type has always been called ExecutionTree and previously returned an execution plus its direct children — so a capability that called a capability that called another reported the middle layer and stopped, and a caller who did not know to walk it received a complete-looking answer missing everything below depth one.

  • CapabilityExecutionQuery gained subtree(String). Third-party implementations of that interface need the new method; both bundled implementations have it.

Added

  • subtree(executionId) on CapabilityExecutionQuery — the root and every descendant in one call. childrenOf answers a single level, so reassembling a tree cost a round trip per level and left every caller writing the same traversal. In-memory: one pass to index by parent, then breadth-first. Over JDBC: a recursive CTE, exercised against H2 and MySQL 8.

  • Ordering is part of the contract. Root first, every node after its own parent, so a consumer builds the nested shape in a single pass with no sorting. Siblings are most-recent-first, tie-broken on execution id so that calls recorded in the same millisecond — a capability fanning out to several tools does exactly that — come back in a fixed order. Both implementations produce the same order.

  • Malformed graphs are bounded, not fatal. Nothing validates that parent links form a tree, so a record whose ancestor claims it as a parent would loop forever on a read path serving an actuator endpoint. The in-memory store tracks visited ids, the CTE bounds depth, and each returns a truncated tree rather than hanging. A duplicated id cannot detach already-attached children from the response.

  • A build. mvn verify now runs on every pull request and push to main, on a runner with Docker so the MySQL Testcontainers round-trips actually execute — they are @Testcontainers(disabledWithoutDocker = true) and skip without a daemon, which meant a green local run could be tests that never ran. The build reports totals and names any suite that skipped.

Fixed

  • Test isolation in JdbcCapabilityExecutionRoundTripTest — every test opened the same H2 database under its default name and none closed it, so rows leaked between tests. The kind of fault that passes alone and fails together.

Example

// Every descendant, ordered root-first, parents before children.
List<CapabilityExecution> tree = query.subtree(rootExecutionId);
GET /actuator/capabilityexecutions/{id}
{
  "execution": { "capabilityName": "Generate Course", "executionId": "exec-1", … },
  "children": [
    { "execution": { "capabilityName": "Generate Lesson", … },
      "children": [ { "execution": { "capabilityName": "Score Lesson", … }, "children": [] } ] }
  ]
}

Not in this release

subtree returns the same CapabilityExecution records the rest of the query interface returns rather than a new export type. An export shape that consumers persist and diff is a contract worth settling once, alongside the ordered execution events it will need to carry — so findByAttribute and a versioned export shape remain open.


io.capstead:capstead-starter:0.8.0 · CHANGELOG · Maven Central