refactor(catalog): explicit org/repo terminology; npm targets coulomb Gitea registry

Gitea's "project/package/release" terms are overloaded, so the catalog now uses
the most explicit words:
- org  = coulomb (the Gitea organisation)
- repo = whynot-design (the Gitea repository/product) — not an org, not a scope
- npm scope @whynot and package @whynot/design are distinct from both

Changes:
- catalog schema: replace conflated `owner` with required `org` + `repo`; `owner`
  is now a derived `org/repo` slug property
- npm-config delivery is data-driven: registry + scope live in
  delivery_config.npm and are validated; engine no longer hardcodes a registry
- exec delivery writes `<scope>:registry=<url>` + scoped `:_authToken` for the
  configured Gitea registry (token still env-expanded, never written to disk)
- pilot lane points at https://gitea.coulomb.social/api/packages/coulomb/npm/,
  scope @whynot, KV path coulomb/whynot-design/npm/publish
- npm-publish-demo uses @whynot scope so dry-run resolves the Gitea registry
- docs: terminology table; routing owner shown as coulomb/whynot-design
- tests: org/repo required, npm-config validation, registry authkey mapping

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
tegwick 2026-06-28 12:44:55 +02:00
parent 147cf8acda
commit f87f4e5e4d
9 changed files with 153 additions and 17 deletions

View file

@ -1,7 +1,8 @@
# Example BUILD-stage lane. Demonstrates that build entries can be looser:
# generated test values are allowed and no production decision is required.
id: example-build-test-token
owner: platform-ci
org: coulomb
repo: platform-ci
stage: build
description: >-
Throwaway generated credential for build-stage integration tests. May be

View file

@ -1,17 +1,25 @@
# whynot-design npm publish token — the MVP pilot lane.
# This file is NON-SECRET. It describes where the token lives in OpenBao and how
# it may be consumed. The token VALUE never appears here.
#
# Terminology (Gitea is overloaded — we use the most explicit words):
# org = coulomb the Gitea organisation
# repo = whynot-design the Gitea repository / product (NOT an org, NOT a scope)
# npm package = @whynot/design published to the coulomb Gitea npm registry
# "@whynot" is the npm *scope*; it is neither the org nor the repo name.
id: whynot-design-npm-publish
owner: whynot-design
org: coulomb
repo: whynot-design
stage: prod
description: >-
npm automation token used to publish the whynot-design package. Delivered to
npm automation token used to publish the @whynot/design package from the
coulomb/whynot-design repo to the coulomb Gitea npm registry. Delivered to
`npm publish` via an exec-time temporary npm config; never printed or exported
into the parent shell.
# OpenBao KV v2 location of the secret material.
# OpenBao KV v2 location of the secret material (org/repo-scoped path).
mount: secret
path: whynot-design/npm/publish
path: coulomb/whynot-design/npm/publish
# Field(s) inside the KV entry. The publish token is stored under this key.
fields:
@ -21,14 +29,22 @@ fields:
consumers:
- name: whynot-design-ci
auth: approle # bound OpenBao auth method
claim: "role:whynot-design-publish"
purpose: "publish whynot-design npm package from CI"
claim: "repo:coulomb/whynot-design"
purpose: "publish @whynot/design to the coulomb Gitea npm registry from CI"
# How the value may leave OpenBao. npm-config = temp .npmrc for the child only.
delivery_modes:
- npm-config
- read-check
# npm-specific delivery target. The registry/scope live here as catalog DATA so
# the engine never hardcodes a registry. Matches coulomb/whynot-design/.npmrc.
delivery_config:
npm:
registry: "https://gitea.coulomb.social/api/packages/coulomb/npm/"
scope: "@whynot"
package: "@whynot/design"
# Privileged actions on this lane require an approved decision/CCR.
approval:
model: decision