Migrates the two Go modules under molecule-core off the dead
github.com/Molecule-AI/molecule-monorepo/... identity onto the vanity
host go.moleculesai.app. Also fixes the historical naming
inconsistency where the Gitea repo is molecule-core but the Go module
path said molecule-monorepo.
Module changes:
- workspace-server/go.mod:
github.com/Molecule-AI/molecule-monorepo/platform
-> go.moleculesai.app/core/platform
- tests/harness/cp-stub/go.mod:
github.com/Molecule-AI/molecule-monorepo/tests/harness/cp-stub
-> go.moleculesai.app/core/tests/harness/cp-stub
Surfaces touched
- 174 *.go files (374 import lines) — every import under
workspace-server/ + tests/harness/cp-stub/
- 2 Dockerfiles (workspace-server/Dockerfile + Dockerfile.tenant) —
-ldflags strings updated in lockstep with the module rename so
buildinfo.GitSHA injection still resolves correctly
- README + docs + scripts + comment URLs to git.moleculesai.app form
- NEW workspace-server/internal/lint/import_path_lint_test.go —
structural lint gate rejecting future github.com/Molecule-AI/ or
Molecule-AI/molecule-monorepo references. Identical template to the
other migration PRs (plugin-gh-identity#3, molecule-cli#2,
molecule-controlplane#32).
Cross-repo dep allowlist (documented in lint gate)
workspace-server requires molecule-ai-plugin-gh-identity, whose own
vanity migration is PR molecule-ai-plugin-gh-identity#3. Until that PR
merges + a tag is cut at go.moleculesai.app/plugin/gh-identity, the
two locations referencing the legacy github.com path
(workspace-server/go.mod require, cmd/server/main.go import) remain
allowlisted. Follow-up PR drops the allowlist + updates both refs in
one shot once gh-identity is fully migrated.
Test plan
- go build ./... clean for both modules
- go test ./... green except two pre-existing failures
(TestStartSweeper_RecordsMetricsOnSuccess flaky-on-suite,
TestLocalResolver_BubblesUpCopyFailure relies on read-only fs perms
but runs as root on operator host) — both reproduce identically on
baseline main pre-migration; NOT regressions of this PR
- Mutation-tested: lint gate fails on canaries in .go + .md;
allowlist correctly suppresses cross-repo dep references in go.mod
while still flagging unrelated additions
Open dependency
- go.moleculesai.app responder must be deployed before fresh-clone
external builds resolve the vanity path. Existing CI / Docker builds
ride pinned go.sum + self-referential module path + responder is
not on critical path for those.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
4.6 KiB
Memory Plugin Contract — Changelog
Every breaking or operationally-relevant change to the v1 plugin contract or the workspace-server-side wiring lands here. Plugin authors should subscribe to PRs touching this file.
[Unreleased] — fixup wave 1 (post-RFC-#2728 self-review)
A self-review of the initial 11-PR rollout (PRs #2729-#2742) flagged two correctness bugs and three operational hazards. This wave fixes all of them. Order matches operator-impact severity.
Critical: backfill idempotency via MemoryWrite.id (#2744)
The bug. The backfill CLI claimed idempotent on re-run, but
gen_random_uuid() in the plugin's INSERT meant every retry created
a fresh row. Operators retrying a failed -apply would silently
double their memory count.
The fix. Optional id field on MemoryWrite. When supplied,
plugins MUST upsert. The backfill now forwards agent_memories.id
to MemoryWrite.id, so retries update in place.
Plugin author action. If your plugin uses
INSERT INTO ... DEFAULT gen_random_uuid(), switch to
INSERT ... ON CONFLICT (id) DO UPDATE when id is set. The wire
contract is forward-compatible — plugins that ignore the field still
work for production agent commits (which leave id empty), but they
will silently corrupt backfill retries.
Critical: memory-backfill -verify mode (#2747)
The miss. The original PR-7 task spec called for a parity-check mode but it never landed. Operators had no way to confirm a migration succeeded short of "no errors logged."
The fix. New -verify flag samples N workspaces, queries
agent_memories direct, runs an equivalent plugin search via the
namespace resolver, multiset-compares contents. Reports mismatches
to stdout and exits non-zero so CI can gate the cutover.
memory-backfill -verify # default sample 50
memory-backfill -verify -verify-sample=200 # bigger
memory-backfill -verify -workspace=<uuid> # one workspace
Important: expires_at validation (#2746)
The bug. commit_memory_v2 silently dropped malformed
expires_at strings. Agent passes expires_at: "tomorrow", gets a
200, memory has no TTL — agent thinks it set a TTL, didn't.
The fix. Returns
fmt.Errorf("invalid expires_at: must be RFC3339") on parse
failure. Plugin is not called in this case.
Plugin author action. None — this is a workspace-server-side
fix. But: if your plugin advertises the ttl capability, make sure
you actually evict expired rows on read (not just on a janitor cron
that runs once a day). The harness in testing-your-plugin.md has
a TTL-eviction test you should run.
Important: audit log JSON via json.Marshal (#2746)
The bug. auditOrgWrite built activity_logs.metadata via
fmt.Sprintf with %q. For ASCII (today's UUID + hex digest) this
coincidentally produces valid JSON; for unicode or control bytes it
silently produces non-JSON.
The fix. Replaced with json.Marshal(map[string]string{...}).
Same wire shape today, won't regress when metadata grows.
Plugin author action. None — workspace-server-internal.
Operator action: staging verification (#292)
Status. Tracked as task #292. PR-merged ≠ verified. Operator must:
- Provision a staging tenant, set
MEMORY_PLUGIN_URL - Run real
commit_memory_v2from a workspace memory-backfill -dry-runagainst staging datamemory-backfill -apply, then-verify- Set
MEMORY_V2_CUTOVER=true, verify admin export still works - Run a legacy
commit_memoryfrom a workspace, verify it lands in plugin storage via the PR-6 shim
Other follow-ups still open
- #289: admin export O(workspaces) → O(namespaces) — N+1 pattern
in
exportViaPlugin(1000-workspace tenants run 1000× resolver CTEs + 1000× plugin searches today). - #291: workspace deletion must call
DELETE /v1/namespaces/{name}— orphans accumulate today. - #293: real-subprocess boot E2E — current PR-11 is integration (httptest + sqlmock), not E2E.
These are tracked but deferred; they're operationally annoying, not incident-shaped.
[v1.0.0] — initial release (RFC #2728, PRs #2729-#2742)
Initial plugin contract + 11-PR rollout. See issue #2728 for the full RFC.
Endpoints: /v1/health, /v1/namespaces/{name} (PUT/PATCH/DELETE),
/v1/namespaces/{name}/memories (POST), /v1/search (POST),
/v1/memories/{id} (DELETE).
Capabilities: embedding, fts, ttl, pin, propagation.
Operator runbook: see README.md § Replacing the built-in plugin.