Skip to main content
← Back to list
01Issue
FeatureShippedSwamp CLI
Assigneesstack72

Relationships

↔ sibling #479

#464 feat: giga-swamp phase 1 — Namespace value object + config

Opened by stack72 · 5/28/2026· Shipped 5/28/2026

Problem

Giga-swamp (multi-repo shared datastores) requires a Namespace value object as the foundational building block. Nothing in the system currently understands namespaces — this phase introduces the type and configuration plumbing so subsequent phases can consume it.

Proposed Solution

1. Create src/domain/data/namespace.ts

  • Namespace value object with validation: [a-z0-9][a-z0-9-]*, 1-64 characters
  • Namespace.SOLO static for the empty-string sentinel (solo mode / no namespace)
  • Namespace.equals(), Namespace.toString(), Namespace.isEmpty()
  • parseNamespacedModelName(input) — splits security:scanner into { namespace: security, modelName: scanner }, splits *:scanner into { namespace: *, modelName: scanner } (wildcard), returns { namespace: undefined, modelName: input } when no colon present

2. Create src/domain/data/namespace_test.ts

  • Validation: valid slugs, rejection of invalid characters, length limits
  • Equality: two namespaces with the same slug are equal
  • SOLO sentinel: isEmpty() returns true, toString() returns empty string
  • parseNamespacedModelName: colon parsing, wildcard, edge cases (no colon, empty prefix, multiple colons)

3. Add namespace to DatastoreConfigData

In src/domain/datastore/datastore_config.ts, add optional namespace field to FilesystemDatastoreConfig, CustomDatastoreConfig, and DatastoreConfigData.

4. Update repo_marker_repository.ts

Read/write namespace field from .swamp.yaml datastore config.

5. Update resolve_datastore.ts

Resolve namespace from config (env var, CLI flag, yaml).

Ship Gate

  • deno check passes
  • deno lint passes
  • deno fmt passes
  • deno run test passes
  • No behavioral change — solo mode is identical to before
  • The Namespace type exists and can be constructed/validated but nothing consumes it yet

Design Context

Full design doc: resources/giga-swamp.md

This is Phase 1 of 7. The Namespace value object should follow the same DDD patterns as DataId, ModelType, and other existing value objects in src/domain/data/. See the design doc Domain Layer Changes and Implementation Order Phase 1 sections.

Alternatives Considered

None — this is foundational plumbing required by all subsequent phases.

02Bog Flow
OPENTRIAGEDIN PROGRESSSHIPPED+ 1 MOREASSIGNED+ 5 MOREREVIEW+ 3 MOREPR_MERGED+ 1 MORENOTIFICATION_SKIPPED

Shipped

5/28/2026, 3:32:36 PM

Click a lifecycle step above to view its details.

03Sludge Pulse
stack72 assigned stack725/28/2026, 1:09:58 PM
stack72 linked related to #4795/28/2026, 9:45:01 PM
Editable. Press Enter to edit.

stack72 commented 5/28/2026, 1:10:10 PM

Architectural Context for Phase 1

Design Philosophy

This phase establishes the Namespace as a domain concept, not an implementation detail. The namespace is a bounded context identifier — it answers "which swamp repo produced this?" for every artifact in a shared datastore. Treat it with the same care as other identity types in src/domain/data/.

The full design doc is at resources/giga-swamp.md. Read it before starting — especially the "Namespace as a Domain Concept" section and the resolved design decisions. The decisions around query scoping (ns:model, *:model, ambiguity errors) directly inform how parseNamespacedModelName should behave.

What Solo Mode Means

Solo mode (namespace = "") is the backward-compatibility contract. After this phase lands, every existing repo in the world must behave identically. No new output, no new paths, no new behavior. Empty namespace is a no-op everywhere. This is the invariant that protects us through all 7 phases — if it breaks here, every subsequent phase inherits the regression.

Boundary of This Phase

The namespace type exists and config can carry it. Nothing consumes it yet. The temptation will be to start threading it through the catalog, the path resolver, or the repository — don't. Those are phases 2 and 3. Phase 1 is deliberately inert. The value is in getting the domain concept right and the config plumbing tested before anything depends on it.

How We Verify

  1. All existing tests pass unchanged. No test should need modification — if one does, something is wrong.
  2. New tests cover the value object exhaustively. Validation boundaries, the SOLO sentinel, equality semantics, and the parseNamespacedModelName parser. The parser has subtle edge cases — think about what happens with multiple colons, empty segments, and the wildcard. The colon is the namespace separator in CEL expressions, so getting this parser right matters for phase 4.
  3. Config round-trip. Write namespace: infra to .swamp.yaml, read it back, confirm it survives. Write no namespace, read it back, confirm it's absent (not namespace: "").
  4. deno check, deno lint, deno fmt, deno run test — the standard gate.
  5. Manual smoke test: run any swamp command against an existing repo. Output must be identical to before this change.

Patterns to Follow

Look at the existing value objects and identity types in src/domain/data/ — DataId, CompositeName, etc. Follow whichever pattern fits best for a validated, immutable identifier. Look at composite_name_test.ts for the canonical test structure. Follow the DDD conventions described in the ddd skill.

For the config changes, trace how hydrationStrategy was added — it's the most recent optional field added to the config types and flows through the same path namespace will take.

Colons Are Load-Bearing

The colon in namespace:model is the syntax that phases 4 and 5 depend on for cross-namespace queries and CLI output. The parseNamespacedModelName function is the single parser that everything downstream will use. It needs to handle the wildcard (*:model) cleanly because that's how users query across all namespaces. Get the edge cases right here and phases 4-5 are straightforward. Get them wrong and we'll be patching the parser under pressure later.

Sign in to post a ripple.