Port the semantic layer from Go to the compositor, with the Go tests as the oracle #333

Closed
opened 2026-08-25 17:08:10 +00:00 by coilyco-ops · 1 comment
Member

Parent

#329

What to build

Move every semantic step out of Go into a real Python compositor, replacing the throwaway from #332.

What moves:

  • Personality meld resolution.
  • Boundary allocation, owner assignment, scoped grants, and the rejection of an owner declaring its own boundary.
  • Copy-contract validation, role-skill frontmatter validation, the three-paragraph rule, and the 400-word body cap.
  • Local library merge and conflict detection.
  • The OKLab centroid that derives each role's favorite color from its meld, including the chroma restoration and the 0.60 to 0.80 lightness clamp.
  • The role-by-tier compatibility matrix.

The existing Go tests in internal/person and internal/color are the specification. Read them as the requirements document rather than reimplementing from the prose docs, because the tests encode edge cases the prose does not.

Differential testing is the point of this slice. Both engines still exist while this runs, so compose all seven roles with each and diff the resulting bundles byte for byte. That oracle disappears the moment #339 deletes the Go side, so every parity question has to be settled here.

Acceptance criteria

  • A Python compositor performs every semantic step listed above.
  • All seven Core roles compose to bundles that are byte-identical between the Go engine and the compositor, and the comparison runs as a test.
  • Every negative case the Go tests cover is covered in Python, including owner-declares-own-boundary, missing personality binding, mismatched definition set, and out-of-band color.
  • Derived favorite colors match the Go output exactly for all seven roles.
  • Every cheat listed in #332 is discharged or explicitly carried forward with a reason.
  • Byte-identity holds under the frontier, commodity, and OSS tiers, since tier must not change selected context.

Blocked by

## Parent #329 ## What to build Move every semantic step out of Go into a real Python compositor, replacing the throwaway from #332. What moves: * Personality meld resolution. * Boundary allocation, owner assignment, scoped grants, and the rejection of an owner declaring its own boundary. * Copy-contract validation, role-skill frontmatter validation, the three-paragraph rule, and the 400-word body cap. * Local library merge and conflict detection. * The OKLab centroid that derives each role's favorite color from its meld, including the chroma restoration and the 0.60 to 0.80 lightness clamp. * The role-by-tier compatibility matrix. The existing Go tests in `internal/person` and `internal/color` are the specification. Read them as the requirements document rather than reimplementing from the prose docs, because the tests encode edge cases the prose does not. **Differential testing is the point of this slice.** Both engines still exist while this runs, so compose all seven roles with each and diff the resulting bundles byte for byte. That oracle disappears the moment #339 deletes the Go side, so every parity question has to be settled here. ## Acceptance criteria - [ ] A Python compositor performs every semantic step listed above. - [ ] All seven Core roles compose to bundles that are byte-identical between the Go engine and the compositor, and the comparison runs as a test. - [ ] Every negative case the Go tests cover is covered in Python, including owner-declares-own-boundary, missing personality binding, mismatched definition set, and out-of-band color. - [ ] Derived favorite colors match the Go output exactly for all seven roles. - [ ] Every cheat listed in #332 is discharged or explicitly carried forward with a reason. - [ ] Byte-identity holds under the frontier, commodity, and OSS tiers, since tier must not change selected context. ## Blocked by - Blocked by #332
Author
Member

Landed as e76e3cb, with 29ed388 and 8389c3d on top. The engine is housecast/, 6 modules and 3 test files, reading housecast/data/roster.yaml.

Acceptance criteria

  • A Python compositor performs every semantic step - personality meld resolution, boundary allocation with owner assignment and scoped grants, the rejection of an owner declaring or scoping its own boundary, copy-contract validation, role-skill frontmatter validation, the three-paragraph rule, the 400-word cap, the OKLab centroid, and the role-by-tier matrix. Local library merge is the one step not ported, carried forward in #373.
  • All seven Core roles byte-identical, as a test - yes, and the comparison is housecast/tests/test_parity.py, which builds the Go binary and diffs whole trees. 12 native-skills bundles plus 7 compiled ones.
  • Every negative case covered - owner-declares-own-boundary, owner-scopes-own-boundary, missing personality binding, role with no personalities, mismatched definition set, unknown deferred boundary, orphan personality, out-of-band color, overlong role body, under-three-paragraph body, frontmatter naming another skill, boundary missing a side, unsupported model tier.
  • Derived favorite colors match exactly for all seven - yes, including the 400-round iterative spread in color.Favorites, asserted against the Go roster snapshot.
  • Every #332 cheat discharged or carried forward - six discharged, four carried forward in #373 and in housecast/__init__.py.
  • Byte-identity across frontier, commodity, and OSS - yes, every tier each role declares, with a test that fails if the tier set silently narrows.

agent-compose verify accepts the Python output unmodified.

Three things worth knowing

The float64 risk did not materialize, but one line carries it. Go's math.Round rounds half away from zero and Python's round() rounds half to even. delinearize floors x + 0.5 instead, and that is the whole reason the two engines agree on a hex digit through a 400-round optimization.

The identity content digest was the only real debugging. It covers an anonymous struct in manifestContent that carries no json tags, so Go falls back to capitalized Go field names, and it is not a digest of the manifest's own identity block. Everything else matched on the first or second attempt.

One negative control was wrong and I only caught it by running it. I used #808080 as the too-gray case. It lands at OKLab lightness 0.5983 and trips the lightness floor before it ever reaches the chroma floor, so it tested nothing I meant. #a0a0a0 is the real control.

A process failure worth recording

3e8976f retitled the eval seat to Applied Scientist while this port was in flight. My generated YAML predated it, and I pushed e76e3cb with two failing parity tests because I chained the push after the test command instead of gating on it. 29ed388 fixes the drift and adds the guard that makes it loud: it compares every field housecast reads against both the Go person data and the Go roster snapshot, and names the file to regenerate. The body-level guard alone missed display_name, so I mutated the YAML and watched each half fire rather than trusting a green suite.

Two sources of truth exist until #339, and that is the cost of it.

#337 is now unblocked for the code half.

Landed as `e76e3cb`, with `29ed388` and `8389c3d` on top. The engine is `housecast/`, 6 modules and 3 test files, reading `housecast/data/roster.yaml`. ## Acceptance criteria * **A Python compositor performs every semantic step** - personality meld resolution, boundary allocation with owner assignment and scoped grants, the rejection of an owner declaring or scoping its own boundary, copy-contract validation, role-skill frontmatter validation, the three-paragraph rule, the 400-word cap, the OKLab centroid, and the role-by-tier matrix. Local library merge is the one step **not** ported, carried forward in #373. * **All seven Core roles byte-identical, as a test** - yes, and the comparison is `housecast/tests/test_parity.py`, which builds the Go binary and diffs whole trees. 12 native-skills bundles plus 7 compiled ones. * **Every negative case covered** - owner-declares-own-boundary, owner-scopes-own-boundary, missing personality binding, role with no personalities, mismatched definition set, unknown deferred boundary, orphan personality, out-of-band color, overlong role body, under-three-paragraph body, frontmatter naming another skill, boundary missing a side, unsupported model tier. * **Derived favorite colors match exactly for all seven** - yes, including the 400-round iterative spread in `color.Favorites`, asserted against the Go `roster` snapshot. * **Every #332 cheat discharged or carried forward** - six discharged, four carried forward in #373 and in `housecast/__init__.py`. * **Byte-identity across frontier, commodity, and OSS** - yes, every tier each role declares, with a test that fails if the tier set silently narrows. `agent-compose verify` accepts the Python output unmodified. ## Three things worth knowing **The float64 risk did not materialize, but one line carries it.** Go's `math.Round` rounds half away from zero and Python's `round()` rounds half to even. `delinearize` floors `x + 0.5` instead, and that is the whole reason the two engines agree on a hex digit through a 400-round optimization. **The identity content digest was the only real debugging.** It covers an anonymous struct in `manifestContent` that carries no json tags, so Go falls back to capitalized Go field names, and it is not a digest of the manifest's own `identity` block. Everything else matched on the first or second attempt. **One negative control was wrong and I only caught it by running it.** I used `#808080` as the too-gray case. It lands at OKLab lightness 0.5983 and trips the lightness floor before it ever reaches the chroma floor, so it tested nothing I meant. `#a0a0a0` is the real control. ## A process failure worth recording `3e8976f` retitled the eval seat to Applied Scientist while this port was in flight. My generated YAML predated it, and **I pushed `e76e3cb` with two failing parity tests** because I chained the push after the test command instead of gating on it. `29ed388` fixes the drift and adds the guard that makes it loud: it compares every field housecast reads against both the Go person data and the Go roster snapshot, and names the file to regenerate. The body-level guard alone missed `display_name`, so I mutated the YAML and watched each half fire rather than trusting a green suite. Two sources of truth exist until #339, and that is the cost of it. #337 is now unblocked for the code half.
Sign in to join this conversation.
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/agent-compose#333
No description provided.