Skip to content

Seal function — the Starlark-callable resource #662

Closed

Description

@David-Noble-at-work

Feature: #625 · split out of #643 (USER, 2026-08-24), which now covers json, yaml, and mem only.

function is the outlier among the Unpacker four and does not fit the template the earlier phases established.

Why it is separate

Six exported fields, against one for service and three for git:

Compiled        []byte  `json:"-" yaml:"-"`
CompilerVersion uint32  `json:"-" yaml:"-"`
FuncName        string
ParamNames      []string
NumParams       int
OriginalPos     string

Four of them carry no codec tag, so unlike git's ref/head they participate in the default marshaling of any structure that embeds them.

It carries the Starlark-callable path. A function resource holds a compiled *starlark.Function, and its bytecode travels in the graph document's content section — pkg/op/starlarkbridge/invoker.go exists for it. That makes its Unpack the most load-bearing of the four: rehydration has to reconstitute an executable, not just an identity.

Compiled and CompilerVersion are runtime state, not identity. Whether they belong on a sealed interface at all is a design question the other providers do not raise.

What the survey found — smaller than the file counts suggest

Five files outside the package name function.Resource. Four are doc comments (pkg/op/planner.go, pkg/op/receiver_registry.go, pkg/op/starlarkbridge/invoker.go, pkg/op/provider/mem/resource.go) — prose, not field reads.

The one real external use is a test assertion:

arrivedFunction, ok := transported[functionResource.URI()].(*function.Resource)

pkg/op/provider/plan/lifecycle_api_test.go:774 — a concrete type assertion that becomes .(function.Resource) under sealing. That is the first out-of-package consumer any phase has had to change, and the plan anticipated it: "a later phase with an out-of-package caller will have to decide differently."

Inherited from earlier phases

  • Register both op.RegisterResourceImplementation and op.RegisterResourceMint from the provider's init.
  • Identity governs the type id and the registry name; the implementation governs only reflection (Seal git and appnet #642).
  • A codec cannot set unexported fields — function does not use the type alias embed, so that specific trap does not apply, but any decode path assigning through exported fields needs the same scrutiny.
  • The generator carries method metadata across on its own since The generator must derive resource method metadata from the implementation, not the interface #658; a method that must be dispatchable still has to be declared on the interface.

Exit

function's resource is a sealed interface over an unexported struct; its content-section round trip is pinned (bytecode in, executable out); the lifecycle_api_test.go assertion reads through the interface; and the announced method metadata is unchanged.

Activity

  1. David-Noble-at-work commented on Aug 25, 2026

    @David-Noble-at-work
    ContributorAuthor

    mem moves into this issue (USER, 2026-08-24). It cannot be sealed separately.

    function.Resource embeds mem.Resource cross-package:

    type Resource struct {
    	mem.Resource
    	invoker starlarkbridge.Invoker
    	...
    }

    Sealing mem makes mem.Resource an interface and its struct unexported, and Go cannot embed an unexported type from another package. The two are coupled at compile time.

    Verified as isolated rather than a pattern: this is the only cross-package resource embed in the tree.

    The shape agreed

    function.resource embeds the mem.Resource interface rather than the struct. That is legal and preserves the method set — but it is a real semantic change worth reviewing carefully: function goes from is-a mem.Resource by value to holds-a mem.Resource. Storage, zero-value behaviour, and anything relying on the embedded struct being addressable all shift.

    mem itself is small — one exported field, Hash — and four files name mem.Resource outside its package. Check which of those are doc comments before assuming consumer changes; every phase so far has found them to be prose.

    Scope is now: seal mem, seal function, and resolve the embedding, in one PR.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions