From 59a96e3888b0c2e60fe2438bde740bf840f4e7e6 Mon Sep 17 00:00:00 2001 From: Hongming Wang Date: Tue, 14 Apr 2026 12:54:04 -0700 Subject: [PATCH] docs: sync documentation with 2026-04-14 evening-tick merges (#63, #64, #65) - 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) --- CLAUDE.md | 8 ++- PLAN.md | 9 ++- docs/edit-history/2026-04-14.md | 122 ++++++++++++++++++++++++++++++++ 3 files changed, 134 insertions(+), 5 deletions(-) diff --git a/CLAUDE.md b/CLAUDE.md index d90be23b..27f69d13 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -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 | diff --git a/PLAN.md b/PLAN.md index 4baeff8b..b38cdb9a 100644 --- a/PLAN.md +++ b/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. diff --git a/docs/edit-history/2026-04-14.md b/docs/edit-history/2026-04-14.md index efcef62b..09e23da0 100644 --- a/docs/edit-history/2026-04-14.md +++ b/docs/edit-history/2026-04-14.md @@ -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//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). +