From 4e318c85913f6a6dc2ef470a0318984aea169054 Mon Sep 17 00:00:00 2001 From: jochen Date: Wed, 4 Nov 2026 11:58:01 +0100 Subject: [PATCH] CAT-G2 (provision), CAT-G2b: the forge provides go-package-registry and keeps serving the npm registry's public root MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit go-package-registry, mesh scope, read with no credential, is offered only by the forge module's new build, so mesh-build-go's fetch from the Go seat waits for it (design 51, E2); its read side is the forge's Go proxy. The npm registry's public-root stays served (ADR 0346 §6, the operator 2026-11-04). Both rows of the forge's manifest test. Claude-Session: https://claude.ai/code/session_01JG73KJNrH5EgFXMKKYSrig --- modules/gitea/cmd/forge/forge_test.go | 26 ++++++++++++++++++- modules/gitea/module.json | 36 +++++++++++++++++++++------ 2 files changed, 53 insertions(+), 9 deletions(-) diff --git a/modules/gitea/cmd/forge/forge_test.go b/modules/gitea/cmd/forge/forge_test.go index 01883b7c..7b3284b9 100644 --- a/modules/gitea/cmd/forge/forge_test.go +++ b/modules/gitea/cmd/forge/forge_test.go @@ -648,7 +648,13 @@ func TestToolsAreTheOnesTheReadmeNamesAndTheManifestBuilds(t *testing.T) { t.Fatal(err) } var m struct { - Tools []string `json:"tools"` + Tools []string `json:"tools"` + Serves map[string]map[string]any `json:"serves"` + Provides []struct { + Name string `json:"name"` + Scope string `json:"scope"` + Credential map[string]string `json:"credential"` + } `json:"provides"` Build struct { Artifacts []struct { From string `json:"from"` @@ -664,6 +670,24 @@ func TestToolsAreTheOnesTheReadmeNamesAndTheManifestBuilds(t *testing.T) { if len(m.Tools) != 0 { t.Error("the manifest lists tools: its claims would offer them as both seats' verbs") } + // CAT-G2b (ADR 0346 §6, the operator 2026-11-04): the forge keeps serving the npm registry's public root, so a lock + // file naming it installs as today. CAT-G2 (design 51, E2): the Go seat's read side is the provision + // go-package-registry, mesh scope, read with no credential, offered only by this build. + if root, _ := m.Serves["npm-package-registry"]["public-root"].(string); !strings.HasPrefix(root, "https://") || !strings.HasSuffix(root, "/api/packages/novox/npm/") { + t.Errorf("npm-package-registry serves public-root %q: the public root must stay", root) + } + if g := m.Serves["go-package-registry"]; g["go-path"] != "/api/packages/novox/go" { + t.Errorf("go-package-registry serves %v, want the forge's Go proxy path", g) + } + goProvided := false + for _, p := range m.Provides { + if p.Name == "go-package-registry" { + goProvided = p.Scope == "mesh" && p.Credential["form"] == "none" + } + } + if !goProvided { + t.Error("go-package-registry is not provided at mesh scope with no credential") + } found := false for _, a := range m.Build.Artifacts { if a.From == "cmd/forge" { diff --git a/modules/gitea/module.json b/modules/gitea/module.json index 2fd3ae33..a8af60b9 100644 --- a/modules/gitea/module.json +++ b/modules/gitea/module.json @@ -99,7 +99,7 @@ "port": 22, "protocol": "tcp", "from": "mesh", - "why": "git over ssh, gitea's own unmodified sshd. Published on the machine's own side at 222, the mesh's fixed public convention — not 22, which the machine's own daemon holds and a module does not take" + "why": "git over ssh, gitea's own unmodified sshd. Published on the machine's own side at 222, the mesh's fixed public convention \u2014 not 22, which the machine's own daemon holds and a module does not take" }, { "name": "forge-events", @@ -119,6 +119,11 @@ "git": { "scheme": "http", "port": 3000 + }, + "go-package-registry": { + "scheme": "http", + "port": 3000, + "go-path": "/api/packages/novox/go" } }, "receives": { @@ -169,7 +174,7 @@ }, "keep-main-rules": { "type": "boolean", - "description": "Whether the module keeps main's branch protection as the mesh wants it. True: every ten minutes, and at the start, main's rule in every repository of the organisation novox is put to no push and the tools' account alone merging, and each apply of the `repositories` value narrows the main rule of the repositories it lists, requiring no status. False (the default): the module changes no existing repository's branch protection, whoever set it at the forge, but takes away a required status it finds on a rule covering main, once (ADR 0534 §1); a repository listed in `repositories` that does not exist is still made, with its first protection.", + "description": "Whether the module keeps main's branch protection as the mesh wants it. True: every ten minutes, and at the start, main's rule in every repository of the organisation novox is put to no push and the tools' account alone merging, and each apply of the `repositories` value narrows the main rule of the repositories it lists, requiring no status. False (the default): the module changes no existing repository's branch protection, whoever set it at the forge, but takes away a required status it finds on a rule covering main, once (ADR 0534 \u00a71); a repository listed in `repositories` that does not exist is still made, with its first protection.", "trusted": true, "default": false }, @@ -181,15 +186,17 @@ }, "check-gates-merge": { "type": "boolean", - "description": "Whether gitea_merge_pull_request judges mesh/check before it merges. True (the default): a head merges only on a passing mesh/check judged against the trunk's head (ADR 0447, ADR 0534 §4). False (the operator, 2026-10-25 22:56: \"run the check locally only, but not as a gate, it loses us too much time\"): a head the operator's Approve names in approved_heads merges whatever its mesh/check says (pending, failed, missing or judged on an older trunk); a head that moved, a branch no rule guards and a merge git refuses are still refused. Set together with mesh-delivery-gate's check-gates-merge.", + "description": "Whether gitea_merge_pull_request judges mesh/check before it merges. True (the default): a head merges only on a passing mesh/check judged against the trunk's head (ADR 0447, ADR 0534 \u00a74). False (the operator, 2026-10-25 22:56: \"run the check locally only, but not as a gate, it loses us too much time\"): a head the operator's Approve names in approved_heads merges whatever its mesh/check says (pending, failed, missing or judged on an older trunk); a head that moved, a branch no rule guards and a merge git refuses are still refused. Set together with mesh-delivery-gate's check-gates-merge.", "trusted": true, "default": true }, "main-mergers": { "type": "list", - "description": "The forge logins that may merge into main beside the tools' account, on every repository of the organisation (ADR 0534 §2): the operator's own, so the operator can always merge by hand at the forge. The tools' account is always on main's merge whitelist; no agent's account belongs here.", + "description": "The forge logins that may merge into main beside the tools' account, on every repository of the organisation (ADR 0534 \u00a72): the operator's own, so the operator can always merge by hand at the forge. The tools' account is always on main's merge whitelist; no agent's account belongs here.", "trusted": true, - "default": ["jschoubben"] + "default": [ + "jschoubben" + ] } }, "facts": { @@ -397,16 +404,27 @@ "provides": [ { "name": "npm-package-registry", - "credential": {"form": "pair"}, + "credential": { + "form": "pair" + }, "scope": "mesh", "identity": { "max": 40, "in": "a Gitea user name" } }, + { + "name": "go-package-registry", + "credential": { + "form": "none" + }, + "scope": "mesh" + }, { "name": "git", - "credential": {"form": "none"}, + "credential": { + "form": "none" + }, "scope": "mesh" }, { @@ -419,7 +437,9 @@ }, { "name": "forge-account", - "credential": {"form": "none"}, + "credential": { + "form": "none" + }, "scope": "mesh", "answered-by": "helper" }