- edit-history/2026-04-14.md: append tick-4 section covering the 12 modular guardrail plugins (#63), global-secrets auto-restart fan-out (#64, fixes issue #15), and synthetic restart-context A2A message (#65, fixes issue #19 Layer 1; Layer 2 deferred to issue #66). - CLAUDE.md: bump Go test count 699 -> 726 (measured); note global secrets auto-restart on SetGlobal/DeleteGlobal in the route table; add Workspace Lifecycle paragraph for the restart-context message and its system:restart-context caller prefix. - PLAN.md: bump Go test count in the coverage table; record issues #15 and #19 Layer 1 as launched; add new Backlog entry for the Layer 2 follow-up (issue #66). Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
383582fbbf
commit
2fa6f7c6cd
@ -214,7 +214,7 @@ OPENAI_API_KEY=... bash scripts/test-team-e2e.sh # E2E: Multi-template
|
||||
|
||||
### Unit Tests
|
||||
```bash
|
||||
cd platform && go test -race ./... # 699 Go tests (handlers, registry, provisioner, CLI, delegation, org, channels, wsauth — sqlmock + miniredis; +4 on 2026-04-14 tick-3 for TestAdminTestToken_* covering the dev-only /admin/workspaces/:id/test-token route)
|
||||
cd platform && go test -race ./... # 726 Go tests (handlers, registry, provisioner, CLI, delegation, org, channels, wsauth — sqlmock + miniredis; +2 on 2026-04-14 tick-4 for TestSetGlobal_* / TestDeleteGlobal_* auto-restart branches (#64); +4 on 2026-04-14 tick-4 for TestRestartContext_* covering the synthetic restart-context A2A message (#65); raw PASS-line count is higher due to table-driven subtests)
|
||||
cd canvas && npm test # 357 Vitest tests (store, components, hydration, buildTree, secrets API, org template import, ConfirmDialog singleButton + 7 native-dialog replacements)
|
||||
cd workspace-template && python -m pytest -v # 1140 pytest tests (adds platform_auth token store for Phase 30.1, memory_write activity logging)
|
||||
cd sdk/python && python -m pytest -v # 132 SDK tests (agentskills.io spec validator, CLI, AgentskillsAdaptor round-trip, workspace/org/channel validators, RemoteAgentClient Phase 30 flows)
|
||||
@ -334,6 +334,8 @@ Agents can auto-execute a prompt on startup before any user interaction. Configu
|
||||
### Workspace Lifecycle
|
||||
`provisioning` → `online` (on register) → `degraded` (error_rate > 0.5) → `online` (recovered) → `offline` (Redis TTL expired OR health sweep detects dead container) → auto-restart → `provisioning` → ... → `removed` (deleted). Any state → `paused` (user pauses) → `provisioning` (user resumes). Paused workspaces skip health sweep, liveness monitor, and auto-restart.
|
||||
|
||||
**Restart context message (issue #19 Layer 1):** After any restart (HTTP `/restart` or programmatic `RestartByID`) and successful re-registration, the platform sends a synthetic A2A `message/send` to the workspace with `metadata.kind=restart_context` — body contains restart timestamp, previous session end + duration, and env-var keys (keys only, never values) now available. Sender uses the `system:restart-context` caller prefix so it bypasses `CanCommunicate` via `isSystemCaller()`. If the workspace does not re-register within 30s the message is dropped (logged). Handler: `platform/internal/handlers/restart_context.go`. Layer 2 (user-defined `restart_prompt` from `config.yaml` / `org.yaml`) is tracked as GitHub issue #66.
|
||||
|
||||
## Platform API Routes
|
||||
|
||||
| Method | Path | Handler |
|
||||
@ -350,8 +352,8 @@ Agents can auto-execute a prompt on startup before any user interaction. Configu
|
||||
| DELETE | /workspaces/:id/secrets/:key | secrets.go (DELETE auto-restarts workspace) |
|
||||
| GET | /workspaces/:id/model | secrets.go |
|
||||
| GET | /settings/secrets | secrets.go — list global secrets (keys only, values masked) |
|
||||
| PUT/POST | /settings/secrets | secrets.go — set a global secret {key, value} |
|
||||
| DELETE | /settings/secrets/:key | secrets.go — delete a global secret |
|
||||
| PUT/POST | /settings/secrets | secrets.go — set a global secret {key, value}; auto-restarts every non-paused/non-removed/non-external workspace that does not shadow the key with a workspace-level override (issue #15 / PR #64) |
|
||||
| DELETE | /settings/secrets/:key | secrets.go — delete a global secret; same auto-restart fan-out as SetGlobal |
|
||||
| GET | /admin/workspaces/:id/test-token | admin_test_token.go — mint a fresh bearer token for E2E scripts; 404 unless `MOLECULE_ENV != production` or `MOLECULE_ENABLE_TEST_TOKENS=1` |
|
||||
| GET/POST/DELETE | /admin/secrets[/:key] | secrets.go — legacy aliases for /settings/secrets |
|
||||
| WS | /workspaces/:id/terminal | terminal.go |
|
||||
|
||||
9
PLAN.md
9
PLAN.md
@ -231,6 +231,11 @@ point for "what else is out there."
|
||||
12. **YAML-configurable per-agent repo access** — #65. New `workspace_access: none|read_only|read_write` field in `org.yaml` + `:ro` bind-mount for research agents; eliminates the "PM couriers documents to reports" workaround.
|
||||
13. **SDK executor swallows subprocess stderr** — #66. `workspace-template/claude_sdk_executor.py` surfaces only "Command failed with exit code 1 / Check stderr output for details" when the `claude` CLI crashes, making every failure opaque. Capture stderr, log at ERROR, include first ~1 KB in the A2A error response. **High priority** — blocked real debugging during PLAN.md coordination on 2026-04-12.
|
||||
14. **Agent MCP client defaults to `localhost:8080`** — #67. Inside a workspace container, `localhost` is the container itself, not the platform — so `mcp__molecule__*` tools fail with "platform unreachable." Inject `MOLECULE_URL=${PLATFORM_URL}` into every container at provision time and change the MCP client default to `http://host.docker.internal:8080`. **High priority** — blocks agents from calling platform tools (e.g. PM couldn't restart its own reports).
|
||||
15. **Workspace `restart_prompt` — user-defined restart context (#19 Layer 2)** — GitHub issue **#66** (new 2026-04-14 tick-4 follow-up to PR #65 which shipped Layer 1). Let `config.yaml` / `org.yaml` declare a user-authored `restart_prompt` that is delivered alongside the platform-generated restart-context system message — e.g. "re-read your CLAUDE.md, re-hydrate TODOs from memory, resume the active delegation." Layer 1 (platform state snapshot) already ships; Layer 2 adds the user-defined side.
|
||||
|
||||
### Recently launched (2026-04-14 tick-4)
|
||||
- **GitHub issue #15** — Provisioner: auto-refresh `CLAUDE_CODE_OAUTH_TOKEN` from `global_secrets` on workspace restart → **DONE** via PR #64 (`SetGlobal` / `DeleteGlobal` now fan out `RestartByID` to every affected workspace).
|
||||
- **GitHub issue #19 Layer 1** — Platform-generated restart context → **DONE** via PR #65 (synthetic A2A `message/send` with `metadata.kind=restart_context`, `system:restart-context` caller prefix, 30s re-register wait). Layer 2 deferred to issue #66 (see Backlog item 15 above).
|
||||
|
||||
---
|
||||
|
||||
@ -238,12 +243,12 @@ point for "what else is out there."
|
||||
|
||||
| Stack | Tests | Framework |
|
||||
|-------|-------|-----------|
|
||||
| Go (platform) | 695 | `go test -race` |
|
||||
| Go (platform) | 726 | `go test -race` (raw PASS lines incl. subtests; +6 top-level `Test*` this tick: #64 secrets auto-restart x2, #65 restart-context x4) |
|
||||
| Python (workspace) | 1,140 | pytest |
|
||||
| Canvas (frontend) | 357 | Vitest |
|
||||
| SDK (python) | 132 | pytest |
|
||||
| MCP server | 97 | Jest |
|
||||
| **Total** | **2,421** | |
|
||||
| **Total** | **2,452** | |
|
||||
|
||||
E2E: 67/67 comprehensive checks passing, 62/62 API tests (also gated in CI `e2e-api` job), shellcheck-clean across all 5 E2E scripts.
|
||||
|
||||
|
||||
@ -127,3 +127,125 @@ true doc-sync fix (code ships the var; example now matches).
|
||||
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-template/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
|
||||
`platform/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** — `platform/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).
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user