feat: allowlist the composed sources Sirens Deep may load #99

Merged
coilysiren merged 8 commits from feat/sirens-deep-compose into main 2026-08-11 05:50:30 +00:00
Member

First increment of #98. Establishes the safety boundary before any bundle is wired, because the boundary is the expensive part to get wrong.

What lands

agent/compose/roles.kdl names every composed source Sirens Deep may load, governed by two rules.

No global repositories. The host profile declares global profile and global lore. Either would put private operating context into an agent that answers strangers in a guild the operator does not moderate. This file declares no repository and no global at all.

Exact names, never globs. personal-preference-* silently includes personal-preference-social, which is the one preference source that must not compose here. An organization can own a favorite colour; it cannot own a person's social accounts. Listing sources by exact name makes every addition a reviewed line in a diff.

The nine approved sources are the public-safe tastes and the house style, all mirrored on the public Coilyco website, and all reframed onto the organization in coilyco-bridge/agentic-os-kai#853.

Enforcement, negative-tested

internal/community/compose_test.go fails when a source is outside the reviewed set, when an entry is a glob, or when a global repository appears. The approved set is duplicated in the test on purpose, so widening the surface changes a test rather than only a config file.

Each guard was verified to actually fire:

probe [composed-skill "personal-preference-social"] -> CAUGHT
probe [composed-skill "personal-preference-*"]      -> CAUGHT
probe [global lore]                                 -> CAUGHT

Prerequisite confirmed

#98 flagged agent-compose inside the release image as unverified, and the whole build-time bundling approach depends on it. It is present: docker/dev-base/install-common.sh installs it to /usr/local/bin/agent-compose with a SHA256 check and an acompose symlink, and verify-common.sh asserts agent-compose version runs. Build-time bundling is viable.

Verified from the image sources rather than by running the image, since no Docker daemon was available.

Not in this PR

The compose request, per-role bundles baked into the image, the SIRENS_DEEP_ROLE selector, inverting the persona validators so the markers become required for Deep and stay forbidden for Echo, and per-role prompt snapshots.

Verification

pre-commit run --all-files exit 0, go vet, and go test -count=1 ./... all pass.

First increment of #98. Establishes the safety boundary before any bundle is wired, because the boundary is the expensive part to get wrong. ## What lands `agent/compose/roles.kdl` names every composed source Sirens Deep may load, governed by two rules. **No global repositories.** The host profile declares `global profile` and `global lore`. Either would put private operating context into an agent that answers strangers in a guild the operator does not moderate. This file declares no repository and no global at all. **Exact names, never globs.** `personal-preference-*` silently includes `personal-preference-social`, which is the one preference source that must not compose here. An organization can own a favorite colour; it cannot own a person's social accounts. Listing sources by exact name makes every addition a reviewed line in a diff. The nine approved sources are the public-safe tastes and the house style, all mirrored on the public Coilyco website, and all reframed onto the organization in `coilyco-bridge/agentic-os-kai#853`. ## Enforcement, negative-tested `internal/community/compose_test.go` fails when a source is outside the reviewed set, when an entry is a glob, or when a global repository appears. The approved set is duplicated in the test on purpose, so widening the surface changes a test rather than only a config file. Each guard was verified to actually fire: ``` probe [composed-skill "personal-preference-social"] -> CAUGHT probe [composed-skill "personal-preference-*"] -> CAUGHT probe [global lore] -> CAUGHT ``` ## Prerequisite confirmed #98 flagged `agent-compose` inside the release image as unverified, and the whole build-time bundling approach depends on it. It is present: `docker/dev-base/install-common.sh` installs it to `/usr/local/bin/agent-compose` with a SHA256 check and an `acompose` symlink, and `verify-common.sh` asserts `agent-compose version` runs. Build-time bundling is viable. Verified from the image sources rather than by running the image, since no Docker daemon was available. ## Not in this PR The compose request, per-role bundles baked into the image, the `SIRENS_DEEP_ROLE` selector, inverting the persona validators so the markers become required for Deep and stay forbidden for Echo, and per-role prompt snapshots. ## Verification `pre-commit run --all-files` exit 0, `go vet`, and `go test -count=1 ./...` all pass.
feat: allowlist the composed sources Sirens Deep may load
All checks were successful
ci / test (pull_request) Successful in 35s
ci / publish-echo-image (pull_request) Has been skipped
ed37a25dad
First increment of issue 98. Establishes the safety boundary before any
bundle is wired, because the boundary is the part that is expensive to
get wrong.

agent/compose/roles.kdl names every composed source Sirens Deep may
compose. It declares no repository and no global. The host profile
declares `global profile` and `global lore`, and either would put private
operating context into an agent answering strangers in a guild the
operator does not moderate.

Sources are listed by exact name rather than by glob. That is the point:
`personal-preference-*` silently includes personal-preference-social,
which is the one preference source that must not compose here. An
organization can own a favorite colour; it cannot own a person's social
accounts.

internal/community/compose_test.go enforces all three properties and each
guard is negative-tested. Adding personal-preference-social, a
personal-preference-* glob, or `global lore` each fails the suite. The
approved set is duplicated in the test so widening the surface changes a
test, not only a config file.

Prerequisite confirmed while writing this: agent-compose ships in the
dev-base release image at /usr/local/bin/agent-compose, checksum-verified,
with verify-common.sh asserting it runs. Build-time bundling is viable.

Not yet wired: the compose request, the per-role bundles in the image, the
SIRENS_DEEP_ROLE selector, the inverted persona validators, and per-role
prompt snapshots.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Update agent/compose/roles.kdl
Some checks failed
ci / test (pull_request) Has been cancelled
ci / publish-echo-image (pull_request) Has been cancelled
2b8a247909
Update agent/compose/roles.kdl
Some checks failed
ci / test (pull_request) Has been cancelled
ci / publish-echo-image (pull_request) Has been cancelled
995d0fd600
feat: compose the creator, social, and community skillset
Some checks failed
ci / test (pull_request) Has been cancelled
ci / publish-echo-image (pull_request) Has been cancelled
b1fde3d821
Follows the role change to creator and the personal-preference-* wildcard,
which is now safe because personal-preference-social was reframed onto the
organization and names no member.

Adds the aos creator surface that fits a conversational agent. The aos role
is composed-skill "writing-*", which reaches the three writing-social-*
sources: adapting to an active community, running an editorial loop, and
handling corrections and moderation-adjacent moments.

It does not reach tooling-discord-community-host, because that is tooling-*
rather than writing-*. That skill is the closest match in the catalog to
what this agent actually does, so it is listed explicitly.

Three writing-* sources are deliberately left out.
writing-content-linkedin-video and writing-public-repos are production
formats rather than conversation, and writing-voice-observer-narrator
constrains its subject to a passive observer that is "not an active
agent", which contradicts an agent that answers and acts.

The allowlist test changes shape. Banning globs was a proxy for the real
rule and it blocked legitimate widening once social became safe. The
invariant is now direct: no pattern may reach a denied source. kai-*,
kai-career, writing-kai-*, global lore, and an unapproved name each fail.
writing-kai-* is caught because it reaches writing-kai-linkedin, a member's
personal channel voice.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Update agent/compose/roles.kdl
Some checks failed
ci / test (pull_request) Has been cancelled
ci / publish-echo-image (pull_request) Has been cancelled
c2369b1fd9
Update agent/compose/roles.kdl
Some checks failed
ci / test (pull_request) Has been cancelled
ci / publish-echo-image (pull_request) Has been cancelled
49a3d62dcf
Update agent/compose/roles.kdl
Some checks failed
ci / test (pull_request) Failing after 19s
ci / publish-echo-image (pull_request) Has been skipped
f9d7e79f11
fix: align the compose guards with the renamed catalog
All checks were successful
ci / test (pull_request) Successful in 50s
ci / publish-echo-image (pull_request) Has been skipped
bf8de9d8fe
CI failed on the updated roles.kdl for three reasons. Two were the guards
being wrong rather than the configuration.

Globals are now allowed for public repositories. The previous rule banned
every global, which rejected agent-compose, sirens-echo, and the public
profile, all public. The rule that matters is that no global resolves to a
private repository, so the guard resolves each global to its declared path
and fails on coilysiren/lore, the private overlays, and the deployment and
infrastructure surfaces. An undeclared global also fails.

The denied set follows the catalog renames. writing-kai-linkedin became
kai-linkedin-voice, and kai-linkedin-video, kai-bio-surface, and
kai-public-repos join it. kai-public-repos matters most: it names four
private sibling repositories, so a glob reaching it hands an agent that
list.

tooling-customer-success-* is approved after reading both sources. Signal
routing turns support contact into resolution and product signal, and
trust repair handles a failure or broken promise through impact
acknowledgment and bounded ownership. Both are generic craft with no
personal content, and both fit an agent that will sometimes be wrong in
front of a community.

writing-* replaces the enumerated writing entries, which is only safe
because the personal sources moved to the kai- prefix.

Every guard is negative-tested. A private global, an undeclared global, a
kai-* glob, a denied source by exact name, and an unapproved source each
fail the suite.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Co-authored-by: Kai Siren <coilysiren@gmail.com>
Co-authored-by: Claude <noreply@anthropic.com>
Sign in to join this conversation.
No reviewers
No milestone
No project
No assignees
2 participants
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-gaming/sirens-echo!99
No description provided.