Elevate MiroFish/CrowdSight from single-container dev to a SaaS foundation: - Local memory backend (Zep-compatible): memory services/models, local graph builder + updater, AgentActivity seam, import-boundary isolation; Zep stays default, local is opt-in behind MEMORY_BACKEND. Semantic parity not yet proven. - Durable product persistence: projects/simulations/reports schema (migration 0007) + tenant/owner-scoped ProductRepository + dual-write + scoped_project read-first + ArtifactStore abstraction; durable JobQueue + worker.py. - SaaS hardening: durable RateLimiter (wired to login), UsageService (LLM accounting), redacted AuditService, idempotency, CORS allowlist, safe API errors, single-use PasswordResetService + endpoints (covers invite-pending). - Exactly 3 roles (super_admin/admin/user) with tenant authz policy. - Admin UI: GET/POST/PATCH /api/admin/users + GET/PUT /api/admin/settings (super-admin only, encrypted/masked); AdminView.vue + SettingsView.vue with admin/super-admin route guards, th/en i18n. - Production deploy topology: multi-stage Dockerfile (frontend build + gunicorn wsgi + nginx SPA-proxy + supervisord worker), backend/wsgi.py, gunicorn dep. Backend 197 passed; frontend 10 tests + build green. ruff unavailable (gap). No commit of credentials; secrets handled via env/.env.example. Deferred: Zep semantic A/B parity, object storage cutover, mobile QA, EasyPanel container build of deploy topology.
5.0 KiB
5.0 KiB
SaaS Foundation Verification — 2026-08-24
Branch: feat/saas-foundation
Status: in progress; not production-ready; no commit/push/deploy
Milestone
Closed the confirmed TaskManager app-state leak and extended the durable-task tenant boundary. Added a deterministic local end-to-end regression covering graph → entity reader → profiles → simulation config → report tools → persisted report.
Changes
TaskManagernow binds to the current Flask app's session factory at construction time instead of relying on app-factory global singleton state.- In-memory TaskManager fallback remains test/legacy-only and isolated from app-bound managers; an app context without its configured factory now fails closed instead of using the class-global binding.
- Shared SQLite engine creation enables
PRAGMA foreign_keys=ONfor every connection, including migration/test engines. - Durable
get_task()andlist_tasks()accept organization/owner filters and push predicates into SQLAlchemy queries. - Scoped task lookup and graph task listing pass tenant/owner predicates before the defensive metadata check.
- Legacy asynchronous
GraphBuilderServiceaccepts session/tenant context and writes it into durable task metadata; authenticated graph build passes the captured context into the worker adapter. - Added
test_local_graph_profile_simulation_report_is_persistedandtest_graph_builder_async_task_keeps_organization_scope.
Verified evidence
- TDD regression sequence: app-context fallback, SQLite-FK, reused-manager, explicit-mismatch, prebound-manager, background-use, auxiliary API auth/CSRF/idempotency, cross-route idempotency, multipart-content, and local Zep-import-boundary tests were observed RED where applicable, then passed GREEN; the full backend suite passed afterward (123 passed, four Alembic deprecation warnings).
- Schema regression: 16 passed, including migration round-trip/model alignment, SQLite foreign-key enforcement, invalid org/owner rejection, delete actions, cross-app TaskManager binding, and background use without Flask context.
- Auxiliary API regression: 8 passed; all five routes are authenticated, LLM mutations enforce CSRF/idempotency, safe error responses are redacted, and completed mutation responses replay correctly.
- Idempotency regression: 5 passed; the request fingerprint separates method/path/query/body and includes multipart file content hashes across repeated file fields.
- Local consumer-boundary regression: 5 fresh-import tests passed plus the activity-dict boundary regression; fresh subprocess tests prove
app.services.local_graph_memory_updater,SimulationManager,SimulationRunner,ReportAgent, andGraphBuilderServicedo not eagerly loadzep_cloud, while activity conversion uses a neutral shared contract. - Frontend contract tests: 10 passed.
npm run build: passed; existing Vite chunk/dynamic-import warnings remain.PYTHONPATH= uv run python -m compileall -q app tests: passed.- Diff security scan over backend code: no
shell=True,eval/exec, pickle loading, or hardcoded-secret assignment introduced. ruff: unavailable; not counted as a pass.- Browser viewport capture at 320×568/500×768: not verified because Chrome requested an OS remote-debugging permission; no permission dialog was clicked.
Risks / open boundaries
- The bounded local path is deterministic E2E; it does not prove semantic LLM parity or production worker durability.
- Zep remains the default and compatibility runtime; consumer cutover is not complete.
- Dockerfile still runs the development stack; no production WSGI/worker/object-storage topology has been verified.
ruffand real browser viewport QA remain blocked/unavailable.- Fresh independent bounded reviewer
deleg_fb0f80b0: PASS (passed=true, emptysecurity_concernsandlogic_errors) after current-code app/factory mismatch probes for create/get/update/list paths, pooled and NullPool SQLite FK probes, migration round-trip, and ORM/migration parity. Suggestions are non-blocking and recorded inHANDOFF.md. - Fresh independent auxiliary reviewer
deleg_d128494e: PASS (passed=true, emptysecurity_concernsandlogic_errors) after verifying all five routes, decorator ordering, safe errors, and 8/8 targeted tests. Wider SaaS/Zep/deployment review remains open. - Current-tree bounded local import-boundary reviewer
deleg_65bce3de: PASS (passed=true, emptysecurity_concernsandlogic_errors) after running the focused boundary command (6 passed) and isolated probes for five fresh local imports, sharedAgentActivity, explicit Zep paths, thegraph_builder.Zepseam, and subprocess isolation. Suggestions are non-blocking; this closes only the import-boundary slice, not semantic parity or full consumer cutover.
Next exact action
Inspect the non-blocking reviewer suggestions only if they are useful for the next test-hardening pass. The bounded schema/TaskManager slice is approved; keep the broader SaaS/Zep/deployment blockers explicit in HANDOFF.md and continue consumer cutover.