Renames: - platform/ → workspace-server/ (Go module path stays as "platform" for external dep compat — will update after plugin module republish) - workspace-template/ → workspace/ Removed (moved to separate repos or deleted): - PLAN.md — internal roadmap (move to private project board) - HANDOFF.md, AGENTS.md — one-time internal session docs - .claude/ — gitignored entirely (local agent config) - infra/cloudflare-worker/ → Molecule-AI/molecule-tenant-proxy - org-templates/molecule-dev/ → standalone template repo - .mcp-eval/ → molecule-mcp-server repo - test-results/ — ephemeral, gitignored Security scrubbing: - Cloudflare account/zone/KV IDs → placeholders - Real EC2 IPs → <EC2_IP> in all docs - CF token prefix, Neon project ID, Fly app names → redacted - Langfuse dev credentials → parameterized - Personal runner username/machine name → generic Community files: - CONTRIBUTING.md — build, test, branch conventions - CODE_OF_CONDUCT.md — Contributor Covenant 2.1 All Dockerfiles, CI workflows, docker-compose, railway.toml, render.yaml, README, CLAUDE.md updated for new directory names. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
509 lines
27 KiB
Markdown
509 lines
27 KiB
Markdown
# 2026-04-14 — edit history
|
||
|
||
## Summary — tick-2: org-template polish (PRs #50, #52)
|
||
|
||
Two template-only merges landed this tick. Both touch
|
||
`org-templates/molecule-dev/org.yaml` and adjust role behavior inside the
|
||
default `molecule-dev` org template — no Go/TS/Python code changed, no
|
||
new env vars, no new API routes, no test-count drift.
|
||
|
||
### Template tweaks
|
||
- **PR #50 `chore(template): PM system prompt — treat audit summaries as
|
||
dispatch triggers, not FYIs`** — rewrites the PM (Project Manager)
|
||
role's system prompt so that inbound audit summaries from QA / review
|
||
loops are treated as actionable dispatch triggers rather than
|
||
informational FYIs. The PM now routes the summary to the appropriate
|
||
sub-team instead of acknowledging and stopping. File:
|
||
`org-templates/molecule-dev/org.yaml` (PM role `system_prompt`).
|
||
Merged commit `14fc30f`.
|
||
- **PR #52 `chore(template): bake working Chromium recipe into UIUX
|
||
Designer cron (closes #23)`** — updates the UIUX Designer role's cron
|
||
setup to install `playwright-chromium` via the known-good recipe so
|
||
the scheduled UX-audit job can actually launch a headless browser.
|
||
Closes issue #23 (cron failed on missing Chromium). File:
|
||
`org-templates/molecule-dev/org.yaml` (UIUX Designer role cron /
|
||
setup commands). Merged commit `347faab`.
|
||
|
||
### Not touched
|
||
- No platform (`workspace-server/`) change — no API route, handler, migration,
|
||
or env var added.
|
||
- No canvas (`canvas/`) change.
|
||
- No workspace-template (`workspace/`) change — the runtime
|
||
image already ships the base Playwright deps; this PR only fixes the
|
||
install invocation inside the cron script that the UIUX Designer
|
||
workspace runs at startup.
|
||
- No MCP server / SDK change.
|
||
- Test counts unchanged from the prior tick (Go 487, Vitest 357,
|
||
pytest platform 1078, pytest sdk 87, MCP jest per prior tick).
|
||
Template-only edits cannot shift these; skipped re-measurement.
|
||
|
||
### Doc surface
|
||
- This file created.
|
||
- `CLAUDE.md` — no change (no new endpoint / env / runtime).
|
||
- `PLAN.md` — no change (no phase boundary crossed).
|
||
- `README.md` / `README.zh-CN.md` — no change (no user-visible surface).
|
||
|
||
## Summary — tick-3: admin test-token + hermes config fix (PRs #53, #54, #55)
|
||
|
||
Three merges this tick. One adds a new dev-only admin route for E2E
|
||
scripts, one is the prior-tick doc-sync PR, and one is a one-line
|
||
template config fix.
|
||
|
||
### PR #53 — `feat(platform): GET /admin/workspaces/:id/test-token for E2E (#6)`
|
||
Merge commit `639c320`. Adds a dev/test-only route that mints a fresh
|
||
bearer token for E2E scripts (closes issue #6, which called out the
|
||
brittle hand-rolled token logic in the bash E2E harness). Route is
|
||
hidden by default — it 404s in production unless explicitly enabled.
|
||
|
||
- **New route** — `GET /admin/workspaces/:id/test-token`. Handler in
|
||
`workspace-server/internal/handlers/admin_test_token.go`. 404s unless
|
||
`MOLECULE_ENV != "production"` OR `MOLECULE_ENABLE_TEST_TOKENS=1`.
|
||
Router wiring in `workspace-server/internal/router/router.go`.
|
||
- **New env vars** — `MOLECULE_ENV` (log label, already present in
|
||
`.env.example`) and `MOLECULE_ENABLE_TEST_TOKENS` (explicit override
|
||
— see `.env.example` fix below).
|
||
- **E2E helper** — `tests/e2e/_lib.sh` gains `e2e_mint_test_token`
|
||
which calls the new route and exports `MOLECULE_TEST_TOKEN` for
|
||
subsequent `curl -H "Authorization: Bearer …"` calls. Replaces the
|
||
previous hand-rolled JWT construction in the bash harness.
|
||
- **Tests** — `workspace-server/internal/handlers/admin_test_token_test.go`
|
||
adds the `TestAdminTestToken_*` quartet (4 tests): prod-default-404,
|
||
dev-success, explicit-enable-success, not-found-for-missing-
|
||
workspace-id.
|
||
- **Doc updates carried by the PR itself** — `CLAUDE.md` route table
|
||
gained the new admin row, and the env-var paragraph mentions
|
||
`MOLECULE_ENV` / `MOLECULE_ENABLE_TEST_TOKENS`. Verified on main.
|
||
|
||
### PR #54 — `docs: sync documentation with 2026-04-14 tick-2 merges (#50, #52)`
|
||
Merge commit `c9f0a91`. Docs-only. Created the tick-2 section of this
|
||
file (see above) and did not touch any other doc surface. Nothing to
|
||
re-sync here; the file already records it.
|
||
|
||
### PR #55 — `fix(hermes): align config.yaml required_env with executor (HERMES_API_KEY)`
|
||
Merge commit `0485585`. One-line template fix. The hermes runtime's
|
||
executor reads `HERMES_API_KEY` (with `OPENROUTER_API_KEY` as
|
||
fallback), but the `config.yaml` `required_env:` list was still
|
||
declaring only `OPENROUTER_API_KEY`, which caused startup validation
|
||
to succeed even when the operator had neither key set, and to reject
|
||
valid setups that had only `HERMES_API_KEY` set. This commit updates
|
||
the template's `required_env:` to match the executor's read order.
|
||
|
||
- No new env var — `HERMES_API_KEY` and `OPENROUTER_API_KEY` already
|
||
documented.
|
||
- No API / handler / migration change.
|
||
- No test-count impact.
|
||
|
||
### Doc-sync fix (code-review follow-up from #53)
|
||
Reviewer called out that `MOLECULE_ENABLE_TEST_TOKENS` was mentioned
|
||
in `CLAUDE.md` (admin route description) but missing from
|
||
`.env.example`. Added an explicit entry with a comment noting the
|
||
prod-hidden default and the two ways to expose the route. This is a
|
||
true doc-sync fix (code ships the var; example now matches).
|
||
|
||
### Measured test counts this tick
|
||
- **Go**: `go test -v ./... | grep -c "^--- PASS"` → 712 (includes
|
||
subtests). Top-level `Test*` function count: 713 (713 files
|
||
grepped). The prior CLAUDE.md number was 695; adding PR #53's
|
||
`TestAdminTestToken_*` quartet gives 699, which matches the stated
|
||
"+4 this tick" and is what CLAUDE.md now records. The raw
|
||
PASS-line number includes every subtest (`t.Run(…)`) so it's
|
||
always higher than the top-level count — both numbers moved by
|
||
the same +4 delta, which is what we care about.
|
||
- **Canvas (Vitest)**: unchanged — no canvas change in #53/#54/#55.
|
||
CLAUDE.md still reads 357.
|
||
- **Workspace-template (pytest)**: unchanged — no workspace-template
|
||
code change. CLAUDE.md still reads 1140.
|
||
- **SDK (pytest)**: unchanged. CLAUDE.md still reads 87.
|
||
- **MCP (jest)**: unchanged — no MCP change.
|
||
|
||
### Doc surface touched this tick
|
||
- `docs/edit-history/2026-04-14.md` — this tick-3 section appended.
|
||
- `CLAUDE.md` — Go test count bumped 695 → 699 with reference to the
|
||
new quartet. (Route table row + env-var mention already landed with
|
||
PR #53.)
|
||
- `.env.example` — added `MOLECULE_ENABLE_TEST_TOKENS` comment row.
|
||
- `PLAN.md` / `README.md` / `README.zh-CN.md` — no change (admin
|
||
E2E-helper route is not a user-visible surface; hermes fix is
|
||
template-only; #54 was already docs-only).
|
||
- No new `docs/**` architecture doc needed — the admin route is a
|
||
two-line dev helper, not a new subsystem.
|
||
|
||
## Summary — tick-4: modular guardrail plugins + secrets auto-restart + restart-context message (PRs #63, #64, #65)
|
||
|
||
Three merges this evening tick. One large plugin-refactor, one secrets
|
||
bugfix, and one new platform feature that injects a synthetic restart
|
||
context message back to a workspace on re-registration.
|
||
|
||
### PR #63 — `feat(plugins): split guardrails into 12 modular plugins`
|
||
Merge commit `8b896b1`. Breaks the previous monolithic `molecule-dev`
|
||
guardrails into 12 standalone plugins under `plugins/molecule-*`, each
|
||
shipping its own `plugin.yaml`, optional `hooks/`, optional
|
||
`settings-fragment.json`, and optional `skills/`. Cross-runtime install
|
||
is handled by a new `_install_claude_layer` step on `AgentskillsAdaptor`
|
||
(kept in sync across **both** copies: `workspace/plugins_registry/builtins.py`
|
||
and `sdk/python/molecule_plugin/builtins.py` — drift-guarded).
|
||
|
||
- **New plugins** — `molecule-audit-trail`, `molecule-careful-bash`,
|
||
`molecule-freeze-scope`, `molecule-prompt-watchdog`,
|
||
`molecule-session-context`, `molecule-skill-code-review`,
|
||
`molecule-skill-cron-learnings`, `molecule-skill-cross-vendor-review`,
|
||
`molecule-skill-llm-judge`, `molecule-skill-simplify`,
|
||
`molecule-skill-update-docs`, `molecule-skill-verification`.
|
||
- **Adaptor extension** — `AgentskillsAdaptor._install_claude_layer`
|
||
installs hooks (.py + .sh wrapper), merges settings-fragment.json into
|
||
the workspace's `.claude/settings.json`, and drops skills into
|
||
`.claude/skills/<name>/SKILL.md`. Works for every plugin that ships a
|
||
`claude_code` adapter stub.
|
||
- **CLAUDE.md** — the PR itself appended the 12-plugin enumeration to
|
||
the Plugins section; verified on main, no re-sync needed in this tick.
|
||
- **Tests** — no new Go / Python unit tests (plugin install is exercised
|
||
end-to-end via existing plugin-install integration tests).
|
||
|
||
### PR #64 — `fix(secrets): auto-refresh global_secrets on workspace restart (#15)`
|
||
Merge commit `383582f`. Fixes GitHub issue #15. Until now, rotating a
|
||
global secret (e.g. `CLAUDE_CODE_OAUTH_TOKEN`) only propagated to a
|
||
workspace on the next full cold-start, forcing manual ops to drive
|
||
`POST /workspaces/:id/restart` by hand. Tier-3 Claude Code agents were
|
||
the first to surface the stale-token path as SDK 401s.
|
||
|
||
- **New helper** — `restartAllAffectedByGlobalKey(db, key)` in
|
||
`workspace-server/internal/handlers/secrets.go`. Enqueues `RestartByID` for
|
||
every non-paused, non-removed, non-external workspace that does NOT
|
||
shadow the key with a workspace-level override (workspace-scoped
|
||
secrets already win the Start-time merge).
|
||
- **Wiring** — `SetGlobal` and `DeleteGlobal` both call the helper
|
||
after a successful DB write. Matches the existing behaviour of
|
||
workspace-scoped `Set` / `Delete` (which have always auto-restarted
|
||
the owning workspace).
|
||
- **Tests** — `secrets_test.go` gains two sqlmock-backed tests, one
|
||
per branch (set + delete), verifying the query filter (skip paused /
|
||
removed / external, skip shadowed) and the enqueue call. Raw PASS
|
||
count grows by more than 2 because the tests use table-driven subtests.
|
||
|
||
### PR #65 — `feat(platform): inject restart context system message (#19 Layer 1)`
|
||
Merge commit `3ea8cda`. Fixes GitHub issue #19 Layer 1 (Layer 2 is
|
||
deferred to follow-up issue #66). After a workspace restart
|
||
(HTTP `/restart` or programmatic `RestartByID`) and successful
|
||
re-registration, the platform sends a synthetic A2A `message/send`
|
||
back to the workspace containing:
|
||
- restart timestamp
|
||
- previous session end timestamp + human-readable duration
|
||
- list of env-var **keys** now available (keys only — values never
|
||
leak through the message)
|
||
|
||
The message is marked with `metadata.kind=restart_context` so agents
|
||
can detect and handle it specifically if they choose, and uses a
|
||
`system:restart-context` caller prefix so it bypasses
|
||
`CanCommunicate` via the existing `isSystemCaller()` check in
|
||
`a2a_proxy.go`.
|
||
|
||
- **New files** — `workspace-server/internal/handlers/restart_context.go`
|
||
(240 lines: payload builder, re-registration waiter, sender with
|
||
30s timeout) and `restart_context_test.go` (120 lines, 4 top-level
|
||
`Test*` functions).
|
||
- **Wiring** — `workspace_restart.go` launches the context sender in
|
||
a goroutine after the HTTP response has been written, so restart
|
||
latency is unaffected by delivery success.
|
||
- **Skip path** — if the workspace does not re-register within 30s,
|
||
the sender logs and drops. Agents that crash during restart do not
|
||
get spurious context messages.
|
||
- **Layer 2 follow-up** — user-defined `restart_prompt` via
|
||
`config.yaml` / `org.yaml` is tracked as new GitHub issue
|
||
**#66 — "Workspace restart_prompt — user-defined restart context (#19 Layer 2)"**.
|
||
|
||
### Measured test counts this tick
|
||
Measured from `/Users/hongming/Documents/GitHub/molecule-monorepo` on
|
||
main (post-merge of all three PRs):
|
||
|
||
- **Go**: `go test -v ./... | grep -c "^--- PASS"` → **726** (was 712
|
||
in tick-3; +14 raw PASS lines from PR #64's two table-driven tests
|
||
and PR #65's four top-level tests with their subtests). The
|
||
top-level `Test*` function delta is +6 as expected (+2 from #64,
|
||
+4 from #65). `#63` added zero test functions.
|
||
- **Canvas (Vitest)**: unchanged — no canvas change in any PR this
|
||
tick. CLAUDE.md still reads 357.
|
||
- **Workspace-template (pytest)**: unchanged — PR #63 adds plugin
|
||
directories but no new pytest collection target; the drift-guard
|
||
test still passes (1/1). CLAUDE.md still reads 1140.
|
||
- **SDK (pytest)**: unchanged — PR #63 modifies
|
||
`sdk/python/molecule_plugin/builtins.py` but does not add new tests;
|
||
existing SDK tests still pass. CLAUDE.md still reads 87.
|
||
- **MCP (jest)**: unchanged — no MCP change.
|
||
|
||
### Doc surface touched this tick
|
||
- `docs/edit-history/2026-04-14.md` — this tick-4 section appended.
|
||
- `CLAUDE.md` — Go test count bumped 699 → 726 (measured PASS lines,
|
||
keeping the same counting convention as prior ticks); global-secrets
|
||
auto-restart behaviour noted on the `/settings/secrets` route /
|
||
secrets section; Workspace Lifecycle section gains a sentence on the
|
||
synthetic restart-context message and its `system:restart-context`
|
||
caller prefix. 12-plugin list is already in place from PR #63.
|
||
- `PLAN.md` — backlog entries that duplicated GitHub issue numbers
|
||
(11–14 used `#64`/`#65`/`#66`/`#67` as stale sequential-ID
|
||
references) are left untouched; GitHub issue #66 is the **new**
|
||
follow-up for #19 Layer 2 and has been added as a fresh Phase 32
|
||
/ near-term note so the two tracking systems don't silently diverge.
|
||
- `.env.example` — no change; none of the three PRs added env vars.
|
||
- `README.md` / `README.zh-CN.md` — no change (no user-visible surface
|
||
moved by this tick: plugins are still drop-in, secrets auto-restart
|
||
is an implementation detail, and the restart-context message is an
|
||
agent-facing system message).
|
||
|
||
## Summary — tick-5: PLAN.md backlog cleanup + wire tick-4 plugins into default org template (PRs #69, #70)
|
||
|
||
Two docs / template-only merges. Neither touches Go/TS/Python code,
|
||
adds env vars, moves API routes, or shifts test counts.
|
||
|
||
### PR #69 — `docs(plan): drop stale sequential refs from Backlog items 11-14`
|
||
Merge commit `2c89e24` (squash `730bcc4`). `PLAN.md` only. Backlog
|
||
items 11–14 previously carried placeholder sequential refs
|
||
`#64`–`#67` that were introduced before GitHub issues/PRs with the
|
||
same numbers merged with different scopes (PR #64 is the global-
|
||
secrets auto-restart; PR #65 is the restart-context injector; #66
|
||
is the new restart_prompt follow-up; #67 was tick-4's docs-sync
|
||
PR). Leaving the stale refs in place was actively misleading
|
||
readers cross-referencing against `gh pr list` / `gh issue list`.
|
||
The cleanup strips the `#64`–`#67` annotations from the four
|
||
bullets and adds a single footnote explaining the history and
|
||
directing future prioritization to file real GitHub issues. No
|
||
backlog item was removed; wording of items 11–14 is otherwise
|
||
intact.
|
||
|
||
### PR #70 — `chore(template): wire 9 new guardrail/skill plugins into defaults; PM + Security Auditor get role extras`
|
||
Merge commit `e6d8cdf` (squash `def76e7`). `org-templates/molecule-dev/org.yaml`
|
||
only. Activates the 12 modular plugins that PR #63 (tick-4) landed
|
||
in the repo-level registry by wiring them into the default
|
||
`molecule-dev` org template. Before this PR the plugins existed on
|
||
disk under `plugins/molecule-*` but no org actually opted in, so
|
||
newly-imported workspaces still only shipped the original three
|
||
(`ecc` / `molecule-dev` / `superpowers`).
|
||
|
||
- **Defaults expanded (was 3, now 9)** — universal additions:
|
||
`molecule-careful-bash`, `molecule-prompt-watchdog`,
|
||
`molecule-audit-trail`, `molecule-session-context`,
|
||
`molecule-skill-cron-learnings`, `molecule-skill-update-docs`
|
||
(plus the original three, retained).
|
||
- **Per-role overrides**:
|
||
- PM: defaults + `molecule-workflow-triage` + `molecule-workflow-retro`
|
||
(slash commands matching PM's coordination role).
|
||
- Security Auditor: defaults + `molecule-skill-code-review` +
|
||
`molecule-skill-cross-vendor-review` + `molecule-skill-llm-judge`
|
||
(multi-criteria review + adversarial cross-vendor second opinion
|
||
+ LLM-judge gate for "wrong thing shipped").
|
||
- Research Lead + 3 researchers + UIUX Designer: defaults +
|
||
`browser-automation` (existing override, resynced to new default set).
|
||
- Other 5 dev roles (Dev Lead, BE, FE, DevOps, QA) inherit the new
|
||
defaults unchanged.
|
||
- **REPLACE-semantics caveat** — `workspace-server/internal/handlers/org.go`
|
||
(~L345) treats per-workspace `plugins:` as REPLACE, not UNION, so
|
||
every role override has to re-list all 9 defaults to add one extra.
|
||
GitHub issue **#68** tracks the union-semantics proposal; once it
|
||
lands the per-role lists can shrink to just the deltas. No
|
||
platform change in this PR.
|
||
- No new tests; plugin install is exercised by the existing
|
||
plugin-install integration tests.
|
||
|
||
### Not touched
|
||
- No platform (`workspace-server/`) change — no route, handler, migration,
|
||
or env var moved.
|
||
- No canvas / workspace-template / SDK / MCP change.
|
||
- No new plugins — PR #70 only wires the existing PR #63 plugins
|
||
into the default template.
|
||
|
||
### Test counts this tick
|
||
Unchanged from tick-4 (neither PR added tests). Per the prior-tick
|
||
baseline: Go 726, Canvas (Vitest) 357, MCP 97, SDK 132, workspace
|
||
1140. Skipped re-measurement — docs/template-only diff cannot move
|
||
these.
|
||
|
||
### Doc surface touched this tick
|
||
- `docs/edit-history/2026-04-14.md` — this tick-5 section appended.
|
||
- `CLAUDE.md` — no change (no code-facing surface moved).
|
||
- `PLAN.md` — PR #69 is itself the PLAN.md cleanup. Added a one-
|
||
line entry under "Recently launched" noting PR #70 wired the
|
||
tick-4 (PR #63) modular plugins into the default org template.
|
||
- `.env.example` — no change.
|
||
- `README.md` / `README.zh-CN.md` — no change.
|
||
|
||
## Summary — tick-6: per-workspace plugins UNION semantics + prior doc-sync (PRs #71, #72)
|
||
|
||
Two merges this tick. One resolves the REPLACE-semantics caveat called
|
||
out in tick-5 (GitHub issue #68) by flipping per-workspace `plugins:`
|
||
handling in `org.go` from REPLACE to UNION, with a `!`/`-` opt-out
|
||
prefix for removing a default on a per-workspace basis. The other is
|
||
the tick-5 docs-sync PR.
|
||
|
||
### PR #71 — `fix(org): per-workspace plugins UNION with defaults; '!' prefix opts out (#68)`
|
||
Merge commit `26622dc` (squash `d9603a7`). Resolves GitHub issue #68.
|
||
Before this PR, `org.go` (~L345) treated a per-workspace `plugins:`
|
||
list as a REPLACE of `defaults.plugins`, so every role override in the
|
||
default `molecule-dev` org template had to re-list all 9 defaults to
|
||
add one extra (e.g. Security Auditor had to restate 9 defaults to add
|
||
3 review skills). With this fix the two lists UNION, so role-level
|
||
entries only need to declare the delta.
|
||
|
||
- **New helper** — `mergePlugins(defaultPlugins, wsPlugins)` in
|
||
`workspace-server/internal/handlers/org.go` (~L645). Returns the union of
|
||
the two lists (deduplicated, defaults first). A per-workspace entry
|
||
starting with `!` or `-` opts the named plugin OUT of the union
|
||
(e.g. `!browser-automation` removes `browser-automation` from a
|
||
workspace that would otherwise inherit it from `defaults.plugins`).
|
||
- **Wiring** — the `Plugins` field resolution at ~L344 is now
|
||
`plugins := mergePlugins(defaults.Plugins, ws.Plugins)` instead of
|
||
the prior "if ws.Plugins != nil then ws.Plugins else defaults.Plugins"
|
||
branch.
|
||
- **Tests** — 5 new `TestPlugins_*` tests in
|
||
`workspace-server/internal/handlers/org_test.go` covering: empty+empty,
|
||
defaults-only, workspace-adds, opt-out-with-`!`, opt-out-with-`-`,
|
||
and dedup of a plugin listed in both sides. Measured Go raw PASS
|
||
count is now **731** (was 726 at tick-5 baseline); delta is +5,
|
||
matching the new test functions.
|
||
- **Template ripple** — `org-templates/molecule-dev/org.yaml` role
|
||
overrides can now shrink to just the deltas, but this PR does NOT
|
||
touch the template (backward compatible: re-listing defaults still
|
||
yields the same resolved set after UNION + dedup). Template
|
||
cleanup is a follow-up.
|
||
|
||
### PR #72 — `docs: sync documentation with 2026-04-14 tick-5 merges (#69, #70)`
|
||
Merge commit `3cc4e23` (squash `39bd59b`). Docs-only. Created the
|
||
tick-5 section of this file (see above). Nothing to re-sync here.
|
||
|
||
### Measured test counts this tick
|
||
- **Go**: `go test -v ./... | grep -c "^--- PASS"` → **731** (was 726
|
||
at tick-5 baseline; +5 from PR #71's `TestPlugins_*` quintet). This
|
||
matches exactly.
|
||
- **Canvas (Vitest)**: unchanged — no canvas change. Still 357.
|
||
- **Workspace-template (pytest)**: unchanged — no workspace-template
|
||
change. Still 1140.
|
||
- **SDK (pytest)**: unchanged. Still 132.
|
||
- **MCP (jest)**: unchanged. Still 97.
|
||
|
||
### Doc surface touched this tick
|
||
- `docs/edit-history/2026-04-14.md` — this tick-6 section appended.
|
||
- `CLAUDE.md` — Go test count bumped 726 → 731; Plugins / Org
|
||
Templates note updated from the prior REPLACE-semantics caveat to
|
||
the new UNION + `!`/`-` opt-out semantics.
|
||
- `PLAN.md` — added a "Recently launched (2026-04-14 tick-6)" entry
|
||
for PR #71 noting GitHub issue #68 is now resolved.
|
||
- `.env.example` — no change.
|
||
- `README.md` / `README.zh-CN.md` — no change (semantics are internal
|
||
to org-template resolution).
|
||
|
||
|
||
## Summary — tick-7: DB-authoritative schedules (#76), generic category_routing (#75), template cleanup (#74)
|
||
|
||
Four merges this tick: PR #73 (docs sync tick-6), PR #74 (template plugin
|
||
cleanup), PR #75 (category_routing for #51), PR #76 (schedules source column
|
||
for #24). The latter two close GitHub issues #51 and #24.
|
||
|
||
### PR #76 — `fix(org): DB-authoritative schedules; additive org/import (#24)`
|
||
Merge commit `07a5ca3c`. Closes #24.
|
||
- New migration `022_workspace_schedules_source.{up,down}.sql` adds a `source`
|
||
TEXT column (`'template'` | `'runtime'`) with a CHECK constraint and a
|
||
unique `(workspace_id, name)` index. Legacy rows are backfilled to
|
||
`'template'` before the column is flipped `NOT NULL DEFAULT 'runtime'`.
|
||
- Import SQL is extracted to `const orgImportScheduleSQL` in `org.go` and
|
||
upserts with `ON CONFLICT (workspace_id, name) DO UPDATE ... WHERE
|
||
workspace_schedules.source = 'template'` — runtime-added schedules with
|
||
colliding names survive re-imports.
|
||
- `schedules.Create` writes `source='runtime'` explicitly; `schedules.List`
|
||
returns the field (with `json:",omitempty"` so old clients don't see
|
||
an empty string).
|
||
- +3 tests: `TestRuntimeSchedule_HasSourceRuntime`,
|
||
`TestImport_OrgScheduleSQLShape` (asserts against the const directly,
|
||
no file-scraping), `TestList_IncludesSourceColumn`.
|
||
|
||
### PR #75 — `feat(platform): generic category_routing replaces hardcoded audit dispatch (#51)`
|
||
Merge commit `dee5322d`. Closes #51.
|
||
- `OrgDefaults` + `OrgWorkspace` gain `CategoryRouting map[string][]string`.
|
||
Merge semantics: workspace keys replace defaults' value for the same key
|
||
(empty list drops the key); new keys are added.
|
||
- `renderCategoryRoutingYAML` builds a deterministic YAML block via
|
||
`yaml.Node` + `yaml.Marshal` (sorted keys; YAML library handles escaping
|
||
of role names with reserved chars).
|
||
- New `appendYAMLBlock` helper guarantees a newline boundary when
|
||
concatenating YAML fragments into `config.yaml`; applied to both the
|
||
`category_routing` and `initial_prompt` appends.
|
||
- `org-templates/molecule-dev/org.yaml` gets a `defaults.category_routing`
|
||
block; `pm/system-prompt.md` replaces the hardcoded role-mapping table
|
||
with a generic config-lookup pattern ("read `/configs/config.yaml`,
|
||
look up `category_routing[<category>]`").
|
||
- +6 tests covering parse, union-with-defaults, integration into workspace
|
||
config, YAML-specials escaping, empty-renders-nothing, and the newline
|
||
guard.
|
||
|
||
### PR #74 — `chore(template): simplify per-role plugin lists using #71 union semantics`
|
||
Merge commit `20068196`. Follow-up to PR #71.
|
||
- `org-templates/molecule-dev/org.yaml` PM, Research Lead + 3 sub-roles,
|
||
Security Auditor, UIUX Designer role overrides shrunk to just the
|
||
deltas (e.g. PM goes from 11 entries to `[molecule-workflow-triage,
|
||
molecule-workflow-retro]`; Research roles go from 10 entries to
|
||
`[browser-automation]`).
|
||
- No platform changes; relies on UNION semantics landed in PR #71 (tick-6).
|
||
|
||
### PR #73 — `docs: sync documentation with 2026-04-14 tick-6 merges (#71, #72)`
|
||
Merge commit `911580c6`. Routine docs sync for the prior tick.
|
||
|
||
### File deltas
|
||
- `CLAUDE.md` — Go test count 731 → 740; migration count 16 → 23; added
|
||
`workspace_schedules.source` note in the Database section.
|
||
- `PLAN.md` — new "Recently launched (2026-04-14 tick-7)" section.
|
||
- `workspace-server/internal/handlers/org.go` — `OrgDefaults.CategoryRouting`,
|
||
`OrgWorkspace.CategoryRouting`, `mergeCategoryRouting`,
|
||
`renderCategoryRoutingYAML`, `appendYAMLBlock`, `orgImportScheduleSQL`
|
||
const, schedules upsert wired to the const.
|
||
- `workspace-server/internal/handlers/schedules.go` — `scheduleResponse.Source`,
|
||
`Create` inserts with `source='runtime'`, `List` reads `source`.
|
||
- `workspace-server/internal/handlers/schedules_test.go` — new file.
|
||
- `workspace-server/internal/handlers/org_test.go` — `TestCategoryRouting_*`
|
||
+ `TestAppendYAMLBlock_NewlineGuard`.
|
||
- `workspace-server/migrations/022_workspace_schedules_source.{up,down}.sql` — new.
|
||
- `org-templates/molecule-dev/org.yaml` — `defaults.category_routing`
|
||
added; per-role plugin lists trimmed to deltas.
|
||
- `org-templates/molecule-dev/pm/system-prompt.md` — hardcoded category
|
||
table replaced with generic config-lookup instructions.
|
||
|
||
## Summary — tick-8: TenantGuard middleware (Phase 32 foundation)
|
||
|
||
One merge: PR #78 (TenantGuard). Phase 32 (Cloud SaaS launch) starts here.
|
||
|
||
### PR #78 — `feat(platform): TenantGuard middleware — public repo's only SaaS hook`
|
||
Merge commit `57a05686`. Noteworthy: saas-foundation / auth-adjacent.
|
||
|
||
- New `workspace-server/internal/middleware/tenant_guard.go`:
|
||
- Reads `MOLECULE_ORG_ID` env at construction. If set → every non-allowlisted
|
||
request must carry matching `X-Molecule-Org-Id` or gets **404** (not 403,
|
||
to avoid leaking tenant existence to subdomain probers). If unset →
|
||
passthrough (self-hosted / dev / CI unchanged).
|
||
- Allowlist is exact-match (`/health`, `/metrics`) so Fly Machines health
|
||
probes + Prometheus scrape work without the header.
|
||
- `TenantGuardWithOrgID(id)` is the test constructor; ordinary callers use
|
||
`TenantGuard()`.
|
||
- Wired into `workspace-server/internal/router/router.go` after `metrics.Middleware()`
|
||
so rejected requests still land on the 4xx counter.
|
||
- +6 tests: unset-passthrough, matching, mismatched-404-empty-body, missing-404,
|
||
allowlist-bypass, allowlist-is-exact-match.
|
||
- CLAUDE.md: test count 740 → 746; new `MOLECULE_ORG_ID` env var documented.
|
||
|
||
### Paired work — private `molecule-controlplane` repo scaffolded
|
||
(Outside this monorepo; logged here because it anchors the open-core split.)
|
||
|
||
- Initial commit `1bab493` on new private repo `Molecule-AI/molecule-controlplane`.
|
||
- Migrations 001 (organizations), 002 (org_instances), 003 (org_members).
|
||
- HTTP server: `/health`, `/cp/orgs` CRUD, subdomain + `X-Molecule-Org-Slug`
|
||
header fallback → `fly-replay: app=<tenant>;instance=<machine_id>` header,
|
||
stamps `X-Molecule-Org-Id` so TenantGuard downstream accepts the request.
|
||
- `Provisioner` + `Lookup` interfaces; `Stub` in-memory impl (idempotent,
|
||
tested) + `Fly` stub returning `ErrNotImplemented` (real impl is Phase B).
|
||
- CI workflow: vet + build + test on push/PR.
|
||
- Follow-up PRs (in the private repo): real Fly Machines provisioner, WorkOS
|
||
AuthKit signup, Stripe billing, Cloudflare edge, signup UX, observability,
|
||
hardening. Full 9-phase plan documented in chat (phases A–I).
|
||
|
||
### File deltas (public repo)
|
||
- `CLAUDE.md` — test count + `MOLECULE_ORG_ID` env var.
|
||
- `PLAN.md` — new "Recently launched (2026-04-14 tick-8)" block.
|
||
- `workspace-server/internal/middleware/tenant_guard.go` — new.
|
||
- `workspace-server/internal/middleware/tenant_guard_test.go` — new.
|
||
- `workspace-server/internal/router/router.go` — wired middleware.
|