Skip to content

Latest commit

 

History

History
200 lines (177 loc) · 12.7 KB

AGENTS.md

File metadata and controls

200 lines (177 loc) · 12.7 KB

Working in codefly-dev/core

github.com/codefly-dev/core (Go 1.27) is the shared library every codefly agent, the CLI, and user services depend on. It owns the resource model, the agent gRPC contracts, the network and configuration models, the proto schema source of truth, and the runner/companion surfaces.

It does not own: the codefly CLI (codefly-dev/cli, a private module — do not import it, and do not make CI depend on it), any individual agent implementation, or user workspaces. Core defines contracts that those fill in. When a change you need belongs to one of them, it gets made there.

How to behave

Fleet standard — handbook#68. These land hard here: core is a library, so a defect is observed downstream and the temptation is always to absorb it locally rather than fix what owns it.

  • Prove reuse before you build, and build the simplest thing that works; size is the signal: fill a gap on what the platform already has, keep a coding task to about 500 hand-written lines, and past 2,000 stop and ask the user. Both in full, with their tells, in the handbook.
  • Look for the codefly command before doing anything by hand. The CLI owns the fleet verbs — codefly agent deps --pin, codefly agent release --pin, codefly update deps, codefly update workspace, codefly generate proto — and each gates what a hand-edit skips: agent release runs the agent's CI before it tags, so a fleet bump cannot ship an agent that does not build. Read --help on the command group first. A bulk edit across repos that no command performed is the signal this step was skipped. See .claude/skills/release-core for the cascade after a core tag.
  • A gap in the tooling is a bug in the tooling — never a reason to reach around it. When something needs a step codefly, buf or a companion does not perform, the answer is a capability fixed in whichever tool owns it, named in the PR — never a hand-assembled substitute, not "just this once".
  • Never hack. Provide the best fix, even when it spans repos. The fix living in codefly-dev/cli or an agent repo is no reason to work around it here: open the PR there. When it cannot be fixed now, the deliverable is a precise issue against that owner plus a stopgap labelled as one.
  • Classify every change that makes something work, in the PR body: a fix at the place that owns the behaviour, or a hack.
  • Never hardcode what the system resolves — injected environment, derived ports, service addresses, credentials copied out of another component. Core is where that resolution is defined: ports come from network.ToNamedPort(), endpoints and credentials from the configuration flow. Typing one encodes something true only on one machine for ten minutes, and it fails quietly — a runtime missing a credential can skip registration silently, so the service boots, serves, and is simply absent.
  • Diagnose, do not pattern-match. "It started working when I set X" is not a diagnosis — set X back and confirm it breaks. Do not trust an error message before checking its claim: a guard here reporting a version contract failure has meant a stale embedded file, not a bad package.
  • Say what you did not verify. Unverified is not working: a unit run is not the tagged suite, nor is a boot. If you could not exercise it, the PR says so.

Build and test

Derived from .github/workflows/go.yml — CI pins Go 1.27.3 and every step is strict (nothing skips when a prerequisite is missing).

go test ./...                                      # the suite
go test ./resources/ -v                            # one package
go test -race ./... -timeout 10m                   # CI runs this separately
make check-coverage                                # thresholds in .testcoverage.yaml

CI additionally runs, and all of these are runnable locally:

Command Guards
go test ./... -tags=sandbox_e2e,nix_required needs bubblewrap, ripgrep, uv, nix
./scripts/check_cgo_free.sh (make check-cgo-free) cgo creep outside code/semantic
./scripts/check_version_tag.sh (make check-version-tag) version/info.codefly.yaml vs. published tags
./scripts/govulncheck.sh unsuppressed vulns that have an upstream fix
make buf-breaking schema breaks, PACKAGE rules
make buf-lint BASIC + COMMENTS: every field, enum value and message documented

Run buf checks through the Makefile, never a bare buf: its targets pin the version CI's gate and the companion's generator both use. Generation runs buf inside the published companion, not from the host's PATH.

GOFLAGS ?= -timeout=300s is exported by the Makefile so no test run hangs. Tests with the proto_companion_required tag are not in CI — they need an image core alone cannot build. Run them locally before touching that surface.

Never mock. Tests use real infrastructure and real YAML fixtures under testdata/. If a boundary is hard to reach, reach it anyway.

Where things live

resources/ is the source of truth for every type; read it there first.

Path Owns
resources/ Workspace/Module/Service/Endpoint/Agent, YAML loading, validation
proto/ schema source of truth (codefly/*, mind/*); generated/ is output
callertoken/ shared gRPC caller-token authentication for agent and service listeners
agents/ agent registration, process manager, the embeddable server types
network/ deterministic port allocation, DNS, native/container/public modes
configurations/ what services provide and consume, injected as env vars
runners/, companions/ process execution; sidecar images for language tooling
imagepolicy/ target-declared Dockerfile requirements (docs)
solutionhost/ ModuleManifest, the one signed record of a module instance on a host: its workloads, endpoints and requested authority (docs)
contracts/module/ the ONE implementation of codefly/module-contract/v1 (Go package module, imported as modulecontract) — the request a module publishes, strict decoding, every refusal as a named rule (rules.go), slot resolution, a serialisable model, shipped fixtures every reader is driven through and a self-check that deletes each rule and proves a fixture notices (docs). The cell (codefly/cell/v1) has no implementation in core
code/semantic the only package allowed to use cgo (tree-sitter)
internal/ciguard invariants about CI config that no other test would notice

The deeper picture is in docs/architecture.md — resource hierarchy, agent lifecycle, network mapping, configuration flow, readiness, dependency graph. Read it rather than re-deriving from the tree.

Rules that bite

  • Core owns runtime configuration and secrets; CLI owns deployment. Infra-base and other producers emit the CLI's codefly/coordinate/v1 declarations. Cluster, registry, ingress, managed services, identity attachments and GitOps policy stay in CLI. Never translate private producer inventories in Core/CLI. See docs/configuration-contract.md.
  • Core and the CLI have no concrete-agent compatibility knowledge. Check the running agent's advertised wire/startup protocol and operation capabilities at runtime, before work. Never gate on agent names, release pins, linked Core versions, or a compiled compatibility roster. Unchanged protocols require no fleet rebuild or repinning. Missing declarations are errors, not permission to fall back. Agent-specific selection, settings and cleanup belong to the agent; Core transports declarations and implements generic contracts. See docs/agent-contract.md. Shared language/framework tooling may remain when explicitly invoked by its consumers; it must not choose agents or impose agent runtime policy.
  • Product replacements never rewrite owner releases. Resolve exact instances from signed metadata; no implicit dependency-source acquisition. Local inputs are development-only. Deployment admits owner output or authorized unchanged- source builds with input-bound qualification; see docs/composition-selections.md.
  • Ports come from network.ToNamedPort() or RuntimeManager. Never hardcode one, always track allocations — a missing dedup once assigned the same port twice.
  • Identity comes from a verified Work Context, never assembled beside one. workcontext mints and verifies the signed capability; Principal is derived from a verified one, and its delegation chain with it. An approval is a grant hop — the one audited exception to attenuation, bound to one tool, subject and call, single-use, and one hop however large the quorum. A capability's seal and every hop's epoch are REQUIRED by the schema. They were optional once, with archived receipts as the reason; two reviews called that a compatibility hedge the rules forbid, and archived receipts are historical data that get a snapshot type if they need one. Core owns the approval_required signal; the approvals engine is product-level. It has exactly one implementation and one strength — this one. Nothing else signs, verifies or re-encodes a capability; the entrypoints share one check path: callees Verify/Authenticate and refuse what they cannot answer, a hop Inspects without consuming and forwards what only live state refuses, the issuer ReadBacks its own capability. A weaker check is the owner's question, never a local accommodation. A consumer DEMONSTRATES BEHAVIOUR with workcontext/conformance.Run, RunAuthenticator or RunInspector — the kit proves behaviour and not identity, and the identity proof is the consumer's own import gate; a foreign encoding is refused before the signature with ErrNotACoreToken, never as a bad signature. A second implementation already cost a day of key-rotation debugging. See docs/work-context.md and workcontext/README.md.
  • An endpoint declares three axes, each read on its own. visibility is reach, location is where it lives, exposure is whether an outward address exists; allow_modules is derived (Workspace.DeriveAllowModules), refused when authored. See docs/network-model.md.
  • Readiness is a gRPC health check, never a TCP connect. An open port does not mean a ready service. Endpoints declare health.readiness; consumers go through resources.PlanReadiness and the readiness package. See docs/readiness.md.
  • Regenerate protos with codefly generate proto --proto ./proto --output ./generated --local. --local selects generated/buf.gen.local.yaml; buf, plugins and goimports run inside the pinned proto companion. Do not hand-run buf/protoc. Commit the .proto change and the regenerated bindings together; see proto/README.md.
  • Keep the CGO-free surface CGO-free. cgo lives only in code/semantic; consumers take the build-tag-aware constructor from code/codeserver.New (-tags codefly_nosemantic). See docs/cgo.md.
  • Companions are ours. A broken companion gets fixed, not routed around.
  • A CI credential is a job of its own, gated to a merged ref that checks out nothing under review. See docs/ci-credentials.md.

Procedures

Step-by-step procedures live in .claude/skills/, loaded on demand rather than carried here:

  • ci-guard-triage — one of the bespoke CI guards is red, and you need to tell a real finding from an escape hatch that would only make it green.
  • release-core — cutting a version, and why the tag is never made by hand.

Workflow

  • Branch and PR; never commit to main. Conventional Commits for the title.
  • Agent protocol changes update agents/contract/contract.json; releases report its compatibility separately from Core's version. See docs/agent-contract.md.
  • internal/ciguard holds the meta-invariants, including this file's length budget and each skill's frontmatter contract. It runs under go test ./..., so CI enforces it with no workflow change.
  • Keep this file under ~150 lines (hard cap 200). Push depth into a nested AGENTS.md beside what it describes, into .claude/skills/, or into docs/.
  • CLAUDE.md points here, and the PR that changes a process updates this file.