feat(deploy): finalize MVP deployment and file transfer approach
This commit is contained in:
parent
6369721876
commit
0f79494fbe
43 changed files with 3078 additions and 645 deletions
|
|
@ -1,62 +0,0 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
phase_name: MVP deployment
|
||||
task: 0
|
||||
total_tasks: 0
|
||||
status: paused
|
||||
last_updated: 2026-04-30T15:03:14Z
|
||||
---
|
||||
|
||||
<current_state>
|
||||
Phase 05 code changes are in place, but the latest workspace-root attachment contract is not yet published in a new production image. Today's last debugging step confirmed that the user-to-agent config itself was fine except for one exact-MXID mismatch: the homeserver suffix in `user_agents` did not match the real Matrix sender, so fallback to the first agent was expected.
|
||||
</current_state>
|
||||
|
||||
<completed_work>
|
||||
|
||||
- Fixed the path-based `base_url` normalization bug that caused WS connects to drop route prefixes.
|
||||
- Added WS lifecycle debug logging behind `SURFACES_DEBUG_WS=1`.
|
||||
- Added Matrix routing/recovery behavior:
|
||||
- warning users when they are not listed in `user_agents`
|
||||
- preserving room bindings across config updates
|
||||
- re-inviting users back into their Space and active rooms after leave
|
||||
- `!new` from the entry/DM room to create a fresh working chat
|
||||
- Reworked attachment handling so user files now go directly into the agent workspace root with Windows-style collision suffixes like `file (1).pdf`.
|
||||
- Updated docs and tests to match the new root-workspace file contract.
|
||||
- Verified that the recent “still goes to default agent” report was caused by exact MXID mismatch in config, not by YAML parsing or runtime routing logic.
|
||||
- Published earlier images:
|
||||
- `mput1/surfaces-bot:debug-ws-20260429`
|
||||
- `mput1/surfaces-bot:matrix-recovery-20260429`
|
||||
</completed_work>
|
||||
|
||||
<remaining_work>
|
||||
|
||||
- Build and publish a new production image that includes the latest workspace-root attachment changes.
|
||||
- Give the platform the new digest and ask them to redeploy the Matrix bot container.
|
||||
- Optionally run local smoke/fullstack validation once more before publishing if extra confidence is needed.
|
||||
</remaining_work>
|
||||
|
||||
<decisions_made>
|
||||
|
||||
- Keep the fallback to the first agent when a user is missing from `user_agents`.
|
||||
- Require exact Matrix MXID match in `user_agents`; no fuzzy matching or homeserver normalization was added.
|
||||
- Warn the user in-band when default-agent fallback is used.
|
||||
- Keep room identity and `platform_chat_id` stable across config updates.
|
||||
- Require container restart for config changes; no image rebuild is needed for `matrix-agents.yaml` edits alone.
|
||||
- Remove `incoming/` and timestamp prefixes from the attachment contract.
|
||||
- Save uploaded user files directly at the workspace root and resolve collisions with copy-style suffixes.
|
||||
</decisions_made>
|
||||
|
||||
<blockers>
|
||||
|
||||
- No code blocker.
|
||||
- External dependency: platform redeploy after the next image publish.
|
||||
- Historical debt: placeholder summary/plan artifacts still exist in old Phase 04 files and were not cleaned during this session.
|
||||
</blockers>
|
||||
|
||||
<context>
|
||||
The current codebase should route correctly if the deployed config uses the exact real Matrix sender IDs, e.g. `@user:matrix.lambda.coredump.ru`. The next likely mistake during resume would be publishing the wrong image digest: the currently published recovery image predates the latest file-contract change. Resume by building a fresh image from the current worktree, not by reusing the old digest.
|
||||
</context>
|
||||
|
||||
<next_action>
|
||||
Rebuild the production image from the current worktree, publish it, and send the new digest to the platform for redeploy.
|
||||
</next_action>
|
||||
158
.planning/phases/05-mvp-deployment/05-01-PLAN.md
Normal file
158
.planning/phases/05-mvp-deployment/05-01-PLAN.md
Normal file
|
|
@ -0,0 +1,158 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
plan: 01
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- adapter/matrix/reconciliation.py
|
||||
- adapter/matrix/bot.py
|
||||
- tests/adapter/matrix/test_reconciliation.py
|
||||
- tests/adapter/matrix/test_restart_persistence.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- PH05-01
|
||||
- PH05-03
|
||||
must_haves:
|
||||
truths:
|
||||
- "On restart, existing Matrix Space and child-room topology is rebuilt before live sync begins."
|
||||
- "Restart recovery preserves Space+rooms UX instead of creating duplicate DM-style working rooms."
|
||||
- "Recovered rooms regain user metadata, room metadata, and chat bindings needed for normal routing."
|
||||
- "Legacy working rooms missing `platform_chat_id` are backfilled deterministically during startup before strict routing handles traffic."
|
||||
artifacts:
|
||||
- path: "adapter/matrix/reconciliation.py"
|
||||
provides: "Authoritative restart reconciliation from Matrix topology into local metadata"
|
||||
- path: "adapter/matrix/bot.py"
|
||||
provides: "Startup wiring that runs reconciliation before sync_forever"
|
||||
- path: "tests/adapter/matrix/test_reconciliation.py"
|
||||
provides: "Regression coverage for startup recovery and idempotence"
|
||||
key_links:
|
||||
- from: "adapter/matrix/bot.py"
|
||||
to: "adapter/matrix/reconciliation.py"
|
||||
via: "startup bootstrap before sync_forever"
|
||||
pattern: "reconcil"
|
||||
- from: "adapter/matrix/reconciliation.py"
|
||||
to: "core/chat.py"
|
||||
via: "chat manager rebuild for recovered rooms"
|
||||
pattern: "get_or_create"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Rebuild Matrix-local routing state from authoritative Space topology before the bot processes live traffic.
|
||||
|
||||
Purpose: Preserve the Phase 01 Space+rooms contract after restart even if SQLite metadata is partial or missing.
|
||||
Output: A startup reconciliation module, bot wiring, and regression tests proving no DM-first duplication on restart.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/a/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/a/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/05-mvp-deployment/05-RESEARCH.md
|
||||
@.planning/phases/05-mvp-deployment/05-VALIDATION.md
|
||||
@.planning/phases/04-matrix-mvp-shared-agent-context-and-context-management-comma/04-02-SUMMARY.md
|
||||
@adapter/matrix/bot.py
|
||||
@adapter/matrix/store.py
|
||||
@adapter/matrix/handlers/auth.py
|
||||
@tests/adapter/matrix/test_invite_space.py
|
||||
@tests/adapter/matrix/test_chat_space.py
|
||||
@tests/adapter/matrix/test_restart_persistence.py
|
||||
|
||||
<interfaces>
|
||||
From `adapter/matrix/bot.py`:
|
||||
|
||||
```python
|
||||
async def prepare_live_sync(client: AsyncClient) -> str | None:
|
||||
response = await client.sync(timeout=0, full_state=True)
|
||||
if isinstance(response, SyncResponse):
|
||||
return response.next_batch
|
||||
return None
|
||||
```
|
||||
|
||||
```python
|
||||
class MatrixBot:
|
||||
async def _bootstrap_unregistered_room(
|
||||
self,
|
||||
room: MatrixRoom,
|
||||
sender: str,
|
||||
) -> list[OutgoingEvent] | None: ...
|
||||
```
|
||||
|
||||
From `adapter/matrix/store.py`:
|
||||
|
||||
```python
|
||||
async def get_room_meta(store: StateStore, room_id: str) -> dict | None: ...
|
||||
async def set_room_meta(store: StateStore, room_id: str, meta: dict) -> None: ...
|
||||
async def get_user_meta(store: StateStore, matrix_user_id: str) -> dict | None: ...
|
||||
async def set_user_meta(store: StateStore, matrix_user_id: str, meta: dict) -> None: ...
|
||||
async def next_platform_chat_id(store: StateStore) -> str: ...
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Add restart reconciliation regression coverage</name>
|
||||
<files>tests/adapter/matrix/test_reconciliation.py, tests/adapter/matrix/test_restart_persistence.py</files>
|
||||
<read_first>tests/adapter/matrix/test_invite_space.py, tests/adapter/matrix/test_chat_space.py, tests/adapter/matrix/test_restart_persistence.py, adapter/matrix/bot.py, adapter/matrix/handlers/auth.py, .planning/phases/05-mvp-deployment/05-RESEARCH.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: startup recovery rebuilds user space metadata, room metadata, and chat bindings from Matrix topology without creating new working rooms (per D-Phase05-reset and PH05-01).
|
||||
- Test 2: reconciliation is idempotent and safe when local SQLite state is already present.
|
||||
- Test 3: reconciliation happens before lazy `_bootstrap_unregistered_room()` would run for existing rooms (per PH05-03).
|
||||
- Test 4: legacy room metadata missing `platform_chat_id` is backfilled deterministically at startup and persisted before routed handling begins.
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- `tests/adapter/matrix/test_reconciliation.py` exists and names reconciliation entrypoints explicitly.
|
||||
- The new tests assert restored `space_id`, `chat_id`, `matrix_user_id`, and `platform_chat_id` values for recovered rooms.
|
||||
- The regression slice also proves existing Space onboarding behavior still passes by running `test_invite_space.py` and `test_chat_space.py`.
|
||||
- The automated command in `<verify>` fails before implementation or would fail if reconciliation is removed.
|
||||
</acceptance_criteria>
|
||||
<action>Create a dedicated `tests/adapter/matrix/test_reconciliation.py` module and extend restart persistence coverage so Phase 05 has a real Wave 0 contract. Model the recovered topology after the Phase 01 Space+rooms onboarding tests, not a DM-first flow, and explicitly keep those onboarding regressions in the verification slice so restart hardening cannot break provisioning UX. Cover recovery of `user_meta`, `room_meta`, `ChatManager` bindings, and room-local routing fields from Matrix-side state before live callbacks begin, including deterministic backfill for legacy rooms that predate `platform_chat_id`. Keep temporary UX state out of scope, per research.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_invite_space.py tests/adapter/matrix/test_chat_space.py tests/adapter/matrix/test_reconciliation.py tests/adapter/matrix/test_restart_persistence.py -v</automated>
|
||||
</verify>
|
||||
<done>Phase 05 has failing-or-red-before-code tests that define authoritative restart reconciliation behavior and exclude duplicate room provisioning.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Implement authoritative startup reconciliation and wire it before live sync</name>
|
||||
<files>adapter/matrix/reconciliation.py, adapter/matrix/bot.py</files>
|
||||
<read_first>adapter/matrix/bot.py, adapter/matrix/store.py, adapter/matrix/handlers/auth.py, tests/adapter/matrix/test_reconciliation.py, tests/adapter/matrix/test_restart_persistence.py, .planning/phases/05-mvp-deployment/05-RESEARCH.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: startup rebuild runs after login and initial full-state fetch, but before `sync_forever()` processes live events.
|
||||
- Test 2: recovered rooms keep their existing Space+rooms identity and do not trigger `_bootstrap_unregistered_room()` unless the room is genuinely new.
|
||||
- Test 3: local metadata can be rebuilt from Matrix topology when SQLite entries are missing, while existing valid metadata remains stable.
|
||||
- Test 4: startup repair assigns a deterministic `platform_chat_id` to legacy rooms missing that field and persists it before routed platform calls can occur.
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- `adapter/matrix/reconciliation.py` exports a focused reconciliation entrypoint used by startup code.
|
||||
- `adapter/matrix/bot.py` invokes reconciliation before `client.sync_forever(...)`.
|
||||
- Recovered room metadata includes `room_type`, `chat_id`, `space_id`, `matrix_user_id`, and `platform_chat_id` where available or rebuildable.
|
||||
- Legacy rooms missing `platform_chat_id` follow one documented startup backfill path rather than ad hoc routing fallbacks.
|
||||
</acceptance_criteria>
|
||||
<action>Implement a restart recovery module that treats Matrix topology as authoritative, per the Phase 05 reset and research notes. Rebuild missing local metadata for Space-owned working rooms, deterministically backfill missing `platform_chat_id` values for legacy rooms, and re-create `ChatManager` entries needed by routing, while keeping SQLite as a rebuildable cache rather than the source of truth. Wire the new reconciliation step into startup after the initial full-state sync and before live sync begins, and keep the onboarding regression slice green while doing it. Do not widen into timeline scraping, new storage backends, or DM-first fallbacks.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_invite_space.py tests/adapter/matrix/test_chat_space.py tests/adapter/matrix/test_reconciliation.py tests/adapter/matrix/test_restart_persistence.py tests/adapter/matrix/test_dispatcher.py -v</automated>
|
||||
</verify>
|
||||
<done>Restart recovery restores the minimum durable state for existing Space rooms before live traffic, and the guarded regression suite passes.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Run the onboarding, reconciliation, restart-persistence, and Matrix dispatcher slices together. Confirm startup now has a deterministic pre-sync recovery and legacy-room backfill step instead of relying on lazy room bootstrap or routing-time fallbacks for existing topology.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
The bot can restart with partial or empty local room metadata, rebuild managed Space rooms before live sync, and continue handling those rooms without creating duplicate onboarding rooms.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/05-mvp-deployment/05-01-SUMMARY.md`
|
||||
</output>
|
||||
99
.planning/phases/05-mvp-deployment/05-01-SUMMARY.md
Normal file
99
.planning/phases/05-mvp-deployment/05-01-SUMMARY.md
Normal file
|
|
@ -0,0 +1,99 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
plan: 01
|
||||
subsystem: infra
|
||||
tags: [matrix, reconciliation, sqlite, startup, testing]
|
||||
requires:
|
||||
- phase: 01-matrix-mvp
|
||||
provides: Space+rooms onboarding, room metadata, and Matrix dispatcher behavior
|
||||
- phase: 04-matrix-mvp-shared-agent-context-and-context-management-comma
|
||||
provides: durable platform_chat_id and restart persistence primitives
|
||||
provides:
|
||||
- authoritative startup reconciliation from Matrix room topology into local metadata
|
||||
- pre-sync startup wiring that repairs managed rooms before live traffic
|
||||
- restart regression coverage for reconciliation, idempotence, and legacy platform_chat_id backfill
|
||||
affects: [matrix, startup, deployment, restart-persistence]
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns: [matrix-topology-as-source-of-truth, sqlite-cache-rebuild, pre-sync-reconciliation]
|
||||
key-files:
|
||||
created: [adapter/matrix/reconciliation.py, tests/adapter/matrix/test_reconciliation.py]
|
||||
modified: [adapter/matrix/bot.py, tests/adapter/matrix/test_restart_persistence.py]
|
||||
key-decisions:
|
||||
- "Treat synced Matrix parent/child topology as authoritative for managed room recovery; keep SQLite rebuildable."
|
||||
- "Backfill missing platform_chat_id values during startup reconciliation instead of routing-time fallbacks."
|
||||
patterns-established:
|
||||
- "Startup runs full-state sync, then reconciliation, then sync_forever."
|
||||
- "Recovered Matrix rooms rebuild user_meta, room_meta, auth state, and ChatManager bindings idempotently."
|
||||
requirements-completed: [PH05-01, PH05-03]
|
||||
duration: 8min
|
||||
completed: 2026-04-27
|
||||
---
|
||||
|
||||
# Phase 05 Plan 01: Restart Reconciliation Summary
|
||||
|
||||
**Matrix startup now rebuilds Space-owned working rooms into durable local routing state before live sync begins**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** 8 min
|
||||
- **Started:** 2026-04-27T22:00:47Z
|
||||
- **Completed:** 2026-04-27T22:08:47Z
|
||||
- **Tasks:** 2
|
||||
- **Files modified:** 4
|
||||
|
||||
## Accomplishments
|
||||
- Added a dedicated reconciliation module that restores `user_meta`, `room_meta`, auth state, chat bindings, and missing `platform_chat_id` values from the synced Matrix room graph.
|
||||
- Wired startup to run reconciliation immediately after the initial full-state sync and before `sync_forever()`.
|
||||
- Added regression coverage for recovery, idempotence, pre-sync ordering, onboarding compatibility, and legacy restart backfill.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Add restart reconciliation regression coverage** - `a75b26a` (test)
|
||||
2. **Task 2: Implement authoritative startup reconciliation and wire it before live sync** - `8a80d00` (feat)
|
||||
|
||||
## Files Created/Modified
|
||||
- `adapter/matrix/reconciliation.py` - Startup recovery from Matrix topology into local room and user metadata.
|
||||
- `adapter/matrix/bot.py` - Startup wiring that runs reconciliation after the bootstrap sync and before live sync.
|
||||
- `tests/adapter/matrix/test_reconciliation.py` - Recovery, idempotence, and startup-order regression coverage.
|
||||
- `tests/adapter/matrix/test_restart_persistence.py` - Legacy `platform_chat_id` backfill persistence coverage.
|
||||
|
||||
## Decisions Made
|
||||
- Used the synced Matrix room graph as the authoritative source for restart recovery, while preserving existing local metadata whenever it is already valid.
|
||||
- Kept legacy `platform_chat_id` repair on a single startup path so routed handling never needs ad hoc fallback creation for existing rooms.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 3 - Blocking] Switched verification to a clean `uv run pytest` environment**
|
||||
- **Found during:** Task 1 and Task 2 verification
|
||||
- **Issue:** The default `pytest` path used a mismatched virtualenv without repo dependencies, and `.env` injected Matrix backend variables that polluted mock-mode tests.
|
||||
- **Fix:** Ran the verification slice through `uv run pytest` with `UV_CACHE_DIR=/tmp/uv-cache-surfaces` and blank `MATRIX_AGENT_REGISTRY_PATH` / `MATRIX_PLATFORM_BACKEND` values to match the intended test environment.
|
||||
- **Files modified:** None
|
||||
- **Verification:** `uv run pytest` slice passed with 50/50 tests green
|
||||
- **Committed in:** not applicable (verification-only adjustment)
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 1 auto-fixed (1 blocking)
|
||||
**Impact on plan:** Verification needed an environment correction, but code scope stayed within the plan and owned files.
|
||||
|
||||
## Issues Encountered
|
||||
- The shell environment loaded deployment-oriented Matrix backend settings from `.env`; these had to be neutralized for the mock-mode regression slice.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None - no external service configuration required.
|
||||
|
||||
## Next Phase Readiness
|
||||
- Restart recovery is in place for existing Space rooms, including deterministic legacy `platform_chat_id` repair.
|
||||
- Remaining Phase 05 plans can build on a stable pre-sync recovery path instead of lazy bootstrap for existing topology.
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
---
|
||||
*Phase: 05-mvp-deployment*
|
||||
*Completed: 2026-04-27*
|
||||
156
.planning/phases/05-mvp-deployment/05-02-PLAN.md
Normal file
156
.planning/phases/05-mvp-deployment/05-02-PLAN.md
Normal file
|
|
@ -0,0 +1,156 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
plan: 02
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 05-01
|
||||
files_modified:
|
||||
- adapter/matrix/handlers/__init__.py
|
||||
- adapter/matrix/handlers/context_commands.py
|
||||
- adapter/matrix/routed_platform.py
|
||||
- tests/adapter/matrix/test_context_commands.py
|
||||
- tests/adapter/matrix/test_routed_platform.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- PH05-02
|
||||
must_haves:
|
||||
truths:
|
||||
- "Each working Matrix room uses its own durable `platform_chat_id` as the real agent context boundary."
|
||||
- "`!clear` resets only the current room by rotating its `platform_chat_id` and disconnecting the old upstream chat."
|
||||
- "Save, load, context, and routed send paths resolve through room-local platform context, not shared user state."
|
||||
- "Strict room routing assumes startup reconciliation has already repaired legacy rooms missing `platform_chat_id`."
|
||||
artifacts:
|
||||
- path: "adapter/matrix/handlers/context_commands.py"
|
||||
provides: "Room-local `!clear`, save/load/context resolution, and upstream disconnect behavior"
|
||||
- path: "adapter/matrix/routed_platform.py"
|
||||
provides: "Strict room -> agent_id + platform_chat_id routing"
|
||||
- path: "tests/adapter/matrix/test_context_commands.py"
|
||||
provides: "Regression coverage for `!clear` and room-local context commands"
|
||||
key_links:
|
||||
- from: "adapter/matrix/handlers/__init__.py"
|
||||
to: "adapter/matrix/handlers/context_commands.py"
|
||||
via: "IncomingCommand registration for `clear`"
|
||||
pattern: "\"clear\""
|
||||
- from: "adapter/matrix/routed_platform.py"
|
||||
to: "adapter/matrix/store.py"
|
||||
via: "room metadata lookup"
|
||||
pattern: "platform_chat_id"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Make room-local platform context explicit and user-facing by shipping real `!clear` semantics and strict per-room routing.
|
||||
|
||||
Purpose: Phase 05 must preserve Space+rooms UX while giving each room a true upstream context boundary.
|
||||
Output: Updated command wiring, room-local context reset behavior, and routing regressions tied to `platform_chat_id`.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/a/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/a/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/05-mvp-deployment/05-RESEARCH.md
|
||||
@.planning/phases/05-mvp-deployment/05-VALIDATION.md
|
||||
@.planning/phases/04-matrix-mvp-shared-agent-context-and-context-management-comma/04-02-SUMMARY.md
|
||||
@adapter/matrix/handlers/__init__.py
|
||||
@adapter/matrix/handlers/context_commands.py
|
||||
@adapter/matrix/routed_platform.py
|
||||
@tests/adapter/matrix/test_context_commands.py
|
||||
@tests/adapter/matrix/test_routed_platform.py
|
||||
|
||||
<interfaces>
|
||||
From `adapter/matrix/handlers/__init__.py`:
|
||||
|
||||
```python
|
||||
dispatcher.register(
|
||||
IncomingCommand,
|
||||
"reset",
|
||||
make_handle_reset(store, prototype_state)
|
||||
if prototype_state is not None
|
||||
else handle_settings,
|
||||
)
|
||||
```
|
||||
|
||||
From `adapter/matrix/handlers/context_commands.py`:
|
||||
|
||||
```python
|
||||
async def _resolve_context_scope(
|
||||
event: IncomingCommand,
|
||||
store: StateStore,
|
||||
chat_mgr,
|
||||
) -> tuple[str, str | None]: ...
|
||||
```
|
||||
|
||||
From `adapter/matrix/routed_platform.py`:
|
||||
|
||||
```python
|
||||
async def _resolve_delegate(self, user_id: str, local_chat_id: str) -> tuple[PlatformClient, str]:
|
||||
...
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Expand room-local context and clear-command tests</name>
|
||||
<files>tests/adapter/matrix/test_context_commands.py, tests/adapter/matrix/test_routed_platform.py</files>
|
||||
<read_first>tests/adapter/matrix/test_context_commands.py, tests/adapter/matrix/test_routed_platform.py, adapter/matrix/handlers/__init__.py, adapter/matrix/handlers/context_commands.py, adapter/matrix/routed_platform.py, .planning/phases/05-mvp-deployment/05-VALIDATION.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: `!clear` rotates only the current room's `platform_chat_id` and disconnects only the old upstream chat (per PH05-02).
|
||||
- Test 2: `!clear` is the supported command name; `!reset` may remain as a compatibility alias but must not be the only registered path.
|
||||
- Test 3: routed send/stream paths fail fast when room metadata lacks `agent_id` or `platform_chat_id` instead of silently sharing context.
|
||||
- Test 4: routed behavior uses startup-repaired room metadata and does not introduce a second fallback path that invents `platform_chat_id` during message handling.
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- Tests explicitly mention `clear` in command registration or command invocation.
|
||||
- The context-command tests assert old and new `platform_chat_id` values and upstream disconnect behavior.
|
||||
- The routed-platform tests assert room-local IDs are passed to delegates unchanged.
|
||||
</acceptance_criteria>
|
||||
<action>Extend the current Matrix context-command and routed-platform regressions so Phase 05 has direct coverage for `!clear`, room-local `platform_chat_id` rotation, and fail-fast routing when room bindings are incomplete. Treat startup reconciliation from `05-01` as the only supported repair path for legacy rooms missing `platform_chat_id`; the routed path must consume repaired metadata, not synthesize new room identities on demand. Preserve the Phase 04 prototype-state behavior where it still fits, but anchor new checks on per-room context isolation rather than shared session assumptions.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_context_commands.py tests/adapter/matrix/test_routed_platform.py -v</automated>
|
||||
</verify>
|
||||
<done>The tests define the Phase 05 room-local contract for reset/clear and for routed upstream calls.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Ship real room-local `!clear` semantics and strict routing</name>
|
||||
<files>adapter/matrix/handlers/__init__.py, adapter/matrix/handlers/context_commands.py, adapter/matrix/routed_platform.py</files>
|
||||
<read_first>adapter/matrix/handlers/__init__.py, adapter/matrix/handlers/context_commands.py, adapter/matrix/routed_platform.py, tests/adapter/matrix/test_context_commands.py, tests/adapter/matrix/test_routed_platform.py, .planning/phases/05-mvp-deployment/05-RESEARCH.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: command registration exposes `!clear` as the real context-reset entrypoint for Matrix rooms.
|
||||
- Test 2: only the active room's `platform_chat_id` rotates, and only the old upstream chat session is disconnected.
|
||||
- Test 3: all room-local context commands resolve through recovered room metadata instead of falling back to shared user scope.
|
||||
- Test 4: strict routing stays strict at runtime because legacy-room repair was already handled at startup by `05-01`, not by hidden message-path fallbacks.
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- `adapter/matrix/handlers/__init__.py` registers `clear`; if `reset` remains, it is clearly a compatibility alias.
|
||||
- `adapter/matrix/handlers/context_commands.py` resolves and rotates room-local platform context without touching other rooms.
|
||||
- `adapter/matrix/routed_platform.py` keeps explicit `MATRIX_ROUTE_INCOMPLETE` behavior when bindings are missing.
|
||||
</acceptance_criteria>
|
||||
<action>Update the Matrix context command surface to match the Phase 05 contract: real `!clear`, room-local `platform_chat_id` rotation, and upstream disconnect scoped to the old room context. Keep `save`, `load`, and `context` anchored to the same room-local identity. Tighten routed-platform behavior only where needed to preserve fail-fast semantics after startup reconciliation has repaired legacy rooms; do not reintroduce shared chat state, user-level reset behavior, or message-time backfill of missing `platform_chat_id`.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_context_commands.py tests/adapter/matrix/test_routed_platform.py tests/adapter/matrix/test_dispatcher.py -v</automated>
|
||||
</verify>
|
||||
<done>Users can clear one working room without affecting others, and all routed upstream calls stay bound to room-local platform context.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Run the context and routed-platform slices plus Matrix dispatcher smoke coverage to confirm the exposed command name and room-local routing behavior are consistent.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
Every working Matrix room has an independent upstream context boundary, and `!clear` resets only the room where it is invoked.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/05-mvp-deployment/05-02-SUMMARY.md`
|
||||
</output>
|
||||
145
.planning/phases/05-mvp-deployment/05-03-PLAN.md
Normal file
145
.planning/phases/05-mvp-deployment/05-03-PLAN.md
Normal file
|
|
@ -0,0 +1,145 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
plan: 03
|
||||
type: execute
|
||||
wave: 1
|
||||
depends_on: []
|
||||
files_modified:
|
||||
- adapter/matrix/files.py
|
||||
- sdk/real.py
|
||||
- tests/adapter/matrix/test_files.py
|
||||
- tests/platform/test_real.py
|
||||
autonomous: true
|
||||
requirements:
|
||||
- PH05-04
|
||||
must_haves:
|
||||
truths:
|
||||
- "Incoming Matrix attachments are written into a room-safe shared-volume path and passed upstream as relative workspace paths."
|
||||
- "Agent-emitted files can be returned to Matrix users without inventing a separate file proxy."
|
||||
- "The shared-volume contract works with the Phase 05 `/agents` deployment shape."
|
||||
artifacts:
|
||||
- path: "adapter/matrix/files.py"
|
||||
provides: "Room-safe shared-volume path building and path resolution"
|
||||
- path: "sdk/real.py"
|
||||
provides: "Attachment path passthrough and send-file normalization"
|
||||
- path: "tests/adapter/matrix/test_files.py"
|
||||
provides: "Regression coverage for shared-volume path construction"
|
||||
key_links:
|
||||
- from: "adapter/matrix/files.py"
|
||||
to: "sdk/real.py"
|
||||
via: "relative `workspace_path` transport"
|
||||
pattern: "workspace_path"
|
||||
- from: "sdk/real.py"
|
||||
to: "adapter/matrix/bot.py"
|
||||
via: "OutgoingMessage attachments rendered back to Matrix"
|
||||
pattern: "MsgEventSendFile"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Harden the Matrix attachment path contract around the shared deployment volume instead of custom transport shims.
|
||||
|
||||
Purpose: Phase 05 file handling must survive real deployment with room-safe paths and outbound file delivery through the existing shared-volume model.
|
||||
Output: Attachment-path regressions and any targeted runtime fixes needed for `/agents`-backed shared volume behavior.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/a/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/a/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/05-mvp-deployment/05-RESEARCH.md
|
||||
@.planning/phases/05-mvp-deployment/05-VALIDATION.md
|
||||
@docs/deploy-architecture.md
|
||||
@docs/superpowers/specs/2026-04-20-matrix-shared-workspace-file-flow-design.md
|
||||
@adapter/matrix/files.py
|
||||
@sdk/real.py
|
||||
@tests/adapter/matrix/test_files.py
|
||||
@tests/platform/test_real.py
|
||||
|
||||
<interfaces>
|
||||
From `adapter/matrix/files.py`:
|
||||
|
||||
```python
|
||||
def build_workspace_attachment_path(
|
||||
*,
|
||||
workspace_root: Path,
|
||||
matrix_user_id: str,
|
||||
room_id: str,
|
||||
filename: str,
|
||||
timestamp: str | None = None,
|
||||
) -> tuple[str, Path]: ...
|
||||
```
|
||||
|
||||
From `sdk/real.py`:
|
||||
|
||||
```python
|
||||
@staticmethod
|
||||
def _attachment_paths(attachments: list[Attachment] | None) -> list[str]: ...
|
||||
|
||||
@staticmethod
|
||||
def _attachment_from_send_file_event(event: MsgEventSendFile) -> Attachment: ...
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 1: Add shared-volume file contract tests for `/agents` deployment</name>
|
||||
<files>tests/adapter/matrix/test_files.py, tests/platform/test_real.py</files>
|
||||
<read_first>tests/adapter/matrix/test_files.py, tests/platform/test_real.py, adapter/matrix/files.py, sdk/real.py, docs/deploy-architecture.md, .planning/phases/05-mvp-deployment/05-RESEARCH.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: incoming Matrix files land under a room-safe `surfaces/matrix/.../inbox/...` path that remains relative to the agent workspace contract.
|
||||
- Test 2: upstream file events normalize `/workspace/...` and `/agents/...`-style absolute paths into relative `workspace_path` values.
|
||||
- Test 3: attachment forwarding never switches to inline blobs or HTTP shim URLs (per PH05-04).
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- `tests/adapter/matrix/test_files.py` asserts the path namespace includes sanitized user and room components.
|
||||
- `tests/platform/test_real.py` contains explicit coverage for send-file path normalization.
|
||||
- The automated test command in `<verify>` exercises both inbound and outbound sides of the shared-volume contract.
|
||||
</acceptance_criteria>
|
||||
<action>Expand the file-flow regressions around the real deployment contract described in research and `docs/deploy-architecture.md`. Keep the tests centered on relative `workspace_path` transport and room-safe on-disk layout. Do not introduce proxy URLs, base64 payload transport, or new platform endpoints.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_files.py tests/platform/test_real.py -v</automated>
|
||||
</verify>
|
||||
<done>Phase 05 has direct test coverage for `/agents`-backed shared-volume behavior across inbound and outbound file paths.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto" tdd="true">
|
||||
<name>Task 2: Tighten attachment path handling for the shared volume contract</name>
|
||||
<files>adapter/matrix/files.py, sdk/real.py</files>
|
||||
<read_first>adapter/matrix/files.py, sdk/real.py, tests/adapter/matrix/test_files.py, tests/platform/test_real.py, docs/deploy-architecture.md</read_first>
|
||||
<behavior>
|
||||
- Test 1: inbound attachment helpers keep returning relative paths even when the bot writes into `/agents`.
|
||||
- Test 2: outbound file normalization accepts absolute paths from the agent runtime but strips them back to relative workspace paths for Matrix rendering.
|
||||
- Test 3: no code path emits non-relative attachment references to the upstream agent API.
|
||||
</behavior>
|
||||
<acceptance_criteria>
|
||||
- `sdk/real.py` only forwards relative attachment paths to the agent API.
|
||||
- `sdk/real.py` normalizes both `/workspace` and `/agents` absolute roots if present in send-file events.
|
||||
- `adapter/matrix/files.py` remains the single source of truth for room-safe attachment path construction.
|
||||
</acceptance_criteria>
|
||||
<action>Implement only the minimum runtime changes needed to satisfy the shared-volume tests. Keep `adapter/matrix/files.py` as the single place that builds surface-owned attachment paths, and keep `sdk/real.py` responsible only for attachment passthrough and send-file normalization. Do not widen this plan into compose edits, registry redesign, or bot command changes.</action>
|
||||
<verify>
|
||||
<automated>pytest tests/adapter/matrix/test_files.py tests/platform/test_real.py tests/adapter/matrix/test_send_outgoing.py -v</automated>
|
||||
</verify>
|
||||
<done>Incoming and outgoing file references stay compatible with the real shared-volume deployment contract, and the targeted file/path regressions pass.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Run the Matrix file helper, real platform client, and outgoing-send slices together so the shared-volume contract is validated from write path through return-to-user rendering.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
The Matrix bot and agent runtime can exchange file references through the shared volume using only relative workspace paths and room-safe storage layout.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/05-mvp-deployment/05-03-SUMMARY.md`
|
||||
</output>
|
||||
128
.planning/phases/05-mvp-deployment/05-04-PLAN.md
Normal file
128
.planning/phases/05-mvp-deployment/05-04-PLAN.md
Normal file
|
|
@ -0,0 +1,128 @@
|
|||
---
|
||||
phase: 05-mvp-deployment
|
||||
plan: 04
|
||||
type: execute
|
||||
wave: 2
|
||||
depends_on:
|
||||
- 05-03
|
||||
files_modified:
|
||||
- docker-compose.prod.yml
|
||||
- docker-compose.fullstack.yml
|
||||
- Dockerfile
|
||||
- .env.example
|
||||
- README.md
|
||||
- docs/deploy-architecture.md
|
||||
autonomous: true
|
||||
requirements:
|
||||
- PH05-05
|
||||
must_haves:
|
||||
truths:
|
||||
- "Production handoff uses a bot-only compose artifact instead of the internal full-stack harness."
|
||||
- "Internal E2E compose brings up the bot, platform-agent, and shared volume with explicit health-gated startup."
|
||||
- "Deployment docs and env examples match the split compose artifacts and shared `/agents` contract."
|
||||
artifacts:
|
||||
- path: "docker-compose.prod.yml"
|
||||
provides: "Bot-only deployment handoff artifact"
|
||||
- path: "docker-compose.fullstack.yml"
|
||||
provides: "Internal E2E harness with shared volume and dependency gating"
|
||||
- path: ".env.example"
|
||||
provides: "Documented runtime contract for Phase 05 deployment"
|
||||
key_links:
|
||||
- from: "docker-compose.fullstack.yml"
|
||||
to: "docker-compose.prod.yml"
|
||||
via: "shared service definition or explicit duplication"
|
||||
pattern: "matrix-bot"
|
||||
- from: "docs/deploy-architecture.md"
|
||||
to: "docker-compose.prod.yml"
|
||||
via: "operator handoff instructions"
|
||||
pattern: "prod"
|
||||
---
|
||||
|
||||
<objective>
|
||||
Split deployment artifacts by operational intent so operator handoff and internal E2E testing stop sharing the same compose contract.
|
||||
|
||||
Purpose: Phase 05 needs an explicit bot-only production artifact and a separate full-stack compose harness aligned with the shared-volume design.
|
||||
Output: `docker-compose.prod.yml`, `docker-compose.fullstack.yml`, and updated env/docs describing when to use each.
|
||||
</objective>
|
||||
|
||||
<execution_context>
|
||||
@/Users/a/.codex/get-shit-done/workflows/execute-plan.md
|
||||
@/Users/a/.codex/get-shit-done/templates/summary.md
|
||||
</execution_context>
|
||||
|
||||
<context>
|
||||
@.planning/PROJECT.md
|
||||
@.planning/ROADMAP.md
|
||||
@.planning/STATE.md
|
||||
@.planning/phases/05-mvp-deployment/05-RESEARCH.md
|
||||
@.planning/phases/05-mvp-deployment/05-VALIDATION.md
|
||||
@.planning/phases/04-matrix-mvp-shared-agent-context-and-context-management-comma/04-03-SUMMARY.md
|
||||
@docs/deploy-architecture.md
|
||||
@docker-compose.yml
|
||||
@Dockerfile
|
||||
@.env.example
|
||||
|
||||
<interfaces>
|
||||
Current root compose contract:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
platform-agent:
|
||||
...
|
||||
matrix-bot:
|
||||
build: .
|
||||
env_file: .env
|
||||
environment:
|
||||
AGENT_BASE_URL: http://platform-agent:8000
|
||||
SURFACES_WORKSPACE_DIR: /workspace
|
||||
```
|
||||
</interfaces>
|
||||
</context>
|
||||
|
||||
<tasks>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 1: Create split prod and fullstack compose artifacts</name>
|
||||
<files>docker-compose.prod.yml, docker-compose.fullstack.yml, Dockerfile, .env.example</files>
|
||||
<read_first>docker-compose.yml, Dockerfile, .env.example, docs/deploy-architecture.md, .planning/phases/05-mvp-deployment/05-RESEARCH.md, .planning/phases/05-mvp-deployment/05-VALIDATION.md</read_first>
|
||||
<acceptance_criteria>
|
||||
- `docker-compose.prod.yml` defines only the bot-side runtime required for operator handoff.
|
||||
- `docker-compose.fullstack.yml` includes the internal platform-agent service, shared volume mounts, and health-gated startup rather than sleep-based sequencing.
|
||||
- `.env.example` documents the Phase 05 env contract without requiring the reader to inspect the old root compose file.
|
||||
</acceptance_criteria>
|
||||
<action>Create two explicit compose artifacts per PH05-05: a bot-only `docker-compose.prod.yml` for deployment handoff and a `docker-compose.fullstack.yml` for internal E2E runs. Align mounts and env values with the Phase 05 shared `/agents` volume direction from research. Reuse the existing Dockerfile unless a small compatibility edit is required. Do not keep the old single-file compose setup as the only documented runtime.</action>
|
||||
<verify>
|
||||
<automated>docker compose -f docker-compose.prod.yml config > /tmp/phase05-prod-compose.yml && docker compose -f docker-compose.fullstack.yml config > /tmp/phase05-fullstack-compose.yml && rg -n "^services:$|^ matrix-bot:$|^volumes:$|/agents" /tmp/phase05-prod-compose.yml && test -z "$(rg -n "^ platform-agent:$" /tmp/phase05-prod-compose.yml)" && rg -n "^ matrix-bot:$|^ platform-agent:$|condition: service_healthy|healthcheck:|/agents" /tmp/phase05-fullstack-compose.yml</automated>
|
||||
</verify>
|
||||
<done>Both compose files render successfully and express distinct operational roles: prod handoff vs internal full-stack testing.</done>
|
||||
</task>
|
||||
|
||||
<task type="auto">
|
||||
<name>Task 2: Update deployment docs and operator guidance for the split artifacts</name>
|
||||
<files>README.md, docs/deploy-architecture.md</files>
|
||||
<read_first>README.md, docs/deploy-architecture.md, docker-compose.prod.yml, docker-compose.fullstack.yml, .env.example</read_first>
|
||||
<acceptance_criteria>
|
||||
- README or deploy doc tells the operator exactly which compose file to use for production vs internal E2E.
|
||||
- The docs describe the shared `/agents` volume behavior and reference the relevant env vars.
|
||||
- The old root `docker-compose.yml` is no longer the primary documented deployment path.
|
||||
</acceptance_criteria>
|
||||
<action>Update the repo docs so the Phase 05 deployment story is executable without inference: production handoff stays bot-only, full-stack compose is for internal E2E, and shared-volume file behavior is described in the same terms as the runtime artifacts. Keep documentation narrowly scoped to the shipped compose split; do not widen into platform-master or future storage design.</action>
|
||||
<verify>
|
||||
<automated>rg -n "docker-compose\\.prod|docker-compose\\.fullstack|/agents|prod handoff|full-stack" README.md docs/deploy-architecture.md .env.example && rg -n "production|deploy" README.md docs/deploy-architecture.md | rg "docker-compose\\.prod" && test -z "$(rg -n "docker compose up|docker-compose\\.yml" README.md docs/deploy-architecture.md | rg "production|deploy")"</automated>
|
||||
</verify>
|
||||
<done>The docs and env guidance match the new compose artifacts and no longer imply a single shared deployment file.</done>
|
||||
</task>
|
||||
|
||||
</tasks>
|
||||
|
||||
<verification>
|
||||
Render both compose files, then grep the docs for the new artifact names and `/agents` references so the operator contract is explicit and consistent.
|
||||
</verification>
|
||||
|
||||
<success_criteria>
|
||||
An operator can deploy the Matrix bot with the bot-only compose file, while developers can run the internal end-to-end harness separately without reinterpreting the deployment docs.
|
||||
</success_criteria>
|
||||
|
||||
<output>
|
||||
After completion, create `.planning/phases/05-mvp-deployment/05-04-SUMMARY.md`
|
||||
</output>
|
||||
|
|
@ -1,157 +0,0 @@
|
|||
# Phase 05: MVP Deployment — Context
|
||||
|
||||
**Gathered:** 2026-04-27
|
||||
**Status:** Ready for planning
|
||||
|
||||
<domain>
|
||||
## Phase Boundary
|
||||
|
||||
Подготовить Matrix-бот к реальному деплою на lambda.coredump.ru:
|
||||
1. Перейти на single-chat архитектуру (chat_id=0, один контекст на пользователя)
|
||||
2. Упростить онбординг: DM-first без Space/rooms provisioning, welcome-сообщение при invite
|
||||
3. Расширить config/matrix-agents.yaml — добавить user_agents (Matrix user_id → agent_id) и per-agent base_url/workspace_path
|
||||
4. Обновить AgentRegistry и _build_platform_from_env для per-agent URL routing
|
||||
5. Реализовать file transfer через shared volume /agents/: входящие → incoming/{filename}, исходящие через MsgEventSendFile
|
||||
6. Добавить !clear (сброс контекста через переподключение AgentApi)
|
||||
7. Написать docker-compose.prod.yml с полным стеком (matrix-bot + placeholder agent + named volume agents)
|
||||
8. Удалить legacy: !agent, !new, !archive, !rename, !save, !load, Space-creation, C1/C2/C3 room provisioning
|
||||
|
||||
НЕ входит:
|
||||
- Конфигурация агентских контейнеров (платформа)
|
||||
- Telegram-адаптер
|
||||
- E2EE
|
||||
- platform-master интеграция
|
||||
- !save / !load (ненадёжны без persistent memory в агенте)
|
||||
|
||||
</domain>
|
||||
|
||||
<decisions>
|
||||
## Implementation Decisions
|
||||
|
||||
### Single-chat архитектура
|
||||
- **D-01:** chat_id=0 для всех сообщений. Один контекст агента на пользователя. Изоляции между разными разговорами нет — вместо этого `!clear` сбрасывает контекст.
|
||||
- **D-02:** Удалить всю multi-room инфраструктуру: C1/C2/C3, `!new`, `!archive`, `!rename`, Space-creation, room provisioning. Matrix-бот работает только в DM-комнате (личка с ботом).
|
||||
- **D-03:** Удалить `!save` и `!load` — ненадёжны без persistent memory в агенте (MemorySaver сбрасывается на рестарте).
|
||||
|
||||
### Онбординг (DM-first)
|
||||
- **D-04:** При получении invite в DM-комнату — принять, отправить welcome-сообщение: "Привет! Я Lambda AI-агент. Просто напиши — и я отвечу. `!clear` чтобы начать новый разговор, `!context` чтобы посмотреть статус."
|
||||
- **D-05:** Никакого Space, никаких дочерних комнат. Вся переписка в одной DM-комнате.
|
||||
|
||||
### !clear (новая команда)
|
||||
- **D-06:** Сбросить контекст агента — закрыть текущий AgentApi connection и создать новый (`await agent.close()` + `await agent.connect()`). Это сбрасывает MemorySaver. Подтвердить пользователю: "Контекст сброшен. Начнём с чистого листа."
|
||||
|
||||
### !agent команда
|
||||
- **D-07:** Удалить полностью. Маппинг user→agent теперь статический из config. Пользователь не может менять агента.
|
||||
|
||||
### Конфиг агентов (config/matrix-agents.yaml)
|
||||
- **D-02:** Расширить текущий matrix-agents.yaml — добавить user_agents dict и поля base_url/workspace_path к каждому агенту. Один файл, один парсер. Формат по docs/deploy-architecture.md:
|
||||
```yaml
|
||||
user_agents:
|
||||
"@user0:matrix.lambda.coredump.ru": agent-0
|
||||
"@user1:matrix.lambda.coredump.ru": agent-1
|
||||
|
||||
agents:
|
||||
- id: agent-0
|
||||
label: "Agent 0"
|
||||
base_url: "ws://lambda.coredump.ru:7000/agent_0/"
|
||||
workspace_path: "/agents/0/"
|
||||
```
|
||||
- **D-03:** AgentDefinition расширяется полями base_url (str) и workspace_path (str). AgentRegistry добавляет user_agents dict (Matrix user_id → agent_id) и метод get_agent_id_by_user(matrix_user_id).
|
||||
|
||||
### Роутинг user → agent в _build_platform_from_env
|
||||
- **D-04:** Вместо глобального AGENT_BASE_URL — per-agent URL из конфига. _build_platform_from_env строит delegates с правильным base_url для каждого агента. RoutedPlatformClient._resolve_delegate использует user_agents из registry для определения delegate по Matrix user_id.
|
||||
|
||||
### Входящие файлы (пользователь → агент)
|
||||
- **D-05:** Путь внутри workspace агента: `incoming/{filename}`. Абсолютный путь: `{workspace_path}/incoming/{filename}` (например `/agents/0/incoming/photo.jpg`). Обновить files.py: `build_workspace_attachment_path` принимает workspace_path агента и строит путь `incoming/{filename}`. Передавать в agent.send_message() как attachments=["incoming/{filename}"] (относительно /workspace).
|
||||
- **D-06:** workspace_path агента берётся из AgentDefinition по agent_id пользователя.
|
||||
|
||||
### Исходящие файлы (агент → пользователь)
|
||||
- **D-07:** При получении MsgEventSendFile(path="output/report.pdf") — читать файл из `{workspace_path}/{path}`. Отправлять как Matrix file message. Обработчик в Matrix bot.py при обработке stream-ответов от агента.
|
||||
|
||||
### docker-compose для prod
|
||||
- **D-08:** `docker-compose.prod.yml` включает полный стек: Matrix-бот + агент-контейнер (placeholder image `lambda-agent:latest` — уточнить у платформы) + named volume `agents`. Это позволяет тестировать полный стек самостоятельно. Платформа берёт отсюда схему интеграции для своего деплоя.
|
||||
- **D-09:** Named volume `agents` монтируется в Matrix-бот как `/agents/` и в агент-контейнер как `/workspace`. Env vars из `.env.prod`. Запуск: `docker compose -f docker-compose.prod.yml up`.
|
||||
|
||||
### Неавторизованные пользователи
|
||||
- **D-10:** Если Matrix user_id не найден в `user_agents` — принять invite, отправить сообщение: "К вашему аккаунту не привязан агент. Напишите @og_mput в Telegram для получения доступа." Дальнейшие сообщения игнорировать (или повторять то же сообщение).
|
||||
|
||||
### !clear
|
||||
- **D-11:** Без диалога подтверждения — сбрасывает немедленно. Закрыть текущий AgentApi connection, создать новый. Ответ пользователю: "Контекст сброшен."
|
||||
|
||||
### !settings и прочие команды настроек
|
||||
- **D-12:** Удалить `!settings`, `!settings soul`, `!settings skills`, `!settings safety` — agent_api не предоставляет настроек, всё равно возвращало "недоступно в MVP".
|
||||
|
||||
### Claude's Discretion
|
||||
- MATRIX_AGENT_REGISTRY_PATH — оставить как env var для пути к конфигу (уже существует)
|
||||
- Формат .env.prod
|
||||
- Group room invites (не-DM) — отклонять автоматически
|
||||
- Существующие Space+rooms у старых пользователей — игнорировать, не мигрировать
|
||||
|
||||
</decisions>
|
||||
|
||||
<canonical_refs>
|
||||
## Canonical References
|
||||
|
||||
**Downstream agents MUST read these before planning or implementing.**
|
||||
|
||||
### Deployment architecture (PRIMARY)
|
||||
- `docs/deploy-architecture.md` — Топология, формат конфига, AgentApi lifecycle, file transfer protocol, открытые вопросы
|
||||
|
||||
### Существующий код (изменяем)
|
||||
- `adapter/matrix/agent_registry.py` — AgentRegistry, AgentDefinition, load_agent_registry — расширяем
|
||||
- `adapter/matrix/bot.py` — _build_platform_from_env, _load_agent_registry_from_env — обновляем роутинг
|
||||
- `adapter/matrix/routed_platform.py` — RoutedPlatformClient._resolve_delegate — обновляем логику
|
||||
- `adapter/matrix/files.py` — build_workspace_attachment_path, download_matrix_attachment — меняем путь
|
||||
- `adapter/matrix/handlers/agent.py` — удаляем или делаем no-op (!agent handler)
|
||||
- `config/matrix-agents.yaml` — расширяем формат
|
||||
- `docker-compose.yml` — существующий dev compose (за основу для prod варианта)
|
||||
|
||||
### SDK (используем как есть)
|
||||
- `sdk/real.py` — RealPlatformClient — base_url теперь per-instance, но сам класс не меняется
|
||||
- `sdk/upstream_agent_api.py` — AgentApi, MsgEventSendFile — читаем MsgEventSendFile в стриме
|
||||
|
||||
</canonical_refs>
|
||||
|
||||
<code_context>
|
||||
## Existing Code Insights
|
||||
|
||||
### Reusable Assets
|
||||
- `adapter/matrix/files.py::build_workspace_attachment_path` — уже строит путь к файлу, нужно заменить логику `surfaces/matrix/...` на `incoming/{filename}`
|
||||
- `adapter/matrix/files.py::download_matrix_attachment` — скачивает файл, нужно передавать workspace_path агента
|
||||
- `adapter/matrix/agent_registry.py::load_agent_registry` — парсер YAML, расширяем без переписывания
|
||||
|
||||
### Established Patterns
|
||||
- `RoutedPlatformClient` + delegates: dict[agent_id, RealPlatformClient] — паттерн уже есть, нужно только per-agent URL при создании delegates
|
||||
- `MATRIX_PLATFORM_BACKEND=real` активирует prod-path — сохраняем
|
||||
- `MATRIX_AGENT_REGISTRY_PATH` — env var для пути к конфигу — сохраняем
|
||||
|
||||
### Integration Points
|
||||
- `_build_platform_from_env` создаёт delegates — здесь меняется источник URL (из конфига, не из env)
|
||||
- `RoutedPlatformClient._resolve_delegate` — здесь добавляется lookup по user_agents
|
||||
- Matrix bot stream handler — здесь добавляется обработка MsgEventSendFile
|
||||
|
||||
</code_context>
|
||||
|
||||
<specifics>
|
||||
## Specific Ideas
|
||||
|
||||
- AgentApi конструктор в master ветке: `AgentApi(agent_id, base_url, on_disconnect=..., chat_id=0)` — base_url это ws:// URL агента
|
||||
- Входящий файл: bot скачивает из Matrix → пишет в `{workspace_path}/incoming/{filename}` → вызывает `agent.send_message(text, attachments=["incoming/{filename}"])` (путь relative to /workspace)
|
||||
- Исходящий файл: при `MsgEventSendFile(path="output/report.pdf")` → читаем `{workspace_path}/output/report.pdf` → отправляем в Matrix через `client.upload()` → `client.room_send(m.file)`
|
||||
- docker-compose.prod.yml монтирует volume: `volumes: ["/agents/:/agents/"]` — хост обеспечивает директорию
|
||||
|
||||
</specifics>
|
||||
|
||||
<deferred>
|
||||
## Deferred Ideas
|
||||
|
||||
- platform-master интеграция (динамический get_agent_url через POST /api/v1/create) — когда feat/storage будет готов
|
||||
- !agent как admin-override — не нужен для MVP, можно добавить позже если потребуется
|
||||
- Per-chat context isolation через разные chat_id (сейчас chat_id=0 для всех) — ждём platform сигнал
|
||||
|
||||
</deferred>
|
||||
|
||||
---
|
||||
|
||||
*Phase: 05-mvp-deployment*
|
||||
*Context gathered: 2026-04-27*
|
||||
|
|
@ -1,65 +0,0 @@
|
|||
# Phase 05: MVP Deployment — Discussion Log
|
||||
|
||||
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
|
||||
> Decisions captured in CONTEXT.md — this log preserves the alternatives considered.
|
||||
|
||||
**Date:** 2026-04-27
|
||||
**Phase:** 05-mvp-deployment
|
||||
**Areas discussed:** !agent legacy, file transfer path, config format, docker-compose scope
|
||||
|
||||
---
|
||||
|
||||
## !agent команда
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Удалить | Убираем полностью — маппинг статический из конфига | ✓ |
|
||||
| Оставить как no-op | Команда остаётся но ничего не делает | |
|
||||
| Только для dev-режима | Работает когда нет user_agents в конфиге | |
|
||||
|
||||
**User's choice:** Удалить
|
||||
**Notes:** Команда была legacy от эпохи когда роутинг был динамическим. С user_agents в конфиге она не нужна.
|
||||
|
||||
---
|
||||
|
||||
## Путь входящих файлов
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| incoming/{filename} | По docs/deploy-architecture.md — /agents/N/incoming/file | ✓ |
|
||||
| surfaces/matrix/{user}/{room}/inbox/{file} | Текущий формат files.py | |
|
||||
|
||||
**User's choice:** incoming/{filename}
|
||||
**Notes:** Пользователь указал — это решение от платформенной команды, зафиксировано в docs/deploy-architecture.md.
|
||||
|
||||
---
|
||||
|
||||
## Формат config/matrix-agents.yaml
|
||||
|
||||
| Option | Description | Selected |
|
||||
|--------|-------------|----------|
|
||||
| Расширить текущий YAML | Добавить user_agents + base_url/workspace_path в тот же файл | ✓ |
|
||||
| Отдельный prod-config.yaml | Два файла: registry (id/label) + prod конфиг (URL/user_agents) | |
|
||||
|
||||
**User's choice:** Расширить текущий YAML
|
||||
**Notes:** Один файл проще. Формат уже определён в docs/deploy-architecture.md.
|
||||
|
||||
---
|
||||
|
||||
## docker-compose prod scope
|
||||
|
||||
**User's choice:** docker-compose.prod.yml только для Matrix-бота
|
||||
**Notes:** Платформа отвечает за агентские контейнеры — мы их не трогаем. Matrix-бот монтирует /agents/ как external host path, платформа обеспечивает содержимое.
|
||||
|
||||
---
|
||||
|
||||
## Claude's Discretion
|
||||
|
||||
- Обработка Matrix user_id не найденного в user_agents
|
||||
- Имена env переменных для prod
|
||||
- Формат .env.prod
|
||||
|
||||
## Deferred Ideas
|
||||
|
||||
- platform-master интеграция
|
||||
- Per-chat chat_id isolation
|
||||
|
|
@ -1,13 +1,13 @@
|
|||
---
|
||||
phase: 5
|
||||
phase: 05
|
||||
slug: mvp-deployment
|
||||
status: draft
|
||||
nyquist_compliant: false
|
||||
status: revised
|
||||
nyquist_compliant: true
|
||||
wave_0_complete: false
|
||||
created: 2026-04-27
|
||||
created: 2026-04-28
|
||||
---
|
||||
|
||||
# Phase 5 — Validation Strategy
|
||||
# Phase 05 — Validation Strategy
|
||||
|
||||
> Per-phase validation contract for feedback sampling during execution.
|
||||
|
||||
|
|
@ -17,35 +17,35 @@ created: 2026-04-27
|
|||
|
||||
| Property | Value |
|
||||
|----------|-------|
|
||||
| **Framework** | pytest |
|
||||
| **Config file** | pyproject.toml |
|
||||
| **Quick run command** | `pytest tests/adapter/matrix/ -v -x` |
|
||||
| **Framework** | `pytest` + `pytest-asyncio` |
|
||||
| **Config file** | `pyproject.toml` |
|
||||
| **Quick run command** | `pytest tests/adapter/matrix/test_reconciliation.py tests/adapter/matrix/test_restart_persistence.py -v` |
|
||||
| **Full suite command** | `pytest tests/ -v` |
|
||||
| **Estimated runtime** | ~30 seconds |
|
||||
| **Estimated runtime** | targeted slices < 60 seconds each; full suite longer |
|
||||
|
||||
---
|
||||
|
||||
## Sampling Rate
|
||||
|
||||
- **After every task commit:** Run `pytest tests/adapter/matrix/ -v -x`
|
||||
- **After every plan wave:** Run `pytest tests/ -v`
|
||||
- **Before `/gsd-verify-work`:** Full suite must be green
|
||||
- **Max feedback latency:** 30 seconds
|
||||
- **After every task commit:** Run the exact `<automated>` command from the task that just changed
|
||||
- **After every plan wave:** Run `pytest tests/adapter/matrix/ -v`
|
||||
- **Before `$gsd-verify-work`:** Full suite must be green
|
||||
- **Max feedback latency:** 60 seconds for task-level slices
|
||||
|
||||
---
|
||||
|
||||
## Per-Task Verification Map
|
||||
|
||||
| Task ID | Plan | Wave | Requirement | Threat Ref | Secure Behavior | Test Type | Automated Command | File Exists | Status |
|
||||
|---------|------|------|-------------|------------|-----------------|-----------|-------------------|-------------|--------|
|
||||
| 05-A-01 | A | 1 | D-02/D-03 | — | agent_id lookup by matrix_user_id only | unit | `pytest tests/adapter/matrix/test_agent_registry.py -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-A-02 | A | 1 | D-04 | — | per-agent URL used in delegates | unit | `pytest tests/adapter/matrix/test_routed_platform.py -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-B-01 | B | 1 | D-04/D-05 | — | welcome message sent on invite | unit | `pytest tests/adapter/matrix/test_onboarding.py -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-B-02 | B | 1 | D-10 | — | unauthorized user gets access-denied message | unit | `pytest tests/adapter/matrix/test_onboarding.py::test_unauthorized -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-B-03 | B | 1 | D-11 | — | !clear closes and reopens AgentApi | unit | `pytest tests/adapter/matrix/test_commands.py::test_clear -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-C-01 | C | 2 | D-05/D-06 | — | incoming file written to workspace_path/incoming/ | unit | `pytest tests/adapter/matrix/test_files.py -v` | ✅ | ⬜ pending |
|
||||
| 05-C-02 | C | 2 | D-07 | — | outgoing MsgEventSendFile reads from workspace_path | unit | `pytest tests/adapter/matrix/test_files.py::test_outgoing_file -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-C-03 | C | 2 | D-08/D-09 | — | docker-compose.prod.yml has agents volume and both services | manual | see below | N/A | ⬜ pending |
|
||||
| Task ID | Plan | Wave | Requirement | Test Type | Automated Command | File Exists | Status |
|
||||
|---------|------|------|-------------|-----------|-------------------|-------------|--------|
|
||||
| 05-01-01 | 01 | 1 | PH05-01 | integration | `pytest tests/adapter/matrix/test_invite_space.py tests/adapter/matrix/test_chat_space.py tests/adapter/matrix/test_reconciliation.py tests/adapter/matrix/test_restart_persistence.py -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-01-02 | 01 | 1 | PH05-03 | integration | `pytest tests/adapter/matrix/test_invite_space.py tests/adapter/matrix/test_chat_space.py tests/adapter/matrix/test_reconciliation.py tests/adapter/matrix/test_restart_persistence.py tests/adapter/matrix/test_dispatcher.py -v` | ❌ W0 | ⬜ pending |
|
||||
| 05-02-01 | 02 | 2 | PH05-02 | integration | `pytest tests/adapter/matrix/test_context_commands.py tests/adapter/matrix/test_routed_platform.py -v` | ✅ partial | ⬜ pending |
|
||||
| 05-02-02 | 02 | 2 | PH05-02 | integration | `pytest tests/adapter/matrix/test_context_commands.py tests/adapter/matrix/test_routed_platform.py tests/adapter/matrix/test_dispatcher.py -v` | ✅ partial | ⬜ pending |
|
||||
| 05-03-01 | 03 | 1 | PH05-04 | integration | `pytest tests/adapter/matrix/test_files.py tests/platform/test_real.py -v` | ✅ partial | ⬜ pending |
|
||||
| 05-03-02 | 03 | 1 | PH05-04 | integration | `pytest tests/adapter/matrix/test_files.py tests/platform/test_real.py tests/adapter/matrix/test_send_outgoing.py -v` | ✅ partial | ⬜ pending |
|
||||
| 05-04-01 | 04 | 2 | PH05-05 | smoke | `docker compose -f docker-compose.prod.yml config && docker compose -f docker-compose.fullstack.yml config` | ❌ W0 | ⬜ pending |
|
||||
| 05-04-02 | 04 | 2 | PH05-05 | docs smoke | `rg -n "docker-compose\\.prod|docker-compose\\.fullstack|/agents|prod handoff|full-stack" README.md docs/deploy-architecture.md .env.example` | ✅ | ⬜ pending |
|
||||
|
||||
*Status: ⬜ pending · ✅ green · ❌ red · ⚠️ flaky*
|
||||
|
||||
|
|
@ -53,13 +53,11 @@ created: 2026-04-27
|
|||
|
||||
## Wave 0 Requirements
|
||||
|
||||
- [ ] `tests/adapter/matrix/test_agent_registry.py` — tests for user_agents lookup and per-agent base_url/workspace_path
|
||||
- [ ] `tests/adapter/matrix/test_routed_platform.py` — updated tests for _resolve_delegate using user_agents
|
||||
- [ ] `tests/adapter/matrix/test_onboarding.py` — tests for invite handling, welcome message, unauthorized user response
|
||||
- [ ] `tests/adapter/matrix/test_commands.py` — tests for !clear command behavior
|
||||
- [ ] Update `tests/adapter/matrix/test_files.py` — add outgoing file test
|
||||
|
||||
*Existing: `tests/adapter/matrix/test_files.py` — already exists, covers incoming file path logic*
|
||||
- [ ] `tests/adapter/matrix/test_reconciliation.py` — startup recovery of user and room metadata from Matrix state
|
||||
- [ ] `tests/adapter/matrix/test_restart_persistence.py` additions — deterministic backfill for legacy rooms missing `platform_chat_id`
|
||||
- [ ] `tests/adapter/matrix/test_context_commands.py` additions — room-local `!clear` rotation semantics
|
||||
- [ ] `tests/adapter/matrix/test_files.py` additions — cross-room attachment isolation and shared-root consistency
|
||||
- [ ] Compose smoke coverage or documented verification command for `docker-compose.prod.yml` and `docker-compose.fullstack.yml`
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -67,8 +65,9 @@ created: 2026-04-27
|
|||
|
||||
| Behavior | Requirement | Why Manual | Test Instructions |
|
||||
|----------|-------------|------------|-------------------|
|
||||
| docker-compose.prod.yml full-stack launch | D-08/D-09 | Requires Docker daemon and lambda-agent:latest image | `docker compose -f docker-compose.prod.yml up` — verify both services start, volume mounts at /agents/ |
|
||||
| Matrix bot invite + DM flow | D-04/D-05 | Requires live Matrix homeserver | Invite bot to DM, verify welcome message appears |
|
||||
| Restart after real Matrix room topology exists | PH05-03 | Full recovery depends on live Space hierarchy and persisted homeserver state | Start the bot, provision a Space and chat rooms, stop the bot, remove local SQLite metadata, restart, confirm routing and room labels are rebuilt before live messages are handled |
|
||||
| Shared `/agents` volume behavior across bot and platform containers | PH05-04 | Container mounts and permissions are environment-dependent | Run `docker compose -f docker-compose.fullstack.yml up`, upload a file in Matrix, confirm the agent sees the relative `workspace_path`, then confirm an agent-created file is readable back from the bot side |
|
||||
| Operator handoff of prod compose | PH05-05 | Final deploy contract depends on real env files and target host conventions | Run `docker compose -f docker-compose.prod.yml config` on the target deployment checkout and confirm only bot services, required env vars, and shared volumes are present |
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -78,7 +77,7 @@ created: 2026-04-27
|
|||
- [ ] Sampling continuity: no 3 consecutive tasks without automated verify
|
||||
- [ ] Wave 0 covers all MISSING references
|
||||
- [ ] No watch-mode flags
|
||||
- [ ] Feedback latency < 30s
|
||||
- [ ] `nyquist_compliant: true` set in frontmatter
|
||||
- [x] Feedback latency target tightened to task slices under 60s
|
||||
- [x] `nyquist_compliant: true` set in frontmatter
|
||||
|
||||
**Approval:** pending
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue