Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,15 +2,15 @@

The Codefly language agent for typed, finite Go operations: generate a handler and harness, compile portable native artifacts, and emit container build recipes.

**Status: the agent process, the Builder load path, typed generation and the invocation harness are implemented. No release is published yet.** The `runnable-go` executable serves the agent lifecycle over gRPC and advertises `BUILDER`, so a runnable can be loaded, scaffolded and generated through the real agent. It advertises no Runtime: a Runnable's invocation process is supervised by its caller. [Issue #2](https://github.com/codefly-dev/runnable-go/issues/2) tracks the rest of the execution lifecycle: native packaging, build evidence and native/k3d qualification.
**Status: the agent process, the Builder load path, typed generation and the invocation harness are implemented. No release is published yet.** The `runnable-go` executable serves the agent lifecycle over gRPC and advertises `BUILDER`, so a runnable can be loaded, scaffolded and generated through the real agent. It advertises no Runtime: a Runnable is called, not supervised. [Issue #2](https://github.com/codefly-dev/runnable-go/issues/2) tracks the rest of the execution lifecycle: native packaging, build evidence and native/k3d qualification.

`pkg/generate` turns a runnable's declared contract into the typed Go bindings and the author handler scaffold. The bounded profile maps to `string`, `int64`, `bool`, generated structs and `List`, and the contract's independent `optional` (the key may be absent) and `nullable` (the value may be null) declarations map to four distinct Go spellings, one per combination, so neither state can stand for the other. Generated files carry a `//go:build go1.24` constraint: absence is kept by the `encoding/json` `omitzero` option, which an older toolchain ignores in silence rather than rejecting.

`main.go` is the agent process, at the repository root because the manifest it embeds is: `go:embed` cannot reach outside its own directory, and a second copy of the agent's identity could drift from the published one. It serves the agent lifecycle over gRPC through `agents.Serve` — identity, capabilities and plugin commands, behind core's auth, health and handshake — and registers the Builder it advertises.

`pkg/agent` serves that Builder. `Load` resolves the `RunnableLocation` the CLI sends and re-derives both the release identity and the directory from the workspace declaration, so a request naming one release while pointing at another's directory is refused rather than generated into. `Create` scaffolds the author's handler and `go.mod` without overwriting either, then writes the generated bindings and harness beside them, bound to the release `Load` resolved. `RunnableBuildInputs` and `Package` report `UNSUPPORTED`, the contract's own word for a phase an agent does not implement: this agent generates the harness but neither compiles nor archives it, so an artifact digest and a launch command could only be guessed.

`harness.go` and `codefly/main.go` are the generated invocation harness: the `codefly.runnable/v1` framing, the payload validation the bindings do not do, and the process lifecycle a launcher observes. Unlike the Python agent, which ships a harness package into the artifact, a Go harness is generated as standard-library-only source and compiled into the runnable — a contract carried beside a compiled binary could be edited after the build that measured it, while one compiled in is covered by the digest of the artifact that carries it. The harness shares the author's package so the caller's invocation identity reaches `Handle` through `InvocationFrom(ctx)`; the executable is a separate `main` because an imported package cannot also be one.
`harness.go` and `codefly/main.go` are the generated invocation harness: it **serves** the operation over Connect/HTTP JSON on the procedure core pins, carrying the payload validation the bindings do not do and the call lifecycle a caller observes. A call is the bounded input document as the whole body, with the Work Context, the effect identity and the deadline as core's own headers; a success answers with the bounded output document the same way. The launcher framing it replaced — `codefly.runnable/v1`, two document paths in the environment and a result file — is gone with the native placement it existed for. Unlike the Python agent, which ships a harness package into the artifact, a Go harness is generated as standard-library-only source and compiled into the runnable — a contract carried beside a compiled binary could be edited after the build that measured it, while one compiled in is covered by the digest of the artifact that carries it. The harness shares the author's package so the caller's invocation identity reaches `Handle` through `InvocationFrom(ctx)`; the executable is a separate `main` because an imported package cannot also be one.

Bindings do not replace payload validation. The generated types carry the value types, the null states and the array states and nothing more, so the harness enforces required fields, undeclared keys, the signed 64-bit integer range and the refusal to coerce against the raw payload before decoding input or accepting output.

Expand All @@ -31,9 +31,9 @@ The Go agent uses the same language-neutral handoff as Python. Adding it must no

## Shared baseline and delivery

This repository builds against `codefly-dev/core v0.3.31`. [Core PR #471](https://github.com/codefly-dev/core/pull/471) introduced `runnable.codefly.yaml`, agent kind `codefly:runnable` (`Agent_RUNNABLE`) and the immutable `RunnablePackage` and `RunnableBinding` contracts; [#473](https://github.com/codefly-dev/core/pull/473) and [#474](https://github.com/codefly-dev/core/pull/474) then closed [core #472](https://github.com/codefly-dev/core/issues/472), carrying a `RunnableLocation` through `Builder.Load`, adding `Builder.RunnableBuildInputs`, returning the native launch command from `Builder.Package`, and freezing the `codefly.runnable/v1` invocation framing. The agent name is `go`; distribution uses the `runnable-go` prefix.
This repository builds against `codefly-dev/core v0.3.31`. [Core PR #471](https://github.com/codefly-dev/core/pull/471) introduced `runnable.codefly.yaml`, agent kind `codefly:runnable` (`Agent_RUNNABLE`) and the immutable `RunnablePackage` and `RunnableBinding` contracts; [#473](https://github.com/codefly-dev/core/pull/473) and [#474](https://github.com/codefly-dev/core/pull/474) then closed [core #472](https://github.com/codefly-dev/core/issues/472), carrying a `RunnableLocation` through `Builder.Load`, adding `Builder.RunnableBuildInputs`, returning the archive's start command from `Builder.Package`, and freezing the invocation contract, which is now `codefly.runnable.served/v1`. The agent name is `go`; distribution uses the `runnable-go` prefix.

The agent/CLI interface and the invocation framing are now merged in core, and [runnable-python](https://github.com/codefly-dev/runnable-python) has qualified them with a working harness and native packaging. This repository implements that same proven contract — reusing its framing rather than defining a Go-specific one — and repeats the native and actual Kubernetes invocation path with a separate generated Go example.
The agent/CLI interface and the invocation framing are now merged in core, and [runnable-python](https://github.com/codefly-dev/runnable-python) has qualified them with a working harness and native packaging. This repository implements that same proven contract — reusing its seam rather than defining a Go-specific one — and both harnesses serve the identical routes and headers, which a test reads from core rather than restating.

Qualification includes real typed I/O, cross-language schema compatibility, failure/interruption, immutable version selection, reconnect behavior and scoped cleanup. Direct binary execution or a manually submitted Kubernetes Job alone does not establish the durable execution path.

Expand Down
6 changes: 3 additions & 3 deletions go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ module github.com/codefly-dev/runnable-go
go 1.27.0

require (
github.com/codefly-dev/core v0.5.1
github.com/codefly-dev/core v0.5.11-0.20260928114826-ae201a2288f1
google.golang.org/grpc v1.83.2
google.golang.org/protobuf v1.36.12
)
Expand Down Expand Up @@ -83,8 +83,8 @@ require (
github.com/santhosh-tekuri/jsonschema/v6 v6.0.3 // indirect
github.com/shirou/gopsutil/v3 v3.24.5 // indirect
github.com/shoenig/go-m1cpu v0.2.1 // indirect
github.com/tklauser/go-sysconf v0.3.16 // indirect
github.com/tklauser/numcpus v0.11.0 // indirect
github.com/tklauser/go-sysconf v0.4.0 // indirect
github.com/tklauser/numcpus v0.12.0 // indirect
github.com/xlab/treeprint v1.2.0 // indirect
github.com/yoheimuta/go-protoparser/v4 v4.14.2 // indirect
github.com/yusufpapurcu/wmi v1.2.4 // indirect
Expand Down
6 changes: 6 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,8 @@ github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
github.com/codefly-dev/core v0.5.1 h1:iDoG1cIm28GhL6msVyTMgInHwV+an28HaaEbTNrnCLs=
github.com/codefly-dev/core v0.5.1/go.mod h1:uG0HQy5iWnkuWUPa1LbbiPfEgzuxi1SzEKwcFPC6eoA=
github.com/codefly-dev/core v0.5.11-0.20260928114826-ae201a2288f1 h1:rIswIHavnUvU+SMsDro+eplJn+6NdKnSs1VXJNXA8co=
github.com/codefly-dev/core v0.5.11-0.20260928114826-ae201a2288f1/go.mod h1:SVF+t040X+9pAXAqBnNE/JkXwmw9peVuKnHKFSOzyzk=
github.com/codefly-dev/gortk v0.2.0 h1:7bOlS5valYz2zil+fZctQNcPCYBcPj86abcw9N8h1hQ=
github.com/codefly-dev/gortk v0.2.0/go.mod h1:dDWUMFgAP063OCGhTEpV7FFUj9+zTcxxO/nV80VKEwg=
github.com/containerd/errdefs v1.0.0 h1:tg5yIfIlQIrxYtu9ajqY42W3lpS19XqdxRQeEwYG8PI=
Expand Down Expand Up @@ -234,8 +236,12 @@ github.com/stretchr/testify v1.12.1 h1:EuwCh5fleGS7H32xRwO3wRGT7DxrDhLAT6FF8MpWD
github.com/stretchr/testify v1.12.1/go.mod h1:MDEgiDPPsNp5cuIrHPPCyornHKgEVbtFUmoNlxoYthg=
github.com/tklauser/go-sysconf v0.3.16 h1:frioLaCQSsF5Cy1jgRBrzr6t502KIIwQ0MArYICU0nA=
github.com/tklauser/go-sysconf v0.3.16/go.mod h1:/qNL9xxDhc7tx3HSRsLWNnuzbVfh3e7gh/BmM179nYI=
github.com/tklauser/go-sysconf v0.4.0 h1:7H0uAN+7RkwWRaxhYXDLqa5V3LPrJeV8wmD9dRUgPQU=
github.com/tklauser/go-sysconf v0.4.0/go.mod h1:8mTNWyog7H+MpKijp4VmKJAd2bbYQ2zuUwkYRbUArPI=
github.com/tklauser/numcpus v0.11.0 h1:nSTwhKH5e1dMNsCdVBukSZrURJRoHbSEQjdEbY+9RXw=
github.com/tklauser/numcpus v0.11.0/go.mod h1:z+LwcLq54uWZTX0u/bGobaV34u6V7KNlTZejzM6/3MQ=
github.com/tklauser/numcpus v0.12.0 h1:NR85qdvHA9pFse3x3weVZ0r0ST8R6l5RHbZrlRaqob4=
github.com/tklauser/numcpus v0.12.0/go.mod h1:ABHeXzJnr/qqwguhClkZKT1/8VABcYrsyUiUGobwWJg=
github.com/xanzy/ssh-agent v0.3.3 h1:+/15pJfg/RsTxqYcX6fHqOXZwwMP+2VyYWJeWM2qQFM=
github.com/xanzy/ssh-agent v0.3.3/go.mod h1:6dzNDKs0J9rVPHPhaGCukekBHKqfl+L3KghI1Bc68Uw=
github.com/xlab/treeprint v1.2.0 h1:HzHnuAF1plUN2zGlAFHbSQP2qJ0ZAD3XF5XD7OesXRQ=
Expand Down
2 changes: 1 addition & 1 deletion pkg/agent/agent.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ import (
// readMe is what a human or model reads to decide what it may ask of this
// process, so it states the current surface rather than the roadmap.
const readMe = `Codefly Runnable agent for Go: typed handler scaffolding, typed ` +
`bindings for the bounded contract profile and the ` + resources.RunnableProtocolV1 +
`bindings for the bounded contract profile and the ` + resources.RunnableServedProtocolV1 +
` invocation harness, generated into the runnable's own Go module, through ` +
`Builder gRPC.

Expand Down
2 changes: 1 addition & 1 deletion pkg/generate/bindings_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ func field(name string, t resources.RunnableFieldType) *resources.RunnableField

func contractOf(input, output []*resources.RunnableField) *resources.RunnableContract {
return &resources.RunnableContract{
Protocol: resources.RunnableProtocolV1,
Protocol: resources.RunnableServedProtocolV1,
Input: &resources.RunnableSchema{Fields: input},
Output: &resources.RunnableSchema{Fields: output},
}
Expand Down
Loading
Loading