Repository navigation
Description
Follow-up from #856, raised by an independent platform review of the core#856 → cli#1196 → obin-ai/module-robin#563 chain. The field itself is well placed; two obligations it creates are unrecorded.
1. Adding a field to codefly/module-contract/v1 is forward-incompatible, and nothing says so
Parse is strict by design, and the design intent is a security property — contracts/module/contract.go:714-718:
Decoding is strict: an unknown field is an error rather than a silently ignored intention, which is what refuses a contract carrying a tenancy, a build digest or an identity it is not entitled to assert.
The consequence is that adding any field to the schema is forward-incompatible for every reader built before it, while the schema string stays v1 and therefore gives the reader no version-skew signal: the schema check at parse() passes and the document then fails as malformed.
Reproduced against module-robin's real module.contract.codefly.yaml (which carries service: on three foreign_methods entries), parsing the same file through each core pseudo-version:
=== core v0.19.7-0.20261026194550-a8599bd3991a (pre-#856) ===
REFUSED: the module contract carries a field no contract declares
line 426: field service not found in type module.ForeignMethod
line 431: field service not found in type module.ForeignMethod
line 436: field service not found in type module.ForeignMethod
=== core v0.19.7-0.20261027183450-b71ee8cd6ed0 (#856 merged) ===
ACCEPTED, foreign methods: 3
docs/module-contract.md contains no versioning or additivity policy at all — grepped for additive, compatib, version step, unknown field and forward; the only hits are unknown-field refusal in unrelated sections.
Scope, measured rather than assumed. Only cli and core import core/contracts/module anywhere in the org; every obin-ai hit is a *_test.go. codefly-dev/authority has zero modulecontract.Parse/Load call sites in production or test code — it imports core/solutionhost for other reasons and modulecontract only for Operation* constants and in-memory Contract construction, neither of which the new field touches. That matters because all ten authority go.mod files, including the deployed local, hop and edge services, pin core …20261026092055, which is pre-#856.
So this is a rollout and documentation obligation on the CLI reader family, not a break in a deployed service. It still bites: a developer or a CI job on a CLI built against pre-#856 core hits field service not found at codefly run or gitops render, with nothing upstream warning them.
Closure: docs/module-contract.md states the additivity policy for codefly/module-contract/v1 — that fields may be added without a schema-string change, what that implies for readers, and the minimum core a reader needs for each added field (starting with service: → b71ee8cd).
Caveat on scope: code search only covers repos indexed and visible to the searching account.
2. The new field has no refusal rule and no accepted fixture
contracts/module/rules.go:95-100 names five foreign-method rules — foreign-method-name, -unique, -requires, -own-kind, -own-action — and each has a testdata/ fixture (foreign-method-declared-twice.yaml, foreign-method-requires-nothing.yaml, foreign-method-beyond-ceiling.yaml, foreign-method-other-kind.yaml, foreign-method-scope-without-action.yaml). None of those fixtures carries service:, and no rule constrains its shape.
This is conformance rather than safety: blank and whitespace selectors do fail closed downstream in the CLI. But this package declares itself home to every refusal rule, and its fixture corpus is what drives the independently released readers, so a field with neither is a gap in the corpus rather than a judgement that the field needs no rule.
Closure: an accepted fixture carrying service: so the corpus exercises round-tripping it, plus a shape rule if one is warranted.
Context
- fix(module): allow foreign methods to select their serving service #856 merged as
b71ee8cd6ed01abb09e164a74ee913d7ade1bf85with no reviews. - Consumer: fix(composition): route foreign methods to their selected service cli#1196, approved, and obin-ai/module-robin#563.
- Neither point blocks fix(module): allow foreign methods to select their serving service #856; both are obligations it created.
Activity