Skip to content

SkillWeave v1.3.5

Choose a tag to compare

@github-actions github-actions released this 17 Aug 00:36
· 32 commits to main since this release

SkillWeave 1.3.5 does two things: it makes the runtime contracts from 1.3.0
hold what they promised, and it adds a routing layer that lets you drive
SkillWeave from one agent tool while the work runs in another.

Run it from one tool, execute it in another

Until now, the model that executed your work was whichever model the tool you
happened to be sitting in provided. If you plan in Codex and want the work done
by a different model, that was a manual affair.

1.3.5 makes both choices explicit and separate:

name: nightly
tier: balanced          # intent, not a model name
roles:
  ops:
    model: sonnet
    tool: { name: opencode, launch_command: "opencode run" }
  reviewer:
    model: gpt-4o       # no tool: runs where you are
  observer:
    model: haiku
    tool: { name: kilo, launch_command: "kilo exec" }
limits:
  timeout: 600
  max_retries: 2
  min_models_required: 2

A role with a target tool is dispatched there. A role without one runs in
place — staying inside your current harness is a first-class configuration,
not a fallback. Tiers name intent (fast, balanced, deep) and are resolved
to concrete models through Faigate, so a profile stays valid when the models
behind it change.

Three routing modes, all declared rather than inferred:

  • pin uses the named profile and decides nothing. An operator's override
    is never silently improved upon.
  • auto derives the tier from task complexity.
  • hybrid lets auto decide within bounds you declare — a floor tier, a
    ceiling tier, and per-role pins. Any bound that moved the decision is
    recorded as an adjustment, so the log never shows a tier nobody chose.

Every decision keeps requested and resolved apart: the mode, the profile, the
tier, the input that drove it, each adjustment, and what Faigate actually
returned. A later reader can tell what ran, not merely what was asked for.

A scheduler and a real runner

DagScheduler turns a task graph into batches. It rejects cycles by name
rather than dropping them, enforces max_parallel, and refuses to release the
dependents of a failed gate. It starts no processes and knows nothing about
runners — that separation is enforced by a test.

RunnerAdapter starts actual processes. Output is bound to the run as
ArtifactReceipt evidence rather than free text, exit codes are distinguished
from signals, cancel kills the whole process group and proves no descendant
survives, a timeout is a defined state rather than an unbounded wait, and a
worker that dies without a result is a failure with a message rather than a
silent success.

Write scopes are now claimed and released rather than merely checked, and the
claim is persistent. Overlap is decided on resolved absolute paths, so two
lanes that would write the same file never share a parallel batch.

Eight contracts that did not hold

These existed in 1.3.0 and looked correct. Each fix below ships with a test
that fails against 1.3.0 and passes here.

  • Compare-and-swap detected no conflicts. The guard read
    connection.total_changes, which is cumulative per connection and therefore
    never differs between two reads. Every stale write reported success. It now
    reads rowcount.
  • Journal sequences collided under concurrency. Allocation and insert are
    now one transaction. Eight writers appending two hundred entries previously
    persisted thirty-three.
  • The authority guard failed open. An unknown action returned True. It
    returns False, and an undeclared role holds no capability at all.
  • Preflight validated part of the envelope, and compared string prefixes
    where it meant resolved paths.
  • Checkpoints, handoffs and evidence lived in process memory and did not
    survive a restart. They are persisted in the same store as runs and
    transitions.
  • The state vocabulary disagreed with its own schema.
  • busy_timeout was never set explicitly. The value does not change
    behaviour — sqlite3.connect already defaults to 5000 — but a
    concurrency-critical property should not depend on a library default nobody
    chose, and a caller supplying their own connection could silently drop it.
  • executor.execute_step never executed anything. It is simulate_step.

Upgrading

cap update LangeVC/skillweave

The thirteen bundled skills stay at 1.3.0. None of them changed in this
release — only the runtime and the new routing layer did, and the bundle
version moves because its composition did. If your tooling expects bundle and
member versions to match, that assumption does not hold and was never intended
to: a bundle can ship a new composition of unchanged parts.

skillweave.routing and skillweave.runtime are not exported from the package
root and are reached by explicit submodule import. Treat them as evolving.

Compatibility

No breaking changes to the thirteen skills or their SKILL.md surfaces. The
runtime repairs change behaviour in the direction of the documented contract:
if you were relying on a compare-and-swap that never detected conflicts, or on
an authority check that returned True for unknown actions, that reliance was
on a defect and it is gone.

Requires Python 3.9+. Faigate is optional — routing profiles that pin concrete
models work without it.