Roster // the context an agent loads

housecast

A YAML driven roster framework for agent context

You changed what a role is allowed to do. The evaluation that checked it did not notice.

boundaries:
  suggest-external-comms:
    owner: advocate

roles:
  frontend:
    defers: ["build-foundational-software", "modify-live-backend"]
    scoped:
      - name: suggest-external-comms
        scope: "labels, empty states, and error text inside a
          surface you own, never words addressed outward"

The frontend role never declares what it owns. The boundary's own owner line is what makes this a deferral, and the scope string is what a grader reads a transcript against.

  • Preview
  • MIT
  • Python

The problem, and what it costs to leave alone

A charter written as prose cannot fail a test.#

An agent's role usually lives in a prompt file. What it does, what it may touch, what it hands to someone else. That file is the entire specification, and the only thing enforcing it is that the model read it carefully on the way past.

Add a second role and the boundaries between them get stated twice, in two places, in whatever words each file reached for. By the seventh, the honest answer to which role owns a given capability is that it depends who you ask, and nothing in the system disagrees with either answer.

The evaluation is a separate artifact, and that is where it costs. Someone wrote challenges against the roles as they were. Then a role changed. The board still runs, still reports a pass rate, and has quietly stopped describing what ships. Nothing announces the gap, because both halves are healthy on their own.

What it does about it

One file is the source, and the board is a consequence of it.#

housecast reads roles, personalities, and boundaries from one YAML roster. It resolves each role's personality meld and its boundary allocation, derives the identity primitives including a favorite colour solved jointly across the whole roster, and emits an immutable bundle with a manifest and a trace.

Boundary allocation is not something a role declares. A boundary names its owner, and every other role either defers it or holds a stated slice of it. A role cannot claim what it does not own, because the claim is derived from the other side of the relationship rather than asserted on this one.

The challenge board derives from that same file. Adding a boundary or changing an adjacency changes which challenges exist, so a role cannot change without changing what gets tested. A human still writes the prompt into each derived challenge, and a human scores the run, because the thing being judged is whether a transcript honoured a sentence.

What it looks like in use

Three keys, and the fourth thing is derived.#

A roster carries boundaries, personalities, and roles, plus an invariant appended verbatim to every set of instructions it emits. Each of the first three is a definition with a body, and a role points at them by name rather than restating them.

What a role states is what it defers and what it holds within a scope. What it owns arrives from the other direction, off the boundary's own owner line. The shipped roster spells the rule out in its own header: a role carries its deferrals, then its scoped grants, then the one boundary whose owner is that role, and it may not declare that last one itself.

The scope string is prose and it is load-bearing. It is the sentence a grader reads a transcript against, which is why "labels and error text inside a surface you own" earns its place and "handles frontend copy" would not.

What it does not do

It composes context. It runs nothing.#

housecast emits a bundle and stops. acompose is downstream of it, rendering that bundle into harness surfaces and launching them. The two are often read the other way round, and that inverts the relationship the project exists to establish.

It is not a prompt manager and not an agent framework. No runtime, no model client, no orchestration. The grading half holds no runner either, which is the seam that lets a board be regraded without re-running it and a run be repeated without regrading it.

There is no PyPI release yet, so consumers depend on it from Forgejo with uv. The Go engine it was ported from still exists and still composes, and the two are held byte-identical until that one is deleted.

Repository and docs

Reference#