feat(aos-eval)!: name the two abstractions the schema was already using #1196

Merged
coilysiren merged 2 commits from aos/claude/sj87-entity-attribute into main 2026-08-23 00:16:43 +00:00
Member

The shared grading layer claimed to be deployment-agnostic and hardcoded one deployment's vocabulary three times. axis_of was the clearest instance, with role-fit and personality (agent-compose's words) sitting in the layer that is supposed to know nothing about it. The same chain repeated in annotate as three header rows and in export as three fields.

Breaking: Challenge.role is entity. boundary, against, and trait collapse into attribute. Profile.group_order is entity_order and boundary_order is attribute_order. PairResult follows. Declaration schema is aos-eval.boundaries.v2, whose role: key is now entity:. Consumers pin by tag, so nothing breaks until a pin moves.

The abstraction

A challenge tests one entity for one attribute. The entity is what is under test, a composed role in one deployment and a deployed lane in another. The attribute is what is being tested about it, a boundary or a personality in one and a clause in the other.

test_type already said which kind, and the profile already let a deployment declare its own kinds, so the three concrete fields were one field wearing a deployment's clothes. axis_of is now derived rather than enumerated, in three lines instead of a chain, and a deployment can declare a test type this layer has never heard of and still get an axis with detail on it.

Also here

AGENT_COMPOSE.attribute_order listed three boundary slugs that stopped existing in agent-compose@12c7198. Corrected while the field was moving. personality now requires its attribute rather than leaving it optional, a tightening the collapse made free.

The declaration module keeps the name boundaries, because it declares the paired kind of attribute specifically, which is what a boundary is. Not every attribute pairs.

Validation

63 tests pass. ruff check and pre-commit run --all-files pass.

closes #1195

The shared grading layer claimed to be deployment-agnostic and hardcoded one deployment's vocabulary three times. `axis_of` was the clearest instance, with `role-fit` and `personality` (agent-compose's words) sitting in the layer that is supposed to know nothing about it. The same chain repeated in `annotate` as three header rows and in `export` as three fields. **Breaking:** `Challenge.role` is `entity`. `boundary`, `against`, and `trait` collapse into `attribute`. `Profile.group_order` is `entity_order` and `boundary_order` is `attribute_order`. `PairResult` follows. Declaration schema is `aos-eval.boundaries.v2`, whose `role:` key is now `entity:`. Consumers pin by tag, so nothing breaks until a pin moves. ## The abstraction A challenge tests one **entity** for one **attribute**. The entity is what is under test, a composed role in one deployment and a deployed lane in another. The attribute is what is being tested about it, a boundary or a personality in one and a clause in the other. `test_type` already said which kind, and the profile already let a deployment declare its own kinds, so the three concrete fields were one field wearing a deployment's clothes. `axis_of` is now derived rather than enumerated, in three lines instead of a chain, and a deployment can declare a test type this layer has never heard of and still get an axis with detail on it. ## Also here `AGENT_COMPOSE.attribute_order` listed three boundary slugs that stopped existing in `agent-compose@12c7198`. Corrected while the field was moving. `personality` now requires its attribute rather than leaving it optional, a tightening the collapse made free. The declaration module keeps the name `boundaries`, because it declares the paired kind of attribute specifically, which is what a boundary is. Not every attribute pairs. ## Validation 63 tests pass. `ruff check` and `pre-commit run --all-files` pass. closes #1195
feat(aos-eval)!: name the two abstractions the schema was already using
All checks were successful
ci / aos-cli-tests (pull_request) Successful in 25s
ci / aos-eval-tests (pull_request) Successful in 8s
ci / ward-doctor (pull_request) Successful in 13s
ci / gate (pull_request) Successful in 1m16s
cd40ef0f2b
The shared layer claimed to be deployment-agnostic and hardcoded one
deployment's vocabulary three times. `axis_of` was the clearest instance:

    if challenge.boundary and challenge.half: return f"{boundary}:{half}"
    if challenge.against:                     return f"role-fit:{against}"
    if challenge.trait:                       return f"personality:{trait}"
    return challenge.test_type

`role-fit` and `personality` are agent-compose's words, sitting in the layer
that is supposed to know nothing about it. sirens-echo declares only a boundary
test type, so two of those branches were dead there, and any deployment adding
a fourth kind fell through to a bare label with no axis detail. The same chain
repeated in `annotate` as three header rows and in `export` as three fields.

BREAKING CHANGE: `Challenge.role` is `entity`. `boundary`, `against`, and
`trait` collapse into `attribute`. `Profile.group_order` is `entity_order` and
`boundary_order` is `attribute_order`. `PairResult.role` and `.boundary` follow.
The declaration schema is `aos-eval.boundaries.v2`, whose `role:` key is now
`entity:`. Consumers pin by tag, so nothing breaks until a pin moves.

## What the abstraction is

A challenge tests one **entity** for one **attribute**. The entity is what is
under test, a composed role in one deployment and a deployed lane in another.
The attribute is what is being tested about it, a boundary or a personality in
one and a clause in the other. `test_type` already said which kind, and the
profile already let a deployment declare its own kinds, so the three concrete
fields were one field wearing a deployment's clothes.

`axis_of` is now derived rather than enumerated, in three lines instead of a
chain, and a deployment can declare a test type this layer has never heard of
and still get an axis with detail on it.

## Also

`AGENT_COMPOSE.attribute_order` listed three boundary slugs that stopped
existing in `agent-compose@12c7198`. Corrected to the four current ones while
the field was moving. `personality` now requires its attribute rather than
leaving it optional, which is a tightening the collapse made free.

The declaration module keeps the name `boundaries`, because it declares the
paired kind of attribute specifically, which is what a boundary is. Not every
attribute pairs.

63 tests pass. `ruff check` and `pre-commit run --all-files` pass.

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: ai
refactor(aos-eval)!: finish the swap, in the module name and the roster read
All checks were successful
ci / aos-eval-tests (pull_request) Successful in 8s
ci / ward-doctor (pull_request) Successful in 17s
ci / aos-cli-tests (pull_request) Successful in 53s
ci / gate (pull_request) Successful in 1m53s
fd2e310fdd
Two things the first commit left half-done, both of them the shared layer still
knowing one deployment's vocabulary.

BREAKING CHANGE: the `boundaries` command group and module are `attributes`,
the declaration schema is `aos-eval.attributes.v1` with an `attributes:` key,
and `--roster` takes an entity projection rather than agent-compose's
`person.json`.

## The module was named for one kind of attribute

I argued the `boundaries` name should stay because it declares the paired kind
specifically and not every attribute pairs. That is true and it is not a reason
to keep it. The pairing is what `inside` and `outside` already say on every
entry, and a module named for the one deployment shape it serves is exactly the
leak this change exists to remove. `Boundary` is `Attribute`, and a deployment
whose attributes are clauses no longer reads a word that belongs to another.

## The roster read was the bigger miss

`entity_header` still did this:

    spec = roster.get("roles", {}).get(entity)
    if boundary.get("owner") == entity: ...
    if spec.get("personalities"): ...

Renaming the function and leaving the body was a rename, not an abstraction.
`roles`, `boundaries`, and `personalities` are agent-compose's keys, so the
layer that must not know them was reading four of them.

The roster is now `{entity_order, entities: {name: {display_name, purpose,
notes}}}`, where `notes` are lines the deployment already composed. Owns,
defers, scoped, and traits are its words, so it renders them and this layer
prints them. That drops the function from twenty lines to eight and removes the
last place aos-eval could tell you what a personality is.

63 tests pass. `pre-commit run --all-files` passes.

Refs #1195

Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Agent-Role: ai
coilysiren deleted branch aos/claude/sj87-entity-attribute 2026-08-23 00:16:43 +00:00
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
coilyco-flight-deck/agentic-os!1196
No description provided.