You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Status: Specification Estimated Effort: 4-5 days Dependencies: Work Unit 002 (CUE Schema), Work Unit 003 (libs/refs OCI Backend), Work Unit 004 (Packaging/Publishing), Work Unit 005 (Inspection), Work Unit 006 (Installation)
Behavioral Goal
As a sow user, I need CLI commands to publish, inspect, add, update, remove, and manage OCI-based refs, So that I can distribute knowledge refs via OCI registries, selectively download only the content I need, manage my local ref cache, and seamlessly integrate OCI refs into my existing sow workflow alongside git and file refs.
Success Criteria
sow refs publish <dir> <registry>:<tag> packages and pushes a directory as an OCI ref with validation
sow refs inspect <url> displays file tree, metadata, and validation status using < 10KB bandwidth
sow refs add <url> installs OCI refs (auto-detected or explicit oci:// prefix) to cache and creates workspace symlink
sow refs add <url> --path <glob> supports multiple --path flags for selective extraction with OR logic
sow refs update and sow refs remove work correctly for OCI refs
sow refs list displays OCI-specific information (digest, selective status)
sow refs prune and sow refs cache-info provide cache management functionality
Index schema (index.json) extended with OCI-specific fields: digest, selective, globs, installed_at, source_type
URL detection correctly identifies OCI refs (explicit oci:// prefix and known registry patterns)
Integration tests cover full publish-inspect-add-update-remove lifecycle
Existing Code Context
Explanatory Context
The sow CLI follows a consistent command pattern using Cobra. Each command is defined in cli/cmd/refs/ with a newXxxCmd() factory function that sets up flags and a runRefsXxx() function that executes the logic. Commands use functional options via refs.WithRefXxx() functions and print confirmation messages with emoji indicators (✓ success, ⚠ warning, ✗ error).
The existing Manager in cli/internal/refs/index_manager.go provides high-level orchestration for ref operations. It handles URL normalization, type inference, index management (both committed and local), and delegates caching to CacheManager. For OCI refs, we'll extend this flow: the Manager.Add() method will detect OCI URLs via the enhanced InferTypeFromURL() function, then delegate to the new OCIType implementation.
The RefType interface in cli/internal/refs/types.go (lines 23-79) defines how ref types integrate with the system. GitType and FileType demonstrate the pattern: each type implements Cache(), Update(), IsStale(), CachePath(), and Cleanup(). The new OCIType will follow this pattern, wrapping the libs/refs.Installer and libs/refs.Client interfaces from Work Units 003-006.
The URL parsing system in cli/internal/refs/url.go uses scheme-based detection (git+https://, file://). OCI URLs need different detection since OCI registries don't have a distinctive URL scheme. We'll support both explicit oci:// prefix and auto-detection of known registry patterns (ghcr.io, docker.io, etc.).
The index schema in libs/schemas/refs_committed.cue defines the Ref structure stored in index.json. For OCI refs, we need additional fields: digest for integrity verification, selective and globs for tracking partial installations, installed_at for timestamps, and source_type to distinguish OCI from legacy types.
Work Units 003-006 provide the core OCI functionality in libs/refs/:
The --path flag is repeatable, uses OR logic for matching.
Management (lines 530-545):
sow refs list # Table with ID, SOURCE, VERSION, LINK, STATUS
sow refs update <id># Update to latest version (compares digests)
sow refs remove <id> [--prune-cache]
sow refs prune # Remove unused cache entries
sow refs prune --all # Remove entire OCI cache
sow refs cache-info # Show cache size and statistics
Discovery Analysis (Integration Points)
Section 8 of discovery analysis (.sow/project/discovery/analysis.md) identifies required URL type inference changes:
Current (lines 345-349): cli/internal/refs/url.go uses scheme-based detection (git+https://...).
Required (lines 350-358): Detect OCI registry URLs which don't have a distinctive scheme:
ghcr.io/org/repo:tag
docker.io/library/image:latest
registry.example.com/path:v1
Recommendation (lines 354-358): Use oci:// scheme prefix for explicit OCI refs, plus auto-detect known registries. The libs/refs.IsOCIRef() function from Work Unit 003 provides this detection.
Section 9 (lines 396-428) documents CLI patterns to follow:
Types implement interfaces registered via init() functions
Use functional options for configuration
Wrap external library errors with fmt.Errorf
Use context.Context for cancellation/timeouts
Use cmd.Printf() with emoji indicators
ADR-003 (URL Format Decision)
ADR-003 (.sow/knowledge/adrs/003-oci-refs-distribution.md) documents URL format decisions:
OCI refs identified by registry URL pattern, not scheme prefix
Support explicit oci:// prefix for disambiguation
Auto-detect known registries: ghcr.io, docker.io, quay.io, etc.
Support digest pinning: @sha256:...
Support version tags: :v1.0.0, :latest
Detailed Requirements
New Command: sow refs publish
Create cli/cmd/refs/publish.go:
funcnewPublishCmd() *cobra.Command {
var (
dryRunboolalsoTagLatestbool
)
cmd:=&cobra.Command{
Use: "publish <directory> <registry>:<tag>",
Short: "Publish a directory as an OCI ref",
Long: `Package and publish a directory as an OCI ref to a registry.The directory must contain a valid .sow-ref.yaml manifest file.The ref will be packaged as an estargz OCI image and pushed.Examples: # Publish to GitHub Container Registry sow refs publish ./team-docs ghcr.io/myorg/go-standards:v1.0.0 # Validate without pushing sow refs publish ./team-docs ghcr.io/myorg/go-standards:v1.0.0 --dry-run # Also tag as latest sow refs publish ./team-docs ghcr.io/myorg/go-standards:v1.0.0 --also-tag-latest`,
Args: cobra.ExactArgs(2),
RunE: func(cmd*cobra.Command, args []string) error {
returnrunRefsPublish(cmd, args[0], args[1], dryRun, alsoTagLatest)
},
}
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Validate without pushing")
cmd.Flags().BoolVar(&alsoTagLatest, "also-tag-latest", false, "Also push :latest tag")
returncmd
}
Implementation flow:
Validate .sow-ref.yaml exists in directory
Create libs/refs.Packager instance
If --dry-run, call packager.Validate() and display results
Otherwise, call packager.Package() then client.Push()
If --also-tag-latest, push again with :latest tag
Print confirmation with digest
New Command: sow refs inspect
Create cli/cmd/refs/inspect.go:
funcnewInspectCmd() *cobra.Command {
cmd:=&cobra.Command{
Use: "inspect <url>",
Short: "Inspect an OCI ref without downloading",
Long: `Display information about an OCI ref without downloading it.Shows file tree, total size, metadata from .sow-ref.yaml, andvalidation status. Uses minimal bandwidth (< 10KB).Examples: sow refs inspect ghcr.io/myorg/go-standards:v1.0.0 sow refs inspect oci://docker.io/library/my-ref:latest`,
Args: cobra.ExactArgs(1),
RunE: func(cmd*cobra.Command, args []string) error {
returnrunRefsInspect(cmd, args[0])
},
}
returncmd
}
Output format:
Ref: ghcr.io/myorg/go-standards:v1.0.0
Digest: sha256:abc123...
Metadata:
Title: Go Team Standards
Description: Team Go coding conventions and best practices.
Classifications: guidelines
Tags: golang, conventions, testing
License: MIT
Authors: Platform Team
Contents (15 files, 2.3 MB):
docs/
README.md (4.2 KB)
coding-standards.md (12.1 KB)
...
examples/
demo.go (1.5 KB)
...
.sow-ref.yaml (512 B)
Validation: ✓ Valid manifest
Extended Command: sow refs add (OCI support)
Extend cli/cmd/refs/add.go:
New flags:
--path <glob> (repeatable): Glob patterns for selective extraction
--force: Re-download even if cached
Changes to runRefsAdd():
Detect if URL is OCI via refs.IsOCIRef(url) or InferTypeFromURL()
For OCI refs with --path flags, use Installer.InstallSelective()
For OCI refs without --path, use Installer.Install()
Parse manifest from result for index entry metadata
Create index entry with OCI-specific fields
Flag validation:
--path only valid for OCI URLs (error for git/file)
--branch only valid for git URLs (error for OCI/file)
Extended Command: sow refs list (OCI output)
Extend cli/cmd/refs/list.go to show OCI-specific columns:
Table format changes:
ID TYPE SEMANTIC LINK SOURCE DIGEST URL
────────────────────────────────────────────────────────────────────────────────────────
go-standards oci knowledge go-standards committed abc1234... ghcr.io/myorg/go-standards:v1.0.0
└─ selective: docs/**/*.md, examples/*.go
api-patterns git knowledge api-patterns committed - github.com/myorg/api-patterns
New columns:
DIGEST: Short digest for OCI refs (7 chars), - for others
For selective installs, show glob patterns on sub-line
New Command: sow refs prune
Create cli/cmd/refs/prune.go:
funcnewPruneCmd() *cobra.Command {
var (
dryRunboolallbool
)
cmd:=&cobra.Command{
Use: "prune",
Short: "Remove unused OCI refs from cache",
Long: `Clean up the local OCI refs cache.By default, removes cached refs that are not referenced by anyworkspace index. Use --all to remove the entire OCI cache.Examples: sow refs prune # Remove unused entries sow refs prune --dry-run # Show what would be deleted sow refs prune --all # Remove entire OCI cache`,
RunE: func(cmd*cobra.Command, args []string) error {
returnrunRefsPrune(cmd, dryRun, all)
},
}
cmd.Flags().BoolVar(&dryRun, "dry-run", false, "Show what would be deleted")
cmd.Flags().BoolVar(&all, "all", false, "Remove all cached OCI refs")
returncmd
}
Implementation:
Get Installer.GetCacheInfo() to list cached refs
If --all, remove entire ~/.cache/sow/refs/oci/ directory
Otherwise, cross-reference with index entries to find unused
If --dry-run, print what would be deleted
Delete unused cache entries
Print summary (removed X refs, freed Y MB)
New Command: sow refs cache-info
Create cli/cmd/refs/cache_info.go:
funcnewCacheInfoCmd() *cobra.Command {
cmd:=&cobra.Command{
Use: "cache-info",
Short: "Display OCI refs cache statistics",
Long: `Show statistics about the local OCI refs cache.Displays cache location, total size, number of refs, anddetails about each cached ref.`,
RunE: func(cmd*cobra.Command, args []string) error {
returnrunRefsCacheInfo(cmd)
},
}
returncmd
}
Work Unit 007: CLI Integration and Commands
Status: Specification
Estimated Effort: 4-5 days
Dependencies: Work Unit 002 (CUE Schema), Work Unit 003 (libs/refs OCI Backend), Work Unit 004 (Packaging/Publishing), Work Unit 005 (Inspection), Work Unit 006 (Installation)
Behavioral Goal
As a sow user,
I need CLI commands to publish, inspect, add, update, remove, and manage OCI-based refs,
So that I can distribute knowledge refs via OCI registries, selectively download only the content I need, manage my local ref cache, and seamlessly integrate OCI refs into my existing sow workflow alongside git and file refs.
Success Criteria
sow refs publish <dir> <registry>:<tag>packages and pushes a directory as an OCI ref with validationsow refs inspect <url>displays file tree, metadata, and validation status using < 10KB bandwidthsow refs add <url>installs OCI refs (auto-detected or explicitoci://prefix) to cache and creates workspace symlinksow refs add <url> --path <glob>supports multiple--pathflags for selective extraction with OR logicsow refs updateandsow refs removework correctly for OCI refssow refs listdisplays OCI-specific information (digest, selective status)sow refs pruneandsow refs cache-infoprovide cache management functionalityindex.json) extended with OCI-specific fields:digest,selective,globs,installed_at,source_typeoci://prefix and known registry patterns)Existing Code Context
Explanatory Context
The sow CLI follows a consistent command pattern using Cobra. Each command is defined in
cli/cmd/refs/with anewXxxCmd()factory function that sets up flags and arunRefsXxx()function that executes the logic. Commands use functional options viarefs.WithRefXxx()functions and print confirmation messages with emoji indicators (✓success,⚠warning,✗error).The existing
Managerincli/internal/refs/index_manager.goprovides high-level orchestration for ref operations. It handles URL normalization, type inference, index management (both committed and local), and delegates caching toCacheManager. For OCI refs, we'll extend this flow: theManager.Add()method will detect OCI URLs via the enhancedInferTypeFromURL()function, then delegate to the newOCITypeimplementation.The
RefTypeinterface incli/internal/refs/types.go(lines 23-79) defines how ref types integrate with the system.GitTypeandFileTypedemonstrate the pattern: each type implementsCache(),Update(),IsStale(),CachePath(), andCleanup(). The newOCITypewill follow this pattern, wrapping thelibs/refs.Installerandlibs/refs.Clientinterfaces from Work Units 003-006.The URL parsing system in
cli/internal/refs/url.gouses scheme-based detection (git+https://,file://). OCI URLs need different detection since OCI registries don't have a distinctive URL scheme. We'll support both explicitoci://prefix and auto-detection of known registry patterns (ghcr.io, docker.io, etc.).The index schema in
libs/schemas/refs_committed.cuedefines theRefstructure stored inindex.json. For OCI refs, we need additional fields:digestfor integrity verification,selectiveandglobsfor tracking partial installations,installed_atfor timestamps, andsource_typeto distinguish OCI from legacy types.Work Units 003-006 provide the core OCI functionality in
libs/refs/:Pull(),Push(),ListFiles(),GetManifest(),GetDigest()Package()for creating estargz images with validationInspect()for lightweight ref inspectionInstall(),InstallSelective(),IsCached(),GetCacheInfo()This work unit wires all these components together in the CLI layer.
Key Files
cli/cmd/refs/refs.gocli/cmd/refs/add.gocli/cmd/refs/list.gocli/cmd/refs/update.gocli/cmd/refs/remove.gocli/internal/refs/types.goRefTypeinterface to implementcli/internal/refs/registry.gocli/internal/refs/manager.goCacheManagerfor symlink creationcli/internal/refs/index_manager.goManagerfor high-level orchestrationcli/internal/refs/url.gocli/internal/refs/git.goRefTypeimplementationlibs/schemas/refs_committed.cuelibs/schemas/cue_types_gen.goExisting Documentation Context
Design Document (Command Specifications)
The OCI Refs Design Document (
.sow/knowledge/designs/oci-refs/oci-refs-design.md, lines 476-545) provides detailed CLI command specifications:Publishing (lines 478-489):
The
--dry-runflag was added in the task description for validation without pushing.Inspection (lines 492-504):
Must use < 10KB bandwidth (TOC + manifest only).
Installation (lines 507-527):
The
--pathflag is repeatable, uses OR logic for matching.Management (lines 530-545):
Discovery Analysis (Integration Points)
Section 8 of discovery analysis (
.sow/project/discovery/analysis.md) identifies required URL type inference changes:Current (lines 345-349):
cli/internal/refs/url.gouses scheme-based detection (git+https://...).Required (lines 350-358): Detect OCI registry URLs which don't have a distinctive scheme:
ghcr.io/org/repo:tagdocker.io/library/image:latestregistry.example.com/path:v1Recommendation (lines 354-358): Use
oci://scheme prefix for explicit OCI refs, plus auto-detect known registries. Thelibs/refs.IsOCIRef()function from Work Unit 003 provides this detection.Section 9 (lines 396-428) documents CLI patterns to follow:
init()functionsfmt.Errorfcontext.Contextfor cancellation/timeoutscmd.Printf()with emoji indicatorsADR-003 (URL Format Decision)
ADR-003 (
.sow/knowledge/adrs/003-oci-refs-distribution.md) documents URL format decisions:oci://prefix for disambiguation@sha256:...:v1.0.0,:latestDetailed Requirements
New Command:
sow refs publishCreate
cli/cmd/refs/publish.go:Implementation flow:
.sow-ref.yamlexists in directorylibs/refs.Packagerinstance--dry-run, callpackager.Validate()and display resultspackager.Package()thenclient.Push()--also-tag-latest, push again with:latesttagNew Command:
sow refs inspectCreate
cli/cmd/refs/inspect.go:Output format:
Extended Command:
sow refs add(OCI support)Extend
cli/cmd/refs/add.go:New flags:
--path <glob>(repeatable): Glob patterns for selective extraction--force: Re-download even if cachedChanges to
runRefsAdd():refs.IsOCIRef(url)orInferTypeFromURL()--pathflags, useInstaller.InstallSelective()--path, useInstaller.Install()Flag validation:
--pathonly valid for OCI URLs (error for git/file)--branchonly valid for git URLs (error for OCI/file)Extended Command:
sow refs list(OCI output)Extend
cli/cmd/refs/list.goto show OCI-specific columns:Table format changes:
New columns:
DIGEST: Short digest for OCI refs (7 chars),-for othersNew Command:
sow refs pruneCreate
cli/cmd/refs/prune.go:Implementation:
Installer.GetCacheInfo()to list cached refs--all, remove entire~/.cache/sow/refs/oci/directory--dry-run, print what would be deletedNew Command:
sow refs cache-infoCreate
cli/cmd/refs/cache_info.go:Output format:
New RefType:
OCITypeCreate
cli/internal/refs/oci.go:URL Detection Extension
Extend
cli/internal/refs/url.go:Index Schema Extension
Extend
libs/schemas/refs_committed.cue:Command Registration
Update
cli/cmd/refs/refs.go:Testing Requirements
Unit Tests
URL Detection Tests (
url_test.goextension):ghcr.io/org/repo:tag→ "oci" typeoci://ghcr.io/org/repo:tag→ "oci" typedocker.io/library/nginx:latest→ "oci" typegit+https://github.com/org/repo→ "git" type (unchanged)file:///path→ "file" type (unchanged)OCIType Tests (
oci_test.go):Name()returns "oci"IsEnabled()returns trueValidateConfig()rejects--branchflagCache()delegates toInstaller.Install()Cache()with globs delegates toInstaller.InstallSelective()IsStale()compares digests correctlyCommand Flag Tests:
publish --dry-runvalidates without pushingadd --pathonly accepted for OCI URLsadd --branchrejected for OCI URLsprune --allremoves entire cacheIntegration Tests
Publish Flow (
publish_integration_test.go):.sow-ref.yamlsucceeds--dry-runvalidates without pushing--also-tag-latestpushes twiceInspect Flow (
inspect_integration_test.go):Add Flow (
add_integration_test.go):--pathextracts only matching files--pathuses OR logic.sow-ref.yamlalways extractedUpdate Flow (
update_integration_test.go):List Flow (
list_integration_test.go):Prune Flow (
prune_integration_test.go):--allclears entire OCI cache--dry-runshows but doesn't deleteEnd-to-End Tests
lifecycle_e2e_test.go):.sow-ref.yamlpublishto test registryinspectthe published refaddthe ref to workspaceupdate(should be no-op, same digest)removethe refprunethe cacheImplementation Notes
Dependency Chain
This work unit depends on all previous OCI work units:
.sow-ref.yamlClientinterface for OCI operations, URL detectionPackagerforsow refs publishInspectorforsow refs inspectInstallerforsow refs addImplementation can be parallelized:
OCITypecan be implemented once Work Unit 003 is completeBackward Compatibility
All changes are additive:
gitandfileref workflowsomitemptyin Go)Error Handling Pattern
Follow existing pattern from
cli/internal/refs/index_manager.go:OCI-specific errors from
libs/refsshould be wrapped with CLI context:Output Formatting
Use emoji indicators consistently:
✓- Success operations⚠- Warnings, skipped items✗- Errors→- Progress indicators (for interactive operations)Use
cmd.Printf()for all output to respect output streams.Implementation Standards
All code produced in this work unit MUST adhere to the following standards:
Code Quality Standards
.standards/STYLE.md.standards/TESTING.mdgolangci-lint runwith zero errors before completionRequired Dependencies
libs/refsmodule which usesgithub.com/jmgilman/go/ociinternallylibs/refsinterfaces, not directly to OCI librarylibs/refs.Client,libs/refs.Packager,libs/refs.Inspector,libs/refs.Installergithub.com/jmgilman/go/fs/coreandgithub.com/jmgilman/go/fs/billyfor file system operations requiring abstractionbilly.NewLocalFS()billy.NewMemoryFS()for isolationVerification Checklist
Before marking this work unit complete, verify:
golangci-lint run ./cli/...passes with zero errorsOut of Scope
docker logindirectlyAcceptance Criteria
sow refs publishcommand creates and pushes estargz OCI imagessow refs publish --dry-runvalidates without pushingsow refs publish --also-tag-latestpushes both versioned and latest tagssow refs inspectdisplays file tree, metadata, and size using < 10KB bandwidthsow refs adddetects OCI URLs automatically (known registries)sow refs add oci://...works with explicit prefixsow refs add --path <glob>enables selective extraction (repeatable flag)--pathflags use OR logicsow refs add --pathrejected for non-OCI refs with clear errorsow refs updatecompares digests for OCI refssow refs removeworks correctly for OCI refssow refs listshows DIGEST column and selective status for OCI refssow refs pruneremoves unreferenced cache entriessow refs prune --allclears entire OCI cachesow refs cache-infodisplays cache statisticsdigest,selective,globs,installed_at,source_typeOCITypeimplementsRefTypeinterface correctly✓,⚠,✗)