Skip to content

refactor: rename "namespace" to "collective" across extensions, models, and vaults#639

Merged
stack72 merged 1 commit intomainfrom
rename-namespace-to-collective
Mar 6, 2026
Merged

refactor: rename "namespace" to "collective" across extensions, models, and vaults#639
stack72 merged 1 commit intomainfrom
rename-namespace-to-collective

Conversation

@stack72
Copy link
Copy Markdown
Contributor

@stack72 stack72 commented Mar 6, 2026

Summary

Standardizes terminology across the codebase to use "collective" instead of "namespace" when referring to the swamp organizational unit (@org/name scoped names). "Collective" is the correct ubiquitous language — a user is a member of one or more collectives, and extensions are published to a collective.

Scope rule

Only renames "namespace" → "collective" when it refers to swamp's organizational unit. External concepts (HashiCorp Vault enterprise namespaces, CEL expression namespaces) are left unchanged.

Changes

Domain layer

  • extension_namespace_validator.tsextension_collective_validator.ts — renamed file, types (NamespaceMismatchCollectiveMismatch, NamespaceValidationResultCollectiveValidationResult), and function (validateContentNamespaces()validateContentCollectives())
  • extension_manifest.ts — error messages and comments: @namespace/name@collective/name, reserved namespacereserved collective
  • model_type.tsRESERVED_NAMESPACESRESERVED_COLLECTIVES, isReservedNamespace()isReservedCollective()
  • user_model_loader.tsvalidateUserNamespace()validateUserCollective()
  • user_vault_loader.ts — comments and error messages updated

CLI commands

  • extension search--namespace flag → --collective flag
  • extension push — now validates the manifest collective against the user's actual collectives from the whoami API (via organizations response), with username fallback when the server is unreachable. Blocks the push with a clear error if the collective doesn't match (no more prompt to continue)
  • extension pull/yank/rm — error message format: @namespace/name@collective/name
  • auth whoami — now displays collectives (extracted from organizations) in both JSON and log output

Infrastructure

  • swamp_club_client.ts — added WhoamiOrganization interface, organizations field on WhoamiResponse, and getCollectives() helper that extracts org slugs
  • extension_api_client.ts — search param namespacecollective

Presentation

  • extension_push_output.tsrenderExtensionPushNamespaceErrors()renderExtensionPushCollectiveErrors(), JSON key namespaceErrorscollectiveErrors

Documentation & skills

  • design/extension.md — all organizational "namespace" → "collective"
  • .claude/skills/swamp-extension-model/ — SKILL.md, publishing.md, troubleshooting.md updated
  • .claude/skills/swamp-vault/ — SKILL.md, troubleshooting.md, user-defined-vaults.md updated (only swamp org references, not HashiCorp Vault namespace refs)

User-facing changes

  1. swamp extension search: The --namespace flag is now --collective

    # Before
    swamp extension search --namespace stack72
    # After
    swamp extension search --collective stack72
  2. swamp extension push: Collective validation now checks against your actual collectives from the server (not just username). If the manifest collective isn't one of yours, the push is blocked with a clear error listing your available collectives:

    Extension collective "@swampadmin" is not one of your collectives (@swamp, @system-initiative, @stack72).
    Use one of: @swamp, @system-initiative, @stack72
    
  3. swamp auth whoami: Now shows collectives in both output modes:

    stack72 ([email protected]) on https://club.swamp.sh
    Collectives: swamp, system-initiative, stack72
    
  4. Error messages: All error messages now use "collective" instead of "namespace" (e.g., reserved collective instead of reserved namespace)

Test plan

  • All 2733 tests pass
  • deno check — type checking passes
  • deno lint — linting passes
  • deno fmt — formatting passes
  • deno run compile — binary compiles
  • Manual testing of swamp extension push with collective validation

🤖 Generated with Claude Code

…s, and vaults

Standardize terminology to use "collective" (the correct ubiquitous language)
instead of "namespace" when referring to the organizational unit in scoped
names like @org/name.

Additionally, extension push now validates the manifest collective against
the user's actual collectives from the whoami API (with username fallback
when the server is unreachable), and blocks the push if the collective
doesn't match rather than prompting to continue.

Co-Authored-By: Claude Opus 4.6 <[email protected]>
Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Adversarial Review

Critical / High

None identified.

Medium

  1. Empty organizations array blocks all pushessrc/cli/commands/extension_push.ts:136-138

    Description: When getCollectives() returns an empty array [] (user has no organizations), the collective check will always fail because [].includes(collectivePart) returns false for any value. The username fallback only triggers when the server is unreachable, not when the server returns empty organizations.

    Breaking example:

    • User "alice" has an account but belongs to no organizations
    • Server responds with { authenticated: true, username: "alice", organizations: [] }
    • collectives = [] (truthy, so no username fallback)
    • [].includes("alice")false
    • User cannot push @alice/my-extension despite the manifest collective matching their username

    Impact: If the server doesn't automatically include a personal org (with slug = username) in the organizations list, new users with no org memberships will be completely blocked from pushing.

    Suggested fix: Either:

    • (a) Fall back to username check when collectives is empty: const isAllowed = collectives && collectives.length > 0 ? collectives.includes(collectivePart) : collectivePart === credentials.username;
    • (b) Confirm server-side always includes personal org and document this assumption
  2. Broad exception catch masks server errorssrc/cli/commands/extension_push.ts:132-135

    Description: The catch block silently falls back to username-only checking for any error, including transient server errors (500s), malformed JSON responses, or other unexpected failures.

    Breaking example:

    • Server is reachable but returns 500 due to temporary database issue
    • Exception is caught, debug message logged
    • User successfully pushes using username-only check
    • Later, when server is healthy, push fails because the collective wasn't in their actual org list
    • User is confused about inconsistent behavior

    Suggested fix: Consider distinguishing error types. Log at warn level for unexpected errors, and only fall back silently for explicit network/timeout errors.

Low

  1. No unit tests for getCollectives() helpersrc/infrastructure/http/swamp_club_client.ts:61-66

    The function has three distinct behaviors (undefined orgs → undefined, empty array → [], populated → slugs) but no dedicated tests. The existing swamp_club_client_test.ts doesn't test this function.

  2. validateContentCollectives silently passes on malformed extension namessrc/domain/extensions/extension_collective_validator.ts:46-48

    If extensionName has no /, the function returns { valid: true, mismatches: [] } without any validation. While this is defensive, it could mask configuration errors upstream. The manifest schema should prevent this, but defensive code that silently succeeds can hide bugs.

Verdict

PASS — The refactoring is mechanically correct. The namespace→collective rename is consistent across all touched files. The two medium issues are edge cases that depend on server-side behavior assumptions. If the server always includes a personal org for each user, Medium #1 is a non-issue. The code is otherwise solid.

Copy link
Copy Markdown

@github-actions github-actions Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review Summary

This PR correctly applies ubiquitous language principles from Domain-Driven Design by renaming "namespace" to "collective" for swamp's organizational unit concept. The change is well-scoped and consistently applied.

Blocking Issues

None.

Code Quality Assessment

TypeScript/Code Style ✓

  • All exports are named (no default exports)
  • No new any types introduced (the pre-existing one in auth_whoami.ts was already there)
  • Proper type definitions for new interfaces (WhoamiOrganization, CollectiveMismatch, etc.)
  • License headers present on all files

Domain-Driven Design ✓

  • Correctly updates ubiquitous language terminology
  • Well-designed value objects (CollectiveMismatch, CollectiveValidationResult)
  • Pure domain functions (validateContentCollectives(), getCollectives())
  • Proper separation: domain logic in domain layer, infrastructure in infrastructure layer
  • Scope rule correctly applied: only renames swamp's organizational concept, leaves external "namespace" concepts (like CEL/HashiCorp Vault) unchanged

Test Coverage ✓

  • extension_collective_validator_test.ts has comprehensive tests (9 test cases)
  • model_type_test.ts properly tests isReservedCollective()
  • extension_api_client_test.ts updated to use collective param
  • Tests live next to source files as required

Security ✓

  • No security vulnerabilities introduced
  • Proper error handling maintained

Suggestions (non-blocking)

  1. Consistency note: The methods isUserNamespace() and getUserNamespace() in model_type.ts weren't renamed. This is acceptable per the PR's scope rule (they refer to the syntactic @prefix notation rather than the organizational concept), but could be documented in a code comment for clarity.

Overall, this is a clean refactoring that improves domain language consistency without introducing any issues.

@stack72 stack72 merged commit 4172137 into main Mar 6, 2026
6 checks passed
@stack72 stack72 deleted the rename-namespace-to-collective branch March 6, 2026 19:53
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant