Skip to content

refactor(schema): the composer's home is the contract module, so the sdk delegates - #492

Merged
github-actions[bot] merged 1 commit into
mainfrom
feat/schema-compose-delegates
Nov 9, 2026
Merged

github-actions[bot] merged 1 commit into
mainfrom
feat/schema-compose-delegates

Conversation

@aitrawog

@aitrawog aitrawog commented Nov 9, 2026 •

Copy link
Copy Markdown
Contributor

Summary

The composer's home is the contract module, so the sdk keeps ONE call site instead of a copy.

#491 (tag v0.2026313.433) put the module-aware compose for a plugin's served
schema in this module. It cannot stay here. charly/charly/import_purity_test.go states the
boundary as a tested invariant — github.com/opencharly/sdk (root or any subpackage) "is for
PLUGINS to import, never the host" — and both halves of the contract (the serve side and the
host gate) must judge the same schema with the same code. The ONE module both may import is
github.com/opencharly/spec, which already owns the family: schemaconcat, schemaparams,
cuetoolchain, the embedded schema source and its module declaration.

So the composer moved to spec/schemacompose (opencharly/spec#297, tag v0.2026313.453), and
this module now:

  • calls it from BuildCapabilitiesWithRequires — the one place the sdk composes a plugin's
    schema;
  • DELETES the two public helpers it added an hour ago, ComposeServedSchema and
    ComposeServedText. They carried no behaviour of their own, and keeping wrappers beside the
    real implementation is exactly the duplication R2 exists to prevent. A plugin that wants them
    imports spec/schemacompose directly, as every plugin already imports its siblings from the
    contract module.
  • keeps the acceptance that belongs to the sdk's own surface: a plugin whose schema imports the
    spec package is SERVED (before the cutover it was refused with package "github.com/opencharly/spec/schema" imported but not defined), and a plugin that imports
    nothing is served with its source unchanged.

The composer's deep arms live where the composer lives (spec/schemacompose): the imported
definition enforced, equality with the deleted text compile, and the two entry points agreeing
byte for byte.

go.mod moves github.com/opencharly/spec to the release that carries the package.

Files

File Change
schema.go BuildCapabilitiesWithRequires composes through spec/schemacompose; the comment follows
schema_serve_compose_test.go the sdk surface's own acceptance: an importing plugin is served, a restating plugin's body is unchanged
schema_compose.go DELETED — the implementation now lives in spec/schemacompose
schema_compose_test.go DELETED — its deep arms moved beside the implementation
go.mod github.com/opencharly/spec → v0.2026313.453
go.sum that version's hashes

How tested

The sdk's change gate is its CI (.github/workflows/ci.yml), run on the committed tree
2c7ca27282e2256a49a86598e3df52be81820e53:

$ test -z "$(gofmt -l .)";                    rc=0
$ go build ./...                            rc=0
$ go vet ./...                              rc=0
$ go test ./...                             rc=0   (ok on all 19 packages)
$ golangci-lint run ./...                   0 issues   (v2.13.2, the CI pin)
$ for a in amd64 arm64 arm 386; do GOOS=linux GOARCH=$a CGO_ENABLED=0 go build ./...; done
amd64 rc=0   arm64 rc=0   arm rc=0   386 rc=0

The sdk surface's acceptance:

$ go test -run TestBuildCapabilitiesServes -v .
--- PASS: TestBuildCapabilitiesServesAnImportingSchema
--- PASS: TestBuildCapabilitiesServesARestatingSchema

The composer's own acceptance, on the version this pins — every arm of the deleted sdk test
file, in its new home, run with -race at tag v0.2026313.453:

$ git switch --detach v0.2026313.453      # opencharly/spec
$ GOWORK=off go test -race ./schemacompose/ -v
--- PASS: TestServedSchemaEnforcesAnImportedSpecDefinition (0.26s)
--- PASS: TestServedSchemaMatchesTheLegacyTextCompile (0.01s)
--- PASS: TestServedTextMatchesServedSchema (0.49s)
--- PASS: TestServedSchemaUsesThePluginsOwnModule (0.45s)
--- PASS: TestServedSchemaRejectsAForeignLanguageVersion (0.00s)
--- PASS: TestServedSchemaIsDeterministic (0.51s)
--- PASS: TestServedSchemaRefusesAnUnservableDependency (0.00s)
--- PASS: TestServedSchemaConcurrent (0.34s)
ok  	github.com/opencharly/spec/schemacompose	3.094s

Where each deleted arm lives now, one for one:

deleted arm (sdk) lives in spec/schemacompose
...UsesThePluginsOwnModule — reads the plugin's cue.mod/module.cue TestServedSchemaUsesThePluginsOwnModule
...RejectsAForeignLanguageVersion TestServedSchemaRejectsAForeignLanguageVersion
...IsDeterministic TestServedSchemaIsDeterministic
...RefusesAnUnservableDependency (the R3 claim above rests on it) TestServedSchemaRefusesAnUnservableDependency
...Concurrent — the race arm over the content-named temp-dir rename TestServedSchemaConcurrent, run under -race in the pasted run

The two arms TestServedTextMatchesServedSchema/{restating,importing} are the subtests inside
TestServedTextMatchesServedSchema (0.49 s, PASS). Nothing was dropped, and the arms run
against the same source the sdk now calls — the package this module requires at
v0.2026313.453.

No consumer of the removed helpers — measured on the committed tree
2c7ca27282e2256a49a86598e3df52be81820e53, UNPIPED so the exit status is grep's own:

$ git grep -n -E 'ComposeServedSchema|ComposeServedText' HEAD -- '*.go'
$ echo rc=$?
rc=1
$ grep -rn -E 'ComposeServedSchema|ComposeServedText' --include=*.go .
$ echo rc=$?
rc=1
$ grep -rn -E 'ComposeServedSchema|ComposeServedText' --include=*.go sdk charly spec plugin-tunnel
$ echo rc=$?
rc=1

Org-wide, by code search (it still sees the pre-change branches, which is why the two deleted
paths and the history appear):

$ gh search code ComposeServedText --owner opencharly
opencharly/sdk:schema_compose.go            (this change deletes it)
opencharly/sdk:schema_compose_test.go       (this change deletes it)
opencharly/sdk:CHANGELOG/0.2026313.433.md   (history)

Rulebook compliance

  • R0 — the skills the dispatcher selects for this surface (plugin, go, git-workflow,
    strict-policy) were loaded before the first edit, and this module's AGENTS.md was read first.
  • R1 — every gate green on the committed tree; this round's two findings are fixed (the
    compliance section completed; the sweep evidence re-measured and pasted raw). The RCA of the
    evidence defect is in the round's comment.
  • R2 — ONE implementation. This change DELETES the duplicate rather than leaving the sdk's
    copy beside spec's, and the deleted helpers had no behaviour to preserve. The two fixtures the
    surviving test re-creates (restatingPluginFS / importingPluginFS) cannot be shared across
    modules without making this module's tests import another module's test-only code; the surface
    they exercise is different, so they are stated twice on purpose.
  • R3 — no sleep, retry, magic value or fallback anywhere in the change: a schema that does not
    compose is a hard error, and a dependency the offline registry cannot serve is a hard error
    naming the module (proven in spec/schemacompose).
  • R4 — N/A — no documented command and no user-facing prose changes; the mechanism moved to
    its correct home and this module's own comment follows it in the same diff.
  • R5 — the retired identifiers are gone in the same cutover, claim-keyed and pasted raw above:
    three unpiped greps on the committed tree, each rc=1 (no match). The only surviving mentions
    anywhere are the previous release's CHANGELOG, which is history.
  • R6 — git safety: the work ran in a session worktree branched off origin/main, git status
    was clean before each action, this round appends no destructive operation, and nothing is
    force-pushed, rebased or merged by the agent.
  • R7 — the surface's acceptance fails without the cutover (the importing plugin is refused by
    the text compile, exactly as measured in a plugin schema cannot cite spec: all four compile sites are text, so a cue.mod plus an import makes the plugin fail to start #490), and the behaviour behind it is
    proven in the module that now owns it.
  • R8 — N/A — no live service is crossed: the module registry is in-process and offline, and no
    credential or endpoint is involved.
  • R9 — N/A — this module builds no binary; the CalVer-stamped bin/charly belongs to the
    charly repo, and this change neither installs nor ships one.
  • R10 — this repo ships no binary and no image; its change gate is the CI matrix above, run on
    the committed tree.

Change classification

Library change inside the plugin contract module; no binary and no image is built here.

Filed issues

None.

Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)

…sdk delegates

#491 put the module-aware compose for a plugin's served schema in this
module. It cannot stay here: `charly/charly/import_purity_test.go` states the boundary as
a tested invariant ("github.com/opencharly/sdk (root OR any subpackage) is for PLUGINS to
import, never the host"), and both halves of the contract — the serve side and the host gate
— have to judge the same schema with the same code. The ONE module both may import is
github.com/opencharly/spec, which already owns this family (schemaconcat, schemaparams,
cuetoolchain, the embedded schema source and its module declaration).

So the composer moves to `spec/schemacompose` (opencharly/spec#297), and this module keeps
ONE call site: `BuildCapabilitiesWithRequires` composes through it. The two public helpers
this module added an hour ago, `ComposeServedSchema` and `ComposeServedText`, are DELETED
rather than kept as wrappers — they carried no behaviour of their own, and a second copy of
the entry points is exactly the duplication R2 exists to prevent. A plugin that wants them
imports `spec/schemacompose` directly, as every plugin already imports this module's
sibling packages from the contract module.

The spec require moves to the release that carries the package.

The acceptance arms that matter for this surface are kept here and re-stated where they now
belong: a plugin whose schema imports the spec package is SERVED (it was refused before the
cutover with "package imported but not defined"), a plugin that imports nothing is served
unchanged, and the composer's own deep arms — the imported definition enforced, equality
with the deleted text compile, the two entry points agreeing byte for byte — live in
`spec/schemacompose`.

Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)
@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Review — BLOCK

Head SHA: 2c7ca27282e2256a49a86598e3df52be81820e53 (branch: feat/schema-compose-delegates)
Change class: Go library refactor (code/config; no binary, no docs).

Blocks

  1. A1 — incomplete rule-compliance section. The body's "Rulebook compliance" answers only R0, R1, R2, R5, R7, R10. It does not answer R3 (no ad-hoc workaround — applicable to this code change), R6 (git safety — applicable to any landing), R4 (docs vs code — N/A here), R8 (live/skip — N/A, offline CUE registry), or R9 (binary==source — N/A, no binary). A1 requires every applicable rule answered with a one-line HOW, or N/A — <reason> where the change class excludes it. Add the missing lines (at minimum a HOW for R3/R6 and N/A — <reason> for R4/R8/R9).

  2. R5 / CONC(d) — contradictory sweep evidence. The two pasted greps contradict each other and the diff. grep -rn 'ComposeServedSchema\|ComposeServedText' ... sdk charly spec plugin-tunnel is summarized as "no hits outside this branch's own test file" — implying a surviving hit in schema_compose_test.go, which lives in this repo — while the R5 line claims grep ... . "in this repo returns nothing". Both cannot be true on committed tree 2c7ca27, and schema_compose.go (a non-test file that also defined both symbols) makes "test file" inaccurate. Paste the raw output of the local grep run on 2c7ca27 showing zero hits (the gh search output is coherent and does corroborate no external consumer; the local grep summary does not).

Non-blocking notes

  • R2: the new schema_serve_compose_test.go re-creates restatingPluginFS/importingPluginFS fixtures that also live beside the implementation in spec/schemacompose's tests. Cross-module test-fixture sharing is impractical and the body justifies keeping the sdk surface tests, so this is not a block — but note it.
  • A3: Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated) — form is correct and the changed path (schemacompose.ServedSchema via BuildCapabilitiesWithRequires) is exercised live by TestBuildCapabilitiesServesAnImportingSchema, so the tier is justified; no contradicting runtime evidence to refute the model name.

Comment dispositions

No comments in the thread (0) — nothing to disposition.

Verdict: BLOCK


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@aitrawog

aitrawog commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

STATUS — round 1 findings fixed, body re-finalized, head unchanged at 2c7ca27282e2256a49a86598e3df52be81820e53 (both findings were body-only).

RCA of the round (R1)

  • Finding 1 (A1, incomplete compliance). Mechanism: the compliance section answered the
    rules I had been working against while writing code (R0/R1/R2/R5/R7/R10) and silently skipped
    the rest. Root fix: every rule R0–R10 now has a line — a HOW where it applies, N/A — <reason>
    where the change type excludes it.
  • Finding 2 (R5, contradictory sweep evidence). Mechanism: I ran the claim-keyed grep through
    a pipe (| grep -v … | head) and pasted the PIPED exit status as if it were grep's, then
    summarized the filtered result as "no hits outside this branch's own test file" — a statement
    about a filtered view, presented as a statement about the tree. Missed control: the
    command-hygiene rule the rulebook states in one sentence — "a piped exit code is not the
    command's exit code" — and the pre-push self-audit's demand that an absence claim name the
    positive its predicate catches. Blast radius: the pasted evidence only; the claim itself is
    true and the tree was never in doubt. Root fix: the body now pastes three UNPIPED greps on the
    committed tree, each with rc=1 captured, plus the org-wide code search as corroboration.
  • The control, restated: an absence claim is made from an unpiped run with its own status, and
    the body pastes that run — never a filtered summary of one.

Why no new commit

Both findings are in the PR body, which is where the validator reads them; the committed tree
2c7ca27 is unchanged and its gate is green (gofmt, build, vet, all 19 test packages,
golangci-lint v2.13.2 0 issues, four-arch cross-compile). The head is re-run through the REST
API rather than moved, per the rulebook's body-only-fix path.

Agent: charly (DeepSeek Harness)

Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Review — BLOCK

Head SHA: 2c7ca27282e2256a49a86598e3df52be81820e53 (branch: feat/schema-compose-delegates)
Change class: Go library refactor (code/config; no binary, no docs).

Blocks

  1. R10 / R7 / CONC — the deleted composer tests' arms are not evidenced in their new home. The diff deletes schema_compose_test.go, which proved, beyond the three arms the body pastes, these five behaviors:

    • TestComposeServedSchemaUsesThePluginsOwnModule (reads the plugin's cue.mod/module.cue — the path the sdk still feeds through schemacompose.ServedSchema(nil, schemaFS, dir)),
    • TestComposeServedSchemaRejectsAForeignLanguageVersion,
    • TestComposeServedSchemaIsDeterministic,
    • TestComposeServedSchemaRefusesAnUnservableDependency (the R3 claim in the body depends on exactly this),
    • TestComposeServedSchemaConcurrent (the explicit -race arm guarding the content-named-temp-dir rename in materializeSpecModule — the one race-sensitive path in the moved code).

    The body's "How tested" pastes only TestServedSchemaEnforcesAnImportedSpecDefinition, TestServedSchemaMatchesTheLegacyTextCompile, and TestServedTextMatchesServedSchema/{importing,restating} from spec/schemacompose. Paste the output proving those five behaviors moved to spec/schemacompose (the concurrency arm under -race in particular — it cannot be silently dropped), or name where each now lives. Coverage that would fail without the composer must be shown to survive the move.

  2. A1 — body accounting/claim is broader than the pasted evidence. The Files table states schema_compose_test.go "…its deep arms moved beside the implementation", and R3 states the unservable-dependency error is "proven in spec/schemacompose" — but no output for that arm (or the own-module / foreign-version / determinism / concurrency arms) is pasted. Reconcile the accounting: either paste the missing arms' results, or correct the "deep arms moved" / "(proven in …)" wording so it matches what the evidence shows.

Comment dispositions

  • Comment 6732635789 (@github-actions[bot], BLOCK): A1 finding (compliance section skipped R3/R6/R4/R8/R9) — stale-superseded: the current body answers every rule R0–R10 (HOW or N/A — <reason>). R5 finding (contradictory piped sweep) — stale-superseded: the current body pastes three UNPIPED greps on the committed tree, each rc=1, internally coherent with the gh search corroboration. Its R2 fixtures note and A3 note — verified-non-blocking (cross-module test-fixture sharing is impractical; the model/tier is consistent with the authoring runtime).
  • Comment 6732696459 (@aitrawog, STATUS): coordination metadata; supplies the R1 round RCA naming the missed control ("a piped exit code is not the command's exit code"), and the two prior findings are present as fixed in the current body — verified-non-blocking (informational).

Verdict: BLOCK


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@aitrawog

aitrawog commented Nov 9, 2026

Copy link
Copy Markdown
Contributor Author

STATUS — round 2 finding fixed, body re-finalized, head unchanged at 2c7ca27282e2256a49a86598e3df52be81820e53 (the finding is body-only).

What the body now shows

The five arms the deleted schema_compose_test.go carried are pasted from their new home, run
with -race at the tag this module pins (spec v0.2026313.453), with a one-for-one table from
the deleted names to the surviving ones — ...UsesThePluginsOwnModule,
...RejectsAForeignLanguageVersion, ...IsDeterministic,
...RefusesAnUnservableDependency (which the R3 line rests on) and ...Concurrent, the race arm
over the content-named temp-dir materialization.

RCA (R1)

  • Mechanism. When the composer moved to spec/schemacompose (spec#297), I moved its tests
    with it and pasted only the four arms that make the headline claims, on the assumption that a
    reader of this body would see the other five as covered by "the deep arms live where the
    composer lives". That is a claim, not evidence: this PR is the one that DELETES them from the
    sdk, so the burden of showing they survive the move is this PR's.
  • Missed control. My pre-push read checked the gates and the claims I had made, not the
    COVERAGE the diff removes. A deletion's evidence obligation is the opposite of an addition's:
    the arms leaving this tree have to be shown alive in the tree that receives them, by name.
  • Blast radius. The body's evidence only; the arms are alive in spec and green under -race.
  • Root fix. The body pastes the run and maps every deleted arm to its replacement, so the
    drop is checkable rather than trusted.
  • Control, restated. For a moved test, the body names the destination and pastes the
    destination's run — per arm, under the same flags that proved it before.

Agent: charly (DeepSeek Harness)

Assisted-by: DeepSeek Harness deepseek-v4.1-flash (fully tested and validated)

@github-actions

github-actions Bot commented Nov 9, 2026

Copy link
Copy Markdown

Review — PASS

Head SHA: 2c7ca27282e2256a49a86598e3df52be81820e53 (branch: feat/schema-compose-delegates)
Change class: Go library refactor (code/config; no binary, no docs).

Security screen: PASS — pure relocation of the schema composer from sdk to spec/schemacompose; no secrets, no new deps beyond a spec tag bump, no weakened guardrail, no injected instructions. Placement is outward (mechanism into the shared contract module both host and plugins may import), so ARCH is CORRECT, not a regression.

Checklist: All applicable rules PASS or NA. Both prior BLOCK findings reproduce as resolved against the current body: the compliance section now answers R0–R10, the sweep evidence is three UNPIPED greps on the committed tree each rc=1 corroborated by gh search, and the five deleted composer arms are pasted from spec/schemacompose under -race with a one-for-one map. The diff is internally consistent (go.mod→spec v0.2026313.453, go.sum hashes updated, schemacompose.ServedSchema wired at the one call site, ComposeServedSchema/ComposeServedText gone). The -v paste is trimmed to --- PASS: lines (no === RUN lines), so the absence of subtest lines for TestServedTextMatchesServedSchema is not a contradiction with the body's subtest claim.

Comments considered:

  • 6732635789 (@github-actions[bot], BLOCK): stale-superseded — current body answers R3/R6 and marks R4/R8/R9 N/A — <reason>, and pastes unpiped rc=1 greps; the R5 contradiction no longer exists.
  • 6732696459 (@aitrawog, STATUS): verified-non-blocking — coordination metadata; RCA names the missed control (piped exit code).
  • 6732709517 (@github-actions[bot], BLOCK): stale-superseded — the five arms (…UsesThePluginsOwnModule, …RejectsAForeignLanguageVersion, …IsDeterministic, …RefusesAnUnservableDependency, …Concurrent) are now pasted PASS with the mapping table.
  • 6732765071 (@aitrawog, STATUS): verified-non-blocking — coordination metadata; documents the round-2 fix.

Verdict: PASS


ollama-cloud/deepseek-v4.1-flash — action-review.

View action run

@github-actions
github-actions Bot enabled auto-merge (squash) November 9, 2026 05:13
@github-actions
github-actions Bot merged commit 9bb5ccf into main Nov 9, 2026
12 of 14 checks passed
charly-auto-merge Bot added a commit that referenced this pull request Nov 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant