All checks were successful
ci/woodpecker/push/ci Pipeline was successful
Co-authored-by: Jason Woltje <jason@diversecanvas.com> Co-committed-by: Jason Woltje <jason@diversecanvas.com>
104 lines
5.0 KiB
Markdown
104 lines
5.0 KiB
Markdown
# P8-019 Verification — Phase 8 Platform Architecture
|
|
|
|
**Date:** 2026-03-15
|
|
**Status:** complete
|
|
**Branch:** feat/p8-019-verify
|
|
**PR:** #185
|
|
**Issue:** #172
|
|
|
|
## Test Results
|
|
|
|
- Unit tests (baseline, pre-P8-019): 101 passing across 9 gateway test files + 1 CLI file
|
|
- Integration tests added: 2 new spec files (68 new tests)
|
|
- `apps/gateway/src/commands/commands.integration.spec.ts` — 42 tests
|
|
- `packages/cli/src/tui/commands/commands.integration.spec.ts` — 26 tests
|
|
- Total after P8-019: 160 passing tests across 12 test files
|
|
- Quality gates: typecheck ✓ lint ✓ format:check ✓ test ✓
|
|
|
|
## Components Verified
|
|
|
|
### Command System
|
|
|
|
- `CommandRegistryService.getManifest()` returns 19 core commands (>= 12 requirement met)
|
|
- All commands have correct `execution` type:
|
|
- `socket`: model, thinking, new, clear, compact, retry, system, gc, agent, mission, prdy, tools, reload
|
|
- `rest`: rename, history, export, preferences
|
|
- `hybrid`: provider, status (gateway), (status overridden to local in TUI)
|
|
- `local`: help (gateway); help, stop, cost, status, clear (TUI local)
|
|
- All aliases verified: m→model, t→thinking, n→new, a→agent, s→status, h→help, pref→preferences
|
|
- `parseSlashCommand()` correctly extracts command + args for all forms
|
|
- Unknown commands return `success: false` with descriptive message
|
|
|
|
### Preferences + System Override
|
|
|
|
- `PreferencesService.getEffective()` applies platform defaults when no user overrides
|
|
- Immutable keys (`limits.maxThinkingLevel`, `limits.rateLimit`) cannot be overridden — enforcement always wins
|
|
- `set()` returns error for immutable keys with "platform enforcement" message
|
|
- `SystemOverrideService.set()` stores to Valkey with 5-minute TTL; verified via mock
|
|
- `/system` command calls `SystemOverrideService.set()` with exact text arg
|
|
- `/system` with no args calls `SystemOverrideService.clear()`
|
|
|
|
### Session GC
|
|
|
|
- `collect(sessionId)` deletes all `mosaic:session:<id>:*` Valkey keys
|
|
- `fullCollect()` clears all `mosaic:session:*` keys on cold start
|
|
- `sweepOrphans()` extracts unique session IDs from keys and collects each
|
|
- GC result includes `duration` and `orphanedSessions` count
|
|
- `/gc` command invokes `sweepOrphans(userId)` and returns count in response
|
|
|
|
### Tool Security (path-guard)
|
|
|
|
- `guardPath` rejects `../` traversal → throws `SandboxEscapeError`
|
|
- `guardPath` rejects absolute paths outside sandbox → throws `SandboxEscapeError`
|
|
- `guardPathUnsafe` rejects sibling-named directories (e.g. `/tmp/test-sandbox-evil/`)
|
|
- All 12 path-guard tests pass; `SandboxEscapeError` message includes path and sandbox in text
|
|
|
|
### Workspace
|
|
|
|
- `WorkspaceService.resolvePath()` returns user path for solo projects:
|
|
`$MOSAIC_ROOT/.workspaces/users/<userId>/<projectId>`
|
|
- `WorkspaceService.resolvePath()` returns team path for team projects:
|
|
`$MOSAIC_ROOT/.workspaces/teams/<teamId>/<projectId>`
|
|
- Path resolution is deterministic (same inputs → same output)
|
|
- `exists()`, `createUserRoot()`, `createTeamRoot()` all tested
|
|
|
|
### TUI Autocomplete
|
|
|
|
- `filterCommands(commands, query)` filters by name, aliases, and description
|
|
- Empty query returns all commands
|
|
- Prefix matching works: "mo" → model, "mi" → mission
|
|
- Alias matching: "h" matches help (alias)
|
|
- Description keyword matching: "switch" → model
|
|
- Unknown query returns empty array
|
|
- `useInputHistory` ring buffer caps at 50 entries
|
|
- Up-arrow recall returns most recent entry
|
|
- Down-arrow after up restores saved input
|
|
- Duplicate consecutive entries are deduplicated
|
|
- Reset navigation works correctly
|
|
|
|
### Hot Reload
|
|
|
|
- `ReloadService` registers plugins via `registerPlugin()`
|
|
- `reload()` iterates plugins, calls their `reload()` method
|
|
- Plugin errors are counted but don't prevent other plugins from reloading
|
|
- Non-MosaicPlugin objects are skipped gracefully
|
|
- SIGHUP trigger verified via reload trigger = 'sighup'
|
|
|
|
## Gaps / Known Limitations
|
|
|
|
1. `SystemOverrideService` creates its own Valkey connection in constructor (not injected) — functional but harder to test in isolation without mocking `createQueue`. Current tests mock it at the executor level.
|
|
2. `/status` command has `execution: 'hybrid'` in the gateway registry but `execution: 'local'` in the TUI local registry — TUI local takes precedence, which is the intended behavior.
|
|
3. `SessionGCService.fullCollect()` runs on `onModuleInit` (cold start) — this is intentional but means tests must mock redis.keys to avoid real Valkey calls.
|
|
4. `ProjectBootstrapService` and `TeamsService` in workspace module have no dedicated tests — they are thin wrappers over Drizzle that delegate to WorkspaceService (which is tested).
|
|
5. GC cron schedule (`SESSION_GC_CRON` env var) is configured at module level — not unit tested here; covered by NestJS cron integration.
|
|
6. `filterCommands` in `CommandAutocomplete` is not exported — replicated in integration test to verify behavior.
|
|
|
|
## CI Evidence
|
|
|
|
Pipeline: TBD after push — all 4 local quality gates green:
|
|
|
|
- pnpm typecheck: 32 tasks, all cached/green
|
|
- pnpm lint: 18 tasks, all green
|
|
- pnpm format:check: all files match Prettier style
|
|
- pnpm test: 32 tasks, 160 tests passing
|