feat(spec)!: drop Persistence/KitDir, add locked/resources, list mounts#37
Open
mdelapenya wants to merge 6 commits into
Open
feat(spec)!: drop Persistence/KitDir, add locked/resources, list mounts#37mdelapenya wants to merge 6 commits into
mdelapenya wants to merge 6 commits into
Conversation
The field was stored, validated, inherited, displayed, and tested, but no engine path actually consumed it; volumes are already declared explicitly via Manifest.Volumes. Drop: - Persistence constants and Manifest.Persistence - agentBlock.Persistence (and the flat-field rejection) - ValidateManifest persistence rule - TCK suite persistence sub-test Kits in this repo that declared `persistence: persistent` no longer carry the field, since it was no-op all along. Kits are documented as experimental, so no schema bump. Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
Same shape as the Persistence removal: KitDir was declared on the manifest, parsed from yaml, copied during inheritance, and rendered by `sbx kit inspect`, but no runtime path consumed it to make a decision. No kit in this repo nor in the engine's embedded agents declares it. Drop the field outright. The engine has its own dead-code references (inherit copy + CLI display) to clean up downstream. Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
Kits can now declare a list of dotted YAML paths that child
kits must not override during single-parent inheritance:
locked:
- agent.image
- network.allowedDomains
The spec library only validates well-formedness — empty entries,
malformed paths (leading/trailing/double dots, disallowed chars),
and duplicates are rejected. Whether a given path is meaningful
against the canonical Artifact shape is the consumer's call;
the engine's inherit step is the natural enforcement site.
TCK suite gains a `locked` sub-test that mirrors ValidateLocked,
so kits exercising the field get a visible per-kit pass/fail
alongside the other manifest sub-tests.
This is the surface; the merge-time check follows downstream.
Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
Optional container resource limits declared under the agent block:
agent:
image: ...
resources:
cpu: 4
memoryMB: 8192
gpu: "1"
All three fields are optional; unset means "no constraint from
the spec". CPU is float64 (fractional cores allowed); MemoryMB
is int64 mebibytes; GPU is a string the consumer interprets
(e.g. "1", "all", a vendor selector).
Resources normalizes onto the Manifest alongside Template and
AIFilename. Validation rejects negative CPU/memory; the TCK
suite gains a `resources` sub-test so kits exercising the field
get a visible per-kit pass/fail alongside the other manifest
sub-tests.
Agent TTL is intentionally deferred — there are unresolved
comments in the proposal doc that need to land first.
Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
Replace Manifest.Volumes and Manifest.Tmpfs (both map[string]string
where the value held packed options like "size=10g,mode=1777") with
[]MountSpec, borrowed from the dash kit format. Discrete fields are
easier to validate, easier to extend, and read better in yaml:
volumes:
- path: /home/agent/.cache
- path: /tmp/scratch
size: 100m
mode: 1777
tmpfs:
- path: /tmp/agent-scratch
size: 512m
mode: 1777
Volumes and tmpfs share the MountSpec type (Path, Size, Mode) because
their per-entry shape is identical; the semantic distinction lives on
the Manifest field that holds the slice. ValidateVolumes and
ValidateTmpfs collapse into thin wrappers around a single
validateMounts helper that takes the field name as the error prefix,
so failures still read "volumes[i]" or "tmpfs[i]".
TCK pure-logic sub-tests iterate the slice form; tck/derive.go's
deriveTmpfs composes the comma-separated option string Docker
expects from Size/Mode, preserving the existing runtime assertTmpfs
check unchanged.
No contrib kit declared `volumes:` or `tmpfs:` in yaml, so kit
specs are unchanged. The engine consumers (volumeCustomizer and
tmpfsCustomizer) need to be adapted downstream.
Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
ValidateCommandsPolicy already rejects empty/relative initFile paths and unsupported content placeholders; extend it to also reject malformed Mode strings using the same octalModePattern already defined for MountSpec. The engine's kit/validate.go has been carrying this check as a local extension. Pushing it upstream means the spec library is the single source of truth and the engine can drop its wrapper. Signed-off-by: Manuel de la Peña <manuel.delapena@docker.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What's changed
Five field-scoped, breaking-but-experimental changes to the kit spec, each a separate commit:
Manifest.Persistence— declared on the manifest, parsed, inherited, displayed, but no runtime decision consumed it. Volumes are already explicit viaManifest.Volumes.Manifest.KitDir— same shape as Persistence: stored, copied during inheritance, rendered by inspect commands, but never consulted at runtime. No kit declares it.locked— a list of dotted YAML paths that child kits must not override duringextends:resolution. The spec library only validates well-formedness; enforcement is the merge consumer's job.agent.resources— optional container resource constraints (cpuas float64 cores,memoryMBas int64,gpuas a consumer-defined string).MountSpec— bothManifest.VolumesandManifest.Tmpfsmove frommap[string]string(with packed option strings) to[]MountSpec{Path, Size, Mode}. The two fields share one entry type since their per-entry shape is identical; the semantic distinction lives on the slice field.TCK gains pure-logic sub-tests for
lockedandresources, and iterates the slice form forvolumesandtmpfs. Agentttlis intentionally deferred — there are unresolved comments on the proposal that need to land first.Why is this important?
The kit spec accumulated fields that looked configurable but had no effect —
PersistenceandKitDirwere declared and validated but never read. Removing them clears the kit-author surface and avoids the fiction that they do something.The two additive fields come from the next phase of kit-author requests:
lockedgives parent kits a way to protect critical fields from being overridden, andresourcesdeclares container limits that the engine will wire to the runtime (matters most for cloud workloads, where CPU/memory/GPU constraints aren't always inherited from the host).The mount restructure replaces a flat map whose values had to pack options into a single string (
"size=512m,mode=1777") — awkward to validate, awkward to extend — with a discrete list-of-struct form. Volumes and tmpfs shareMountSpecbecause their fields are identical; the validators delegate to one sharedvalidateMountshelper that supplies the field name as the error prefix, so failures still readvolumes[i]ortmpfs[i].Kits are documented as experimental, so the removals and shape change don't require a schema bump.
Changes
spec/PersistenceEphemeral/PersistencePersistentconstants,Manifest.Persistence,agentBlock.Persistence, the flat-field rejection innormalize, and the persistence rule inValidateManifest.Manifest.KitDir.Artifact.Locked []stringandspecFile.Lockedparsing; addValidateLocked(rejects empty entries, malformed dotted paths, duplicates).Resourcestype,Manifest.Resources,agentBlock.Resources, normalize wiring, and non-negative CPU/memory validation inValidateManifest.MountSpec{Path, Size, Mode}used by bothManifest.VolumesandManifest.Tmpfs. CollapseValidateVolumes/ValidateTmpfsinto thin wrappers around a sharedvalidateMounts(field, mounts)helper.tck/persistencesub-test fromRunValidationTests.lockedsub-test that mirrorsspec.ValidateLocked.resourcessub-test assertingCPUandMemoryMBare non-negative.deriveTmpfstakes[]spec.MountSpecand composes the comma-separated option string Docker expects fromSize/Mode, preserving the existing runtimeassertTmpfscheck unchanged.Pure-logic only — runtime container assertions for
resources(applying limits via testcontainers and inspectingHostConfig) were considered but kept out of scope. Resource enforcement is the engine's responsibility, and the TCK should not pretend to be the engine.Kits
Eight kit specs drop their no-op
persistence: persistentlines:amp,claude-ollama,nanobot,nanoclaw,openclaw,opencode-model-runner,pi,trivy. No contrib kit declaredvolumes:ortmpfs:in yaml, so kit specs are otherwise unchanged.Migration
Downstream consumers of this library will need to:
Manifest.PersistenceandManifest.KitDir.map[string]stringto[]spec.MountSpec. The packed option string ("size=10g") is now discrete fields; the consumer composes whatever runtime form Docker expects.agent.resourcesto the container runtime when ready to enforce limits.lockedinto the inheritance merge step to actually reject child overrides.Test plan
go test ./...passes across all 15 packages in this repo (spec, tck, and every kit's integration tests).