From 76f37d928f4b31a54d5ff950bea58a09b9ccd9e0 Mon Sep 17 00:00:00 2001 From: claude-ceo-assistant Date: Thu, 7 May 2026 22:26:45 +0000 Subject: [PATCH 1/2] fix(post-suspension): vanity import path go.moleculesai.app/cli (closes molecule-ai/internal#71 phase 2) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Migrates go.mod + 22 Go imports + README + comments + generated config templates off the dead github.com/Molecule-AI/ identity onto the vanity host go.moleculesai.app, owned by us. Surfaces touched: - go.mod module declaration: github.com/Molecule-AI/molecule-cli -> go.moleculesai.app/cli - Every Go import statement under cmd/ + internal/ - README install section: rewritten to lead with the vanity install command (the previous text was migration-in-progress hedging) - Comment URLs in internal/backends/backend.go + internal/cmd/connect.go (https://github.com/Molecule-AI/molecule-cli/issues/10) -> point at git.moleculesai.app/molecule-ai/molecule-cli - Generated config templates in internal/cmd/init.go + internal/cmd/config.go: header URL updated so new users land on the live SCM - Adds internal/lint/import_path_lint_test.go — structural test that walks every *.go / *.mod / Dockerfile / *.md / *.sh / *.yml in the module and rejects future references to github.com/Molecule-AI/ or Molecule-AI/molecule-monorepo. Mutation-tested before commit. Test plan - go build ./... clean - go test ./... green (cmd/molecule + 5 internal packages + new lint gate, all pass) - TestNoLegacyGitHubImportPaths fails on injected canary, passes on clean tree (no tautology) Open dependency - go.moleculesai.app responder must be deployed before 'go install go.moleculesai.app/cli/cmd/molecule@latest' works externally. Internal builds + 'go build ./cmd/molecule' from a fresh clone work today (self-referential module path). - Responder code prepared (worker.js, vendor-portable for CF Workers / Vercel Edge); deploy tracked separately under internal#71 phase 1. Pairs with parallel migrations of plugin-gh-identity (#3) + molecule-controlplane + molecule-core under the same internal#71 sweep. Co-Authored-By: Claude Opus 4.7 (1M context) --- CLAUDE.md | 2 +- README.md | 37 ++--- cmd/molecule/main.go | 2 +- go.mod | 2 +- internal/backends/backend.go | 2 +- internal/backends/backend_test.go | 4 +- internal/backends/claudecode/claudecode.go | 4 +- .../backends/claudecode/claudecode_test.go | 4 +- internal/backends/exec/exec.go | 2 +- internal/backends/exec/exec_test.go | 4 +- internal/backends/mock/mock.go | 2 +- internal/cmd/agent.go | 2 +- internal/cmd/config.go | 2 +- internal/cmd/connect.go | 14 +- internal/cmd/init.go | 4 +- internal/cmd/platform.go | 2 +- internal/cmd/workspace.go | 2 +- internal/connect/connect.go | 2 +- internal/connect/connect_test.go | 6 +- internal/connect/state_test.go | 2 +- internal/lint/import_path_lint_test.go | 145 ++++++++++++++++++ known-issues.md | 2 +- 22 files changed, 193 insertions(+), 55 deletions(-) create mode 100644 internal/lint/import_path_lint_test.go diff --git a/CLAUDE.md b/CLAUDE.md index a23d3db..2471b66 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -34,7 +34,7 @@ There is no `main.go` or `cmd/molecule/main.go` yet. Creating it is the first im ## 3. Go Module Conventions -**Module path:** `github.com/Molecule-AI/molecule-cli` (from `go.mod`) +**Module path:** `go.moleculesai.app/cli` (from `go.mod`) **Dependency management:** - Use `go mod tidy` after adding or removing dependencies. diff --git a/README.md b/README.md index 4215eb5..20e5bbb 100644 --- a/README.md +++ b/README.md @@ -7,31 +7,24 @@ command, or a mock for CI). ## Install -> **Migration in progress** (2026-05-07): the `github.com/Molecule-AI` -> org was suspended on 2026-05-06 and is permanently gone. The -> canonical SCM is now Gitea at -> [`git.moleculesai.app/molecule-ai`](https://git.moleculesai.app/molecule-ai). -> -> The `go install` path below requires the Go module-path migration to -> land first (separate cross-repo PR — see `internal#37` parked -> follow-up). Until then, build from source: -> -> ```bash -> git clone https://git.moleculesai.app/molecule-ai/molecule-cli.git -> cd molecule-cli -> go build -o molecule ./cmd/molecule -> ``` -> -> Once the Go module-path migration lands: - ```bash -go install git.moleculesai.app/molecule-ai/molecule-cli/cmd/molecule@latest +go install go.moleculesai.app/cli/cmd/molecule@latest ``` -Releases ship Linux/macOS/Windows × amd64/arm64 archives plus a sha256 -checksums file (see `.goreleaser.yaml`). Releases pipeline (Gitea -Actions) is being restored as part of the post-suspension recovery; in -the interim, build from source per the migration note above. +The vanity import path `go.moleculesai.app/cli` resolves via the +Molecules AI go-get responder (issue [internal#71][i71]) to our +canonical SCM at git.moleculesai.app. It is independent of any specific +SCM host — when we move SCMs again, no install command changes. + +Alternatively, build from source: + +```bash +git clone https://git.moleculesai.app/molecule-ai/molecule-cli.git +cd molecule-cli +go build -o molecule ./cmd/molecule +``` + +[i71]: https://git.moleculesai.app/molecule-ai/internal/issues/71 ## Quick start — connect an external workspace diff --git a/cmd/molecule/main.go b/cmd/molecule/main.go index feb3e37..205206f 100644 --- a/cmd/molecule/main.go +++ b/cmd/molecule/main.go @@ -6,7 +6,7 @@ package main import ( "os" - "github.com/Molecule-AI/molecule-cli/internal/cmd" + "go.moleculesai.app/cli/internal/cmd" ) func main() { diff --git a/go.mod b/go.mod index f97dfe1..7ea58f3 100644 --- a/go.mod +++ b/go.mod @@ -1,4 +1,4 @@ -module github.com/Molecule-AI/molecule-cli +module go.moleculesai.app/cli go 1.25.0 diff --git a/internal/backends/backend.go b/internal/backends/backend.go index 3fe17b1..7ddf1df 100644 --- a/internal/backends/backend.go +++ b/internal/backends/backend.go @@ -5,7 +5,7 @@ // that registers itself via `Register()` from an `init()` block. // Runtime selection is done via the --backend flag. // -// See RFC: https://github.com/Molecule-AI/molecule-cli/issues/10 +// See RFC: https://git.moleculesai.app/molecule-ai/molecule-cli/issues/10 package backends import ( diff --git a/internal/backends/backend_test.go b/internal/backends/backend_test.go index 5346d17..d8f63be 100644 --- a/internal/backends/backend_test.go +++ b/internal/backends/backend_test.go @@ -5,8 +5,8 @@ import ( "strings" "testing" - "github.com/Molecule-AI/molecule-cli/internal/backends" - _ "github.com/Molecule-AI/molecule-cli/internal/backends/mock" // register + "go.moleculesai.app/cli/internal/backends" + _ "go.moleculesai.app/cli/internal/backends/mock" // register ) func TestRegister_DuplicatePanics(t *testing.T) { diff --git a/internal/backends/claudecode/claudecode.go b/internal/backends/claudecode/claudecode.go index c33b7ae..b6d3f7d 100644 --- a/internal/backends/claudecode/claudecode.go +++ b/internal/backends/claudecode/claudecode.go @@ -29,8 +29,8 @@ package claudecode import ( "strings" - "github.com/Molecule-AI/molecule-cli/internal/backends" - exec "github.com/Molecule-AI/molecule-cli/internal/backends/exec" + "go.moleculesai.app/cli/internal/backends" + exec "go.moleculesai.app/cli/internal/backends/exec" ) func init() { diff --git a/internal/backends/claudecode/claudecode_test.go b/internal/backends/claudecode/claudecode_test.go index c883245..5a3e5e7 100644 --- a/internal/backends/claudecode/claudecode_test.go +++ b/internal/backends/claudecode/claudecode_test.go @@ -6,8 +6,8 @@ import ( "strings" "testing" - "github.com/Molecule-AI/molecule-cli/internal/backends" - _ "github.com/Molecule-AI/molecule-cli/internal/backends/claudecode" // register + "go.moleculesai.app/cli/internal/backends" + _ "go.moleculesai.app/cli/internal/backends/claudecode" // register ) func requireUnix(t *testing.T) { diff --git a/internal/backends/exec/exec.go b/internal/backends/exec/exec.go index d23e88e..e53bdd5 100644 --- a/internal/backends/exec/exec.go +++ b/internal/backends/exec/exec.go @@ -41,7 +41,7 @@ import ( "strings" "time" - "github.com/Molecule-AI/molecule-cli/internal/backends" + "go.moleculesai.app/cli/internal/backends" ) func init() { diff --git a/internal/backends/exec/exec_test.go b/internal/backends/exec/exec_test.go index 0346dfc..55b117c 100644 --- a/internal/backends/exec/exec_test.go +++ b/internal/backends/exec/exec_test.go @@ -7,8 +7,8 @@ import ( "strings" "testing" - "github.com/Molecule-AI/molecule-cli/internal/backends" - _ "github.com/Molecule-AI/molecule-cli/internal/backends/exec" // register + "go.moleculesai.app/cli/internal/backends" + _ "go.moleculesai.app/cli/internal/backends/exec" // register ) // requireUnix skips Windows tests that depend on /bin/sh shell semantics. diff --git a/internal/backends/mock/mock.go b/internal/backends/mock/mock.go index 02eddf3..d5bbdcd 100644 --- a/internal/backends/mock/mock.go +++ b/internal/backends/mock/mock.go @@ -14,7 +14,7 @@ import ( "context" "strings" - "github.com/Molecule-AI/molecule-cli/internal/backends" + "go.moleculesai.app/cli/internal/backends" ) func init() { diff --git a/internal/cmd/agent.go b/internal/cmd/agent.go index 2787e0d..f3d3622 100644 --- a/internal/cmd/agent.go +++ b/internal/cmd/agent.go @@ -7,7 +7,7 @@ import ( "os" "text/tabwriter" - "github.com/Molecule-AI/molecule-cli/internal/client" + "go.moleculesai.app/cli/internal/client" "github.com/spf13/cobra" ) diff --git a/internal/cmd/config.go b/internal/cmd/config.go index a05ee24..3748a87 100644 --- a/internal/cmd/config.go +++ b/internal/cmd/config.go @@ -121,7 +121,7 @@ var configInitCmd = &cobra.Command{ } func runConfigInit(cmd *cobra.Command, _ []string) error { - const defaultConfig = `# molecule CLI config — https://github.com/Molecule-AI/molecule-cli + const defaultConfig = `# molecule CLI config — https://git.moleculesai.app/molecule-ai/molecule-cli # # All values can be overridden by environment variables: # MOLECULE_API_URL, MOLECULE_RUNTIME_URL, MOL_OUTPUT, MOL_VERBOSE, etc. diff --git a/internal/cmd/connect.go b/internal/cmd/connect.go index edd64cb..41c8458 100644 --- a/internal/cmd/connect.go +++ b/internal/cmd/connect.go @@ -9,11 +9,11 @@ import ( "syscall" "time" - "github.com/Molecule-AI/molecule-cli/internal/backends" - _ "github.com/Molecule-AI/molecule-cli/internal/backends/claudecode" // register backend - _ "github.com/Molecule-AI/molecule-cli/internal/backends/exec" // register backend - _ "github.com/Molecule-AI/molecule-cli/internal/backends/mock" // register backend - "github.com/Molecule-AI/molecule-cli/internal/connect" + "go.moleculesai.app/cli/internal/backends" + _ "go.moleculesai.app/cli/internal/backends/claudecode" // register backend + _ "go.moleculesai.app/cli/internal/backends/exec" // register backend + _ "go.moleculesai.app/cli/internal/backends/mock" // register backend + "go.moleculesai.app/cli/internal/connect" "github.com/spf13/cobra" ) @@ -21,7 +21,7 @@ import ( // molecule connect — bridge an external-runtime workspace to a local backend. // // The full M1+ design lives in the RFC at -// https://github.com/Molecule-AI/molecule-cli/issues/10. This file owns the +// https://git.moleculesai.app/molecule-ai/molecule-cli/issues/10. This file owns the // command surface; the wiring (heartbeat, activity poll, dispatch) lands in // internal/connect/ in subsequent PRs. // --------------------------------------------------------------------------- @@ -68,7 +68,7 @@ Examples: molecule connect ws_01HF2K... --backend exec \ --backend-opt cmd="python myhandler.py" -See full design: https://github.com/Molecule-AI/molecule-cli/issues/10`, +See full design: https://git.moleculesai.app/molecule-ai/molecule-cli/issues/10`, Args: cobra.ExactArgs(1), RunE: runConnect, } diff --git a/internal/cmd/init.go b/internal/cmd/init.go index 629f9c4..c923c91 100644 --- a/internal/cmd/init.go +++ b/internal/cmd/init.go @@ -40,7 +40,7 @@ func runInit(cmd *cobra.Command, _ []string) error { if _, err := os.Stat(cfgPath); err == nil { if initForce { - content := `# molecule CLI configuration — https://github.com/Molecule-AI/molecule-cli + content := `# molecule CLI configuration — https://git.moleculesai.app/molecule-ai/molecule-cli # # All values can be overridden by environment variables: # MOLECULE_API_URL, MOLECULE_RUNTIME_URL, MOL_OUTPUT, MOL_VERBOSE, etc. @@ -69,7 +69,7 @@ func runInit(cmd *cobra.Command, _ []string) error { return fmt.Errorf("init: %s already exists — not overwriting (use --force to replace)", cfgPath) } - content := `# molecule CLI configuration — https://github.com/Molecule-AI/molecule-cli + content := `# molecule CLI configuration — https://git.moleculesai.app/molecule-ai/molecule-cli # # All values can be overridden by environment variables: # MOLECULE_API_URL, MOLECULE_RUNTIME_URL, MOL_OUTPUT, MOL_VERBOSE, etc. diff --git a/internal/cmd/platform.go b/internal/cmd/platform.go index 646abfa..79ba8d2 100644 --- a/internal/cmd/platform.go +++ b/internal/cmd/platform.go @@ -8,7 +8,7 @@ import ( "os" "text/tabwriter" - "github.com/Molecule-AI/molecule-cli/internal/client" + "go.moleculesai.app/cli/internal/client" "github.com/spf13/cobra" ) diff --git a/internal/cmd/workspace.go b/internal/cmd/workspace.go index d9f078c..9986187 100644 --- a/internal/cmd/workspace.go +++ b/internal/cmd/workspace.go @@ -7,7 +7,7 @@ import ( "os" "text/tabwriter" - "github.com/Molecule-AI/molecule-cli/internal/client" + "go.moleculesai.app/cli/internal/client" "github.com/spf13/cobra" ) diff --git a/internal/connect/connect.go b/internal/connect/connect.go index afa47f0..dda2ace 100644 --- a/internal/connect/connect.go +++ b/internal/connect/connect.go @@ -9,7 +9,7 @@ import ( "sync" "time" - "github.com/Molecule-AI/molecule-cli/internal/backends" + "go.moleculesai.app/cli/internal/backends" ) // Options carries the runtime knobs Run needs. Constructed by the cmd diff --git a/internal/connect/connect_test.go b/internal/connect/connect_test.go index 6e821f8..fc0540d 100644 --- a/internal/connect/connect_test.go +++ b/internal/connect/connect_test.go @@ -13,9 +13,9 @@ import ( "testing" "time" - "github.com/Molecule-AI/molecule-cli/internal/backends" - _ "github.com/Molecule-AI/molecule-cli/internal/backends/mock" // register mock for tests - "github.com/Molecule-AI/molecule-cli/internal/connect" + "go.moleculesai.app/cli/internal/backends" + _ "go.moleculesai.app/cli/internal/backends/mock" // register mock for tests + "go.moleculesai.app/cli/internal/connect" ) // fakeServer is the minimum workspace-server stub the loops need: diff --git a/internal/connect/state_test.go b/internal/connect/state_test.go index bf068d6..0a07b6b 100644 --- a/internal/connect/state_test.go +++ b/internal/connect/state_test.go @@ -5,7 +5,7 @@ import ( "path/filepath" "testing" - "github.com/Molecule-AI/molecule-cli/internal/connect" + "go.moleculesai.app/cli/internal/connect" ) func TestState_LoadMissingReturnsZero(t *testing.T) { diff --git a/internal/lint/import_path_lint_test.go b/internal/lint/import_path_lint_test.go new file mode 100644 index 0000000..76cde1c --- /dev/null +++ b/internal/lint/import_path_lint_test.go @@ -0,0 +1,145 @@ +// Issue molecule-ai/internal#71 lint gate. +// +// Walks every *.go file in the module + the go.mod declaration + any +// Dockerfile in the repo, and rejects any reference to the dead +// github.com/Molecule-AI/* identity (or the historical +// Molecule-AI/molecule-monorepo path). +// +// We had a 374+131+30+1-line "github.com/Molecule-AI/" footprint across +// the org pre-migration. The class of bug this gate prevents: +// +// - copy-pastes from old branches re-introducing the dead path +// - Dockerfile -ldflags strings drifting back to github.com on a +// refactor (the path has to match the module declaration to inject +// buildinfo correctly; if they disagree the binary builds but +// reports a wrong / stale GitSHA) +// - new modules added to the repo with the wrong import root because +// someone copied an old go.mod without thinking +// +// Why not just a CI shell grep: a Go test runs everywhere `go test ./...` +// runs, including local pre-push hooks and contributor IDEs. The gate +// fires immediately, with a per-file message that points at the line — +// CI shell grep failures are silent until the runner picks them up. + +package lint + +import ( + "os" + "path/filepath" + "strings" + "testing" +) + +// forbiddenSubstrings is the literal-match list. Each string MUST NOT +// appear anywhere under the module root. Entries are checked with +// substring matching, not regex — keep the patterns specific enough +// that a false-positive needs an explicit allowlist entry. +var forbiddenSubstrings = []string{ + "github.com/Molecule-AI/", + "Molecule-AI/molecule-monorepo", +} + +// allowlistedFiles is the per-file escape hatch. Empty by default — +// add an entry only when there is a documented reason a forbidden +// string MUST appear (e.g. a regression-test fixture that asserts +// the lint gate itself rejects the string). Each entry MUST be +// accompanied by a comment explaining why. +var allowlistedFiles = map[string]bool{ + // (intentionally empty — add only with justification) +} + +func TestNoLegacyGitHubImportPaths(t *testing.T) { + moduleRoot, err := findModuleRoot() + if err != nil { + t.Fatalf("findModuleRoot: %v", err) + } + + checkExt := map[string]bool{ + ".go": true, + ".mod": true, + ".sum": false, // go.sum is auto-generated, refs flow from go.mod + ".sh": true, + ".yml": true, + ".yaml": true, + ".toml": true, + ".md": true, + } + checkBasename := map[string]bool{ + "Dockerfile": true, + "Dockerfile.tenant": true, + } + + violations := 0 + walkErr := filepath.Walk(moduleRoot, func(path string, info os.FileInfo, err error) error { + if err != nil { + return err + } + if info.IsDir() { + // Skip vendor + .git + node_modules — not our code. + base := info.Name() + if base == "vendor" || base == ".git" || base == "node_modules" || base == "testdata" { + return filepath.SkipDir + } + return nil + } + ext := filepath.Ext(path) + base := filepath.Base(path) + if !checkExt[ext] && !checkBasename[base] { + return nil + } + rel, _ := filepath.Rel(moduleRoot, path) + if allowlistedFiles[rel] { + return nil + } + // Skip the lint test itself — it legitimately names the forbidden + // strings as match patterns. + if strings.HasSuffix(rel, "import_path_lint_test.go") { + return nil + } + data, err := os.ReadFile(path) + if err != nil { + return err + } + text := string(data) + for _, bad := range forbiddenSubstrings { + if strings.Contains(text, bad) { + // Find the line number for a useful error message. + for lineNo, line := range strings.Split(text, "\n") { + if strings.Contains(line, bad) { + t.Errorf("%s:%d — forbidden substring %q (use go.moleculesai.app//... per molecule-ai/internal#71)", rel, lineNo+1, bad) + violations++ + break + } + } + } + } + return nil + }) + if walkErr != nil { + t.Fatalf("walk: %v", walkErr) + } + if violations > 0 { + t.Logf("Total violations: %d. Add to allowlistedFiles ONLY with a documented justification.", violations) + } +} + +// findModuleRoot walks up from the test's CWD to find go.mod. The Go +// test harness sets CWD to the package directory; the module root may +// be one or more parents up. +func findModuleRoot() (string, error) { + cwd, err := os.Getwd() + if err != nil { + return "", err + } + dir := cwd + for { + if _, err := os.Stat(filepath.Join(dir, "go.mod")); err == nil { + return dir, nil + } + parent := filepath.Dir(dir) + if parent == dir { + return "", os.ErrNotExist + } + dir = parent + } +} diff --git a/known-issues.md b/known-issues.md index 1467da2..71d4e69 100644 --- a/known-issues.md +++ b/known-issues.md @@ -93,7 +93,7 @@ entries may cause CI divergence or checksum mismatches. ### Impact `go mod verify` in CI may fail if `go.sum` has extra entries not in the - lock file. Additionally, if the module path (`github.com/Molecule-AI/molecule-cli`) + lock file. Additionally, if the module path (`go.moleculesai.app/cli`) is referenced via `replace` directives from other repos, those references may persist stale entries. -- 2.45.2 From 15d8cec45f696024ebed3c6be657b448e370b7ba Mon Sep 17 00:00:00 2001 From: claude-ceo-assistant Date: Thu, 7 May 2026 22:57:16 +0000 Subject: [PATCH 2/2] =?UTF-8?q?lint:=20extend=20gate=20to=20.json=20files?= =?UTF-8?q?=20(internal#71=20=E2=80=94=20package=20metadata=20drift)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- internal/lint/import_path_lint_test.go | 1 + 1 file changed, 1 insertion(+) diff --git a/internal/lint/import_path_lint_test.go b/internal/lint/import_path_lint_test.go index 76cde1c..8c8b6ba 100644 --- a/internal/lint/import_path_lint_test.go +++ b/internal/lint/import_path_lint_test.go @@ -63,6 +63,7 @@ func TestNoLegacyGitHubImportPaths(t *testing.T) { ".yaml": true, ".toml": true, ".md": true, + ".json": true, // package.json / tsconfig.json — catches ref drift in package metadata } checkBasename := map[string]bool{ "Dockerfile": true, -- 2.45.2