feat: SaaS foundation for CrowdSight
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.
This commit is contained in:
91
docs/HANDOFF.md
Normal file
91
docs/HANDOFF.md
Normal file
@@ -0,0 +1,91 @@
|
||||
# HANDOFF — MiroFish SaaS Migration
|
||||
|
||||
**Repo:** `/Users/kunthawat/Gitea/MiroFish`
|
||||
**Branch:** `feat/saas-foundation`
|
||||
**Base:** `89d04e7`
|
||||
**Status:** SaaS foundation in progress; not production-ready; no commit/push yet
|
||||
|
||||
## Current outcome
|
||||
|
||||
The repository now has a tested identity/authentication foundation, tenant/owner guards around the main graph, simulation, report, and task lookup paths, durable operations schema/jobs/idempotency records, and a bounded local graph → profile → simulation → report persistence path. The original Zep runtime remains the default compatibility backend and legacy file-backed product persistence remains, so this is not yet a completed SaaS migration or production release.
|
||||
|
||||
## Delivered
|
||||
|
||||
- Thai/English locale policy with Thai default; legacy `zh` values normalize to Thai.
|
||||
- Frontend API error sanitization and backend structured error envelopes.
|
||||
- No full JSON request-body logging.
|
||||
- SQLAlchemy models and Alembic migrations for organizations, users, memberships, and revocable sessions.
|
||||
- Argon2 password hashing.
|
||||
- HttpOnly session cookie plus signed CSRF cookie/header validation.
|
||||
- Explicit credentialed CORS allowlist.
|
||||
- Three roles only: `super_admin`, `admin`, `user`.
|
||||
- `/api/auth/login`, `/api/auth/me`, `/api/auth/logout`.
|
||||
- `/api/admin/users` list/create with role escalation prevention and response redaction.
|
||||
- Project `organization_id`/`owner_user_id` metadata and scoped project reads/lists.
|
||||
- Blueprint-wide auth and CSRF checks for simulation/report routes.
|
||||
- Fail-closed scope checks for project, graph, simulation, report, and task IDs in path/query/body.
|
||||
- Cross-tenant regression tests for project/simulation/report/task access.
|
||||
- Frontend auth store using cookie sessions, `/login` view, protected workspace router guard, and localized Thai/English login copy.
|
||||
- Frontend report-status client corrected to POST `{ task_id, simulation_id }`.
|
||||
- Durable local memory tables and a tenant-scoped SQLAlchemy repository.
|
||||
- Durable `jobs`, idempotency records, and audit-log schema with migrations `0004_operations` through `0006_job_metadata`; `TaskManager` binds to the current app/session factory, fails closed when an app context has no factory, and has SQL-level tenant/owner filters.
|
||||
- SQLite engines created by the shared DB helper enable foreign-key enforcement; regression coverage verifies invalid organization/owner references plus `SET NULL`/`CASCADE` delete behavior.
|
||||
- GraphBuilder async adapter now carries organization/session context into durable task metadata.
|
||||
- Deterministic local graph → profile → simulation-config → report-tools → persisted-report regression.
|
||||
- Strict Pydantic extraction schema, bounded prompts, JSON validation, and deterministic LLM-result ingestion service.
|
||||
- Golden parity fixtures and local adapters for entity filtering/context, quick search, panorama temporal inventory, and insight entity/relationship output shapes.
|
||||
- Explicit `MEMORY_BACKEND=local` path now serves simulation entity routes and preparation; `SimulationManager.create_entity_reader()` fails closed when local mode has no injected scoped factory; default remains Zep during migration. Fresh bounded reviewer `deleg_029828b6` independently passed this exact reader/fallback slice with no security concerns or logic errors.
|
||||
- The bounded local panorama/insight/entity-reader parity slice is covered by RED/GREEN tests; conflicting graph IDs fail closed and local simulation activity updater persists tenant-scoped episodes without constructing a Zep client.
|
||||
- API safe-error regression gate covers simulation, graph, report, agent-group, and template boundaries; task/project/report/run state no longer persists raw exception text.
|
||||
|
||||
## Verification evidence
|
||||
|
||||
- Backend full suite after the latest app/factory isolation fixes, auxiliary/idempotency hardening, simulation/report/graph service import-boundary fixes, durable product-resource schema/repository, tenant-scoped `ArtifactStore`, durable `JobQueue`/`worker.py`, versioned redacted `PlatformSettings`, durable `RateLimiter`, durable LLM `UsageService`, durable redacted `AuditService`, and durable single-use `PasswordResetService`: **193 passed** with `PYTHONPATH= uv run pytest -q` from `backend` (2026-08-24); twenty-four Alembic deprecation warnings remain.
|
||||
- Schema regression: **16 passed**, including Alembic upgrade → downgrade `0003_memory` → re-upgrade, operation-table column/index/FK inspection, SQLite PRAGMA enforcement, invalid org/owner rejection, delete actions, cross-app TaskManager binding checks, and background use without Flask context.
|
||||
- Auxiliary API security regression: **8 passed**; every template/agent-group route requires auth, LLM mutations require CSRF + idempotency, safe internal errors are redacted, and both LLM mutations replay completed responses.
|
||||
- Idempotency API regression: **5 passed**; same-route replay/conflict semantics remain, same-key cross-route/body replay is rejected, and multipart file content/repeated-file coverage is verified.
|
||||
- Local consumer-boundary regression: **9 fresh-import tests passed** (`test_local_service_import.py`); fresh local imports of the updater, `SimulationManager`, `SimulationRunner`, `ReportAgent`, and `GraphBuilderService` do not eagerly load `zep_cloud`, `add_activity_from_dict` uses the shared activity contract without importing `zep_cloud` on the local path, and the reviewer-requested regression guards for explicit Zep-path imports, shared `AgentActivity` identity, and the `graph_builder.Zep` seam were added and pass. The activity-dict boundary regression also passes.
|
||||
- Durable product-resource persistence: **21 product-persistence tests passed** (`test_product_persistence.py`). Migration `0007_product_resources` adds tenant/owner-scoped `projects`, `simulations`, and `reports` tables with FK cascade/SET-NULL, indexes, and a migration round-trip + `command.check` (no autogenerate drift). `ProductRepository` provides flush-only tenant-scoped create/get/list for all three with cross-tenant isolation verified. `sync_project`/`sync_simulation`/`sync_report` are idempotent dual-write bridges wired into project-creation, simulation-creation, and report-generation paths; `SimulationState` carries tenant scope and `_save_simulation_state` mirrors every status update into the durable table when a session factory is available, fail-closed when unscoped. `scoped_project` reads the durable table first (mapping back to the legacy `Project` shape) when the local backend is active, then falls back to filesystem, keeping existing routes intact. Read cutover/dual-write are gated to the local backend so the legacy Zep filesystem path is undisturbed.
|
||||
- Tenant-scoped artifact store: **12 artifact-store tests passed** (`test_artifact_store.py`). `ArtifactStore` resolves tenant-scoped paths under a configured root, rejects traversal/absolute components (fail-closed), and exposes a store/read/exists/delete interface (`default_artifact_store()` is filesystem-backed; swapable for object storage later).
|
||||
- Durable job queue + worker core: **10 tests passed** (`test_job_queue.py` 7, `test_worker.py` 3). `JobQueue` claims/executes/completes/fails durable `jobs` with optional organization scoping, a handler-registry dispatch (raises on unhandled operations for the worker to fail), and no double-claim; `worker.py` `run_once`/`run_loop` provide the portable worker entry point (no broker dependency yet).
|
||||
- Versioned, redacted LLM settings: **4 tests passed** (`test_settings_service.py`). Migration `0008_platform_settings` + `SettingsService` store versioned global platform settings; the API key is encrypted with a `SECRET_KEY`-derived Fernet key (never stored/logged in plaintext, never in job snapshots), public reads return a masked value, and `snapshot_for_job()` captures the settings version for reproducible work. `cryptography` added as a direct dependency.
|
||||
- Durable rate limiting: **6 tests passed** (`test_rate_limiter.py`). Migration `0009_rate_limit` + `RateLimiter` implement a durable sliding-window counter (survives restarts, per-key, no secrets), and are wired into the `/api/auth/login` route so excessive attempts return 429 (`api.tooManyAttempts` added in `th`/`en`).
|
||||
- Durable LLM usage accounting: **4 tests passed** (`test_usage_service.py`). Migration `0010_usage_events` + `UsageService` record per-org/user LLM usage (model, input/output tokens, estimated cost) without storing prompt content or secrets, with org-scoped listing and total-cost totals.
|
||||
- Durable redacted audit service: **3 tests passed** (`test_audit_service.py`). `AuditService` records tenant-scoped audit events while stripping sensitive keys (secrets, tokens, api keys, prompts) from details, and is wired into the `/api/auth/login` success path (`auth.login` event).
|
||||
- Durable single-use password reset: **6 tests passed** (`test_password_reset.py`). Migration `0011_password_reset_tokens` + `PasswordResetService` issue expiring, single-use, hashed reset tokens (plaintext never stored), bump `auth_version` on consume to invalidate old sessions, and are exposed as `/api/auth/password-reset/request` + `/api/auth/password-reset/confirm` (enrollment-agnostic request prevents account enumeration; also covers invite-pending password setup).
|
||||
- Frontend contract tests: **10 passed**; `npm run build`: **PASS**; existing chunk-size/dynamic-import warnings remain.
|
||||
- Python `compileall`: **PASS**.
|
||||
- Diff security scan: no introduced `shell=True`, `eval/exec`, or pickle loading in backend code; `git diff --check`: **PASS**.
|
||||
- `ruff` is not installed in the backend environment; lint is skipped, not treated as a pass.
|
||||
- Dev server `/login` smoke response: **PASS**. Real 320×568 and 500×768 browser capture is **not verified** because Chrome requested an OS remote-debugging permission and no permission dialog was clicked.
|
||||
|
||||
## Important incomplete work
|
||||
|
||||
1. **Zep replacement is not complete.** Local memory storage/extraction contracts and a bounded graph → profile → simulation → report persistence regression exist, but semantic LLM parity and full consumer cutover are not complete; Zep remains the default compatibility backend.
|
||||
2. **Product persistence foundation is in place.** Durable tenant/owner-scoped `projects`/`simulations`/`reports` schema (migration `0007_product_resources`), a scoped `ProductRepository`, dual-write for creation and simulation run status, `scoped_project` read-first cutover (local backend), and a tenant-scoped `ArtifactStore` abstraction are added and tested. Legacy `ProjectManager`/`SimulationManager`/`ReportManager` filesystem paths remain authoritative-by-default for the Zep backend and for artifact payloads; full read cutover for simulation/report and migrating artifact payloads to object storage are deferred to the production storage/worker phase.
|
||||
3. **Production worker/queue topology core is in place; broker/storage not decided.** A durable `JobQueue` (claim/dispatch/complete/fail, tenant-scoped) and a portable `worker.py` run-loop entry point exist and are tested, alongside a tenant-scoped `ArtifactStore`. Production decisions remain: choose a broker (Redis/RabbitMQ) or keep polling the durable table, choose object storage, and define the WSGI/worker/supervisord deployment topology. Background-thread execution in routes is not yet moved onto the durable queue.
|
||||
4. **Admin user-management + super-admin settings UI base in place.** Backend `GET/POST/PATCH /api/admin/users` (role/status policy-guarded, status super-admin only) and `GET/PUT /api/admin/settings` (super-admin only, secret encrypted+masked via `SettingsService`). Frontend `AdminView.vue` (user mgmt) and `SettingsView.vue` (LLM settings form) are routed at `/admin` and `/admin/settings` with admin/super-admin role guards and th/en i18n. Invite self-setup UX, a connection-test endpoint, and full i18n/mobile review remain.
|
||||
5. **LLM settings storage base is in place; admin/bootstrap UI not built.** A versioned, encrypted `PlatformSettings` store (`SettingsService` + migration `0008`) exists and is tested. Super-admin settings UI, provider preset validation, and connection-test endpoints are not yet built; active runtime still reads environment variables.
|
||||
6. **Frontend active UI i18n gates pass; legacy `Process.vue` retired.** Frontend contract tests **10 passed** and the production build contains no CJK code points. The unused legacy `Process.vue` (Chinese copy) was retired — the router's `name: 'Process'` uses `MainView.vue`. Real mobile 320×568/500×768 visual capture requires a full-stack + authenticated browser session and is not yet verified; a standalone `vite preview` of the SPA renders a blank mount (needs backend bootstrap), which is not treated as a pass.
|
||||
7. **Docker deployment drafted as production multi-service; container build not yet proven.** Dockerfile is rebuilt as a production multi-service image (multi-stage frontend build, python + gunicorn `wsgi:app`, nginx serving the built SPA with history fallback and `/api`→gunicorn proxy, supervisord running backend + durable worker). `backend/wsgi.py` and `gunicorn>=21` were added. Local smoke test (gunicorn + built-dist static serving) passed — `/health` OK, assets rebuilt — but the nginx SPA-fallback + proxy + worker-poll path must be verified by building the image in EasyPanel; no Docker runtime is available on this Mac.
|
||||
8. **SaaS hardening mostly complete.** Durable rate limiting (wired to login), durable LLM usage/cost accounting, a durable redacted audit service, and durable single-use password reset (which also covers invite-pending setup) are added and tested. Still open: a complete resource authorization matrix across every role × endpoint, a full retryable-mutation inventory, and long-running-job review.
|
||||
9. **Auxiliary API hardening is verified.** Fresh reviewer `deleg_d128494e` returned `passed=true` with empty `security_concerns` and `logic_errors`; current auxiliary security tests pass **8/8**. This closes only the template/agent-group slice, not the broader SaaS gate.
|
||||
10. **Bounded schema/TaskManager review passed.** Fresh reviewer `deleg_fb0f80b0` returned `passed=true` with empty `security_concerns` and `logic_errors` after current-code runtime app/factory mismatch probes, SQLite pooled/NullPool foreign-key checks, ORM/migration parity, and migration round-trip checks. Its suggestions are non-blocking: expand mismatch coverage across every TaskManager operation, broaden pooled/NullPool assertions in tests, and assert full ORM/migration signatures. This closes only the exact schema/TaskManager slice; broad reviewer `deleg_949ab2f4` timed out and the wider SaaS gate remains open.
|
||||
11. **The current bounded local import-boundary review passed.** Fresh reviewer `deleg_65bce3de` returned `passed=true` with empty `security_concerns` and `logic_errors`; it ran the focused boundary command with **6 passed** and isolated probes for five fresh local imports, shared `AgentActivity`, explicit Zep paths, the `graph_builder.Zep` seam, and subprocess isolation. This closes only the bounded import-boundary slice; semantic parity, complete consumer cutover, and the wider SaaS gate remain open.
|
||||
|
||||
## Next implementation order
|
||||
|
||||
1. Keep the current staged snapshot and re-run verification after any further edits.
|
||||
2. Extend golden parity and tenant-negative coverage to the remaining local consumers and simulation-context/report E2E path.
|
||||
3. Switch graph builder, entity reader, tools, profile generator, and report agent to the local memory adapter.
|
||||
4. Remove Zep dependency/config only after parity and migration tests pass.
|
||||
5. Add admin/super-admin UI and settings policy.
|
||||
6. Finish production worker/deployment hardening and run a fresh end-to-end tenant matrix.
|
||||
|
||||
## Guardrails
|
||||
|
||||
- Exactly three role identifiers: `super_admin`, `admin`, `user`.
|
||||
- Browser never receives LLM/API secrets.
|
||||
- Backend authorization is authoritative; frontend guards are convenience only.
|
||||
- Do not claim semantic LLM/Zep parity without golden-fixture evidence.
|
||||
- Do not commit or deploy while the independent review gate remains unresolved.
|
||||
32
docs/engineering-log.md
Normal file
32
docs/engineering-log.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Engineering Log
|
||||
|
||||
## Current status
|
||||
|
||||
| Milestone | Status | Last verified | Evidence | Next action |
|
||||
|---|---|---|---|---|
|
||||
| Baseline architecture study | complete | 2026-08-23 | `npm run build` passed; `compileall` passed; `git diff --check` passed; source inventory completed | Review MiroFish SaaS plan and lock M0 decisions |
|
||||
| Thai/English frontend hardening | bounded active-surface gate | 2026-08-24 | Frontend contract tests **10 passed**; production build passed; active routed UI has no hardcoded CJK visible strings; legacy unused `Process.vue` (Chinese copy) retired — router uses `MainView.vue`; mobile viewport capture not yet verified | Migrate remaining legacy views and perform real 320×568 and 500×768 capture when browser permission is available |
|
||||
| Zep replacement | bounded local E2E slice | 2026-08-24 | Local graph → profile → simulation config → report tools → persisted report regression passed; default remains Zep; no full consumer cutover or semantic parity claim | Cut over remaining consumers and close semantic/E2E gaps |
|
||||
| Auth/tenant/roles | bounded foundation | 2026-08-24 | Identity/session/roles/CSRF/CORS/idempotency/resource guards covered by focused tests; durable task app-state leak fixed; task query filters now push tenant predicates into SQL | Complete broader tenant matrix, admin UI, rate limits, audit/usage policy |
|
||||
| SaaS foundation batch | in progress | 2026-08-24 | Backend full suite **193 passed** after app/factory isolation, SQLite-FK, auxiliary API auth/CSRF/idempotency, cross-route/multipart idempotency, local consumer-boundary fixes, durable product-resource schema/repository, tenant-scoped `ArtifactStore`, durable `JobQueue`+`worker.py`, versioned redacted `PlatformSettings`, durable `RateLimiter` (wired to login), durable LLM `UsageService`, durable redacted `AuditService`, and durable single-use `PasswordResetService` + endpoints (also covers invite-pending setup); schema/TaskManager regression **16 passed**; auxiliary security **8 passed**; idempotency API **5 passed**; local import-boundary regression **9 fresh-import tests**; product-resource persistence **21 tests**; artifact store **12 tests**; job queue/worker **10 tests**; settings service **4 tests**; rate limiter **6 tests**; usage service **4 tests**; audit service **3 tests**; password reset **6 tests**; frontend gates passed; bounded reviewers passed their exact slices; hardened bases ready; remaining: resource authz matrix completion, admin/bootstrap UI, and deploy topology; `ruff` unavailable; no commit/push/deploy | Complete admin UI, authz matrix, then deploy topology; do not claim full-system approval |
|
||||
| Admin/super-admin UI | bounded foundation | 2026-08-24 | Backend: `GET/POST/PATCH /api/admin/users` + `GET/PUT /api/admin/settings` (super-admin only, masked/encrypted secret via `SettingsService`); Frontend: `AdminView.vue` (user mgmt) + `SettingsView.vue` (LLM settings form) routed at `/admin` + `/admin/settings` with admin/super-admin role guards, th/en i18n identical; build + 10 frontend tests pass; backend 197 passed | Add invite self-setup UX, connection-test endpoint, then full i18n/mobile review |
|
||||
| Production worker/deployment | production topology drafted, locally smoke-tested | 2026-08-24 | Dockerfile rebuilt as multi-stage production (frontend build + python-gunicorn + nginx-SPA-proxy + supervisord worker); `backend/wsgi.py` gunicorn entry + `gunicorn>=21` added; local smoke test: gunicorn `wsgi:app` started, `/health` OK, `/api/auth/login` 401, built SPA assets served 200; backend 197 passed | Build in EasyPanel container to verify nginx SPA-fallback + `/api` proxy + worker poll; choose broker (Redis vs durable-poll) + object storage for full readiness |
|
||||
|
||||
## Guardrails
|
||||
|
||||
- No production implementation was changed during the architecture study.
|
||||
- Keep exactly three role identifiers: `super_admin`, `admin`, `user`.
|
||||
- Never expose LLM/API secrets to the browser or commit them to docs/logs.
|
||||
- Backend authorization and tenant scope are authoritative; frontend visibility is not security.
|
||||
- Do not claim LLM/Zep semantic parity without golden-fixture evidence.
|
||||
|
||||
## Entry index
|
||||
|
||||
- `docs/engineering-log/2026-08-23-architecture-study.md`
|
||||
- `docs/test-evidence/2026-08-23-baseline.md`
|
||||
- `.hermes/plans/2026-08-23_110451-mirofish-saas-migration.md`
|
||||
- `docs/engineering-log/2026-08-24-simulation-memory-fallback.md`
|
||||
- Independent SaaS audit findings were incorporated after the initial plan: raw log redaction, ID/path confinement, idempotency, upload drafts and API contract tests.
|
||||
- Safe-error audit now covers API response/persisted task state paths; raw exception/traceback values are replaced with generic localized errors and error-type-only server logs.
|
||||
- `docs/engineering-log/2026-08-24-memory-parity-panorama-insight.md` records the bounded parity implementation, verification evidence, review verdict, and remaining semantic/E2E boundary.
|
||||
- `docs/engineering-log/2026-08-24-saas-foundation-verification.md`
|
||||
42
docs/engineering-log/2026-08-23-architecture-study.md
Normal file
42
docs/engineering-log/2026-08-23-architecture-study.md
Normal file
@@ -0,0 +1,42 @@
|
||||
# Architecture Study — MiroFish SaaS Migration
|
||||
|
||||
**Date:** 2026-08-23 (+07:00)
|
||||
**Repository:** `/Users/kunthawat/Gitea/MiroFish`
|
||||
**HEAD:** `89d04e7`
|
||||
**Status:** complete — plan only; no production source changes
|
||||
|
||||
## Scope
|
||||
|
||||
Reviewed frontend i18n/rendering, backend API/entry points, Zep integration, LLM prompt paths, persistence, auth/security posture, role/tenant requirements and Docker runtime.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Working tree was clean at start and after baseline verification.
|
||||
- `npm run build` passed; Vite emitted chunk-size and `pendingUpload.js` import warnings.
|
||||
- `python3 -m compileall -q backend/app backend/run.py backend/scripts` passed.
|
||||
- `git diff --check` passed.
|
||||
- Backend route inventory: 64 routes; no auth middleware found.
|
||||
- Frontend locale inventory: `en` and `th` have matching 648 flattened keys; `zh` is still present and is the fallback/default path.
|
||||
- Current built frontend still contains CJK code points in generated HTML/JS.
|
||||
- Zep usage was traced through graph build, entity reader, search tools, graph memory updater, profile generation and ReportAgent.
|
||||
- Current project/task/simulation/report state is mostly filesystem/in-memory scoped globally, not by user or organization.
|
||||
- Independent SaaS audit confirmed additional risks: raw request-body logging, raw exception/path exposure, non-idempotent frontend retries, global history/debug routes, process-local pending uploads and a report-status HTTP method/body mismatch.
|
||||
|
||||
## Decisions captured in plan
|
||||
|
||||
- Use PostgreSQL as SaaS system of record; add tenant scope from v1.
|
||||
- Use LLM for structured memory extraction/merge/summary/query reasoning, not for durable storage or unbounded search.
|
||||
- Preserve internal memory result contracts while replacing Zep implementation.
|
||||
- Use exactly `super_admin`, `admin`, `user` with backend-enforced policy.
|
||||
- Default product locale recommendation: Thai, with English switcher.
|
||||
- Move long-running work to durable workers before calling the system production SaaS-ready.
|
||||
|
||||
## Risks/open decisions
|
||||
|
||||
- Need user decision on whether user-uploaded Chinese content must also be translated/hidden.
|
||||
- Need user decision on multi-organization scope, invite flow, Zep data migration and LLM settings scope.
|
||||
- Python runtime on the current host is 3.14 while project metadata requires Python 3.11–3.12; compile success is not full runtime proof.
|
||||
|
||||
## Exact next action
|
||||
|
||||
Get M0 answers, then create PR-sized implementation batches starting with persistence/auth contracts and locale/API error contracts. Do not begin by deleting Zep files without first implementing the local memory interface and storage schema.
|
||||
@@ -0,0 +1,28 @@
|
||||
# Local Memory First Consumer — 2026-08-23
|
||||
|
||||
## Scope
|
||||
|
||||
Enabled the first explicit local-memory execution path without silently changing the existing Zep default.
|
||||
|
||||
## Changes
|
||||
|
||||
- Added `MEMORY_BACKEND=zep|local`; unknown values fail closed.
|
||||
- Added per-request local entity-reader factory scoped by `organization_id` and `graph_id`.
|
||||
- Added worker-owned SQLAlchemy session lifecycle for local readers.
|
||||
- `SimulationManager` now accepts `entity_reader_factory` and uses it during preparation.
|
||||
- `/api/simulation/entities/*` selects the configured reader and closes it after each request.
|
||||
- `/api/simulation/prepare` uses the configured reader for preview and background preparation.
|
||||
- `OasisProfileGenerator` supports `use_zep_context=False`; local mode does not initialize Zep context for profile enrichment.
|
||||
- Default remains `MEMORY_BACKEND=zep` until remaining consumers and data migration are ready.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Switch/route tests: 3 passed.
|
||||
- Reader factory/entity parity/injection tests: passed.
|
||||
- Full backend suite after this slice: **55 passed**.
|
||||
- Frontend suite: **10 passed**.
|
||||
- Build, compileall, and Alembic upgrade/check: passed.
|
||||
|
||||
## Boundary
|
||||
|
||||
This is not a full Zep removal. Profile generation still uses the legacy flow outside the explicit local preparation path, and graph tools/report consumers still need migration.
|
||||
32
docs/engineering-log/2026-08-23-local-memory-foundation.md
Normal file
32
docs/engineering-log/2026-08-23-local-memory-foundation.md
Normal file
@@ -0,0 +1,32 @@
|
||||
# Local Memory Foundation — 2026-08-23
|
||||
|
||||
## Scope
|
||||
|
||||
Started the Zep replacement without switching existing consumers yet. The goal of this slice is to separate LLM semantic extraction from durable storage, search, and authorization.
|
||||
|
||||
## Delivered
|
||||
|
||||
- SQLAlchemy memory tables: `memory_graphs`, `memory_episodes`, `memory_nodes`, `memory_edges`.
|
||||
- Alembic revision `0003_memory` with SQLite upgrade/check parity.
|
||||
- `SqlAlchemyMemoryRepository` constrained by `organization_id` + `graph_id`.
|
||||
- Deterministic node normalization, confidence bounds, graph/node scope checks, and bounded search.
|
||||
- Golden parity fixture and local adapters for entity filtering/context and quick search output shapes.
|
||||
- Strict Pydantic extraction models with `extra="forbid"`.
|
||||
- Bounded Thai/English extraction prompt builder.
|
||||
- JSON/code-fence parser with validation.
|
||||
- `MemoryExtractionService` that calls a JSON LLM client, validates output, resolves entity references deterministically, and persists only through the scoped repository.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Memory repository tests: 2 passed.
|
||||
- Extraction contract tests: 3 passed.
|
||||
- Memory service test: 1 passed.
|
||||
- Full backend suite: 47 passed.
|
||||
- Frontend contract suite: 10 passed.
|
||||
- Vite build: pass.
|
||||
- Python compileall: pass.
|
||||
- Alembic revisions `0001_identity` → `0003_memory` upgrade/check: pass.
|
||||
|
||||
## Deliberate non-change
|
||||
|
||||
Existing `GraphBuilderService`, `ZepEntityReader`, `ZepToolsService`, `OasisProfileGenerator`, and `ReportAgent` still use Zep. Consumer cutover is deferred until golden parity fixtures exist for entity reads, search, temporal edges, and simulation context.
|
||||
@@ -0,0 +1,46 @@
|
||||
# SaaS Foundation Continuation — 2026-08-23
|
||||
|
||||
## Scope
|
||||
|
||||
Continued implementation after reviewer delegation infrastructure failed to return a verdict.
|
||||
|
||||
## Changes
|
||||
|
||||
- Added blueprint-wide authentication and CSRF enforcement to simulation/report APIs.
|
||||
- Added fail-closed resource helpers for project, graph, simulation, report, and task IDs.
|
||||
- Scoped simulation/report listing and ownership checks to the authenticated actor.
|
||||
- Added task metadata ownership for report-generation jobs and blocked cross-tenant task status reads.
|
||||
- Added local `ApiError` handlers for simulation/report blueprints; removed the frozen exception behavior that caused `FrozenInstanceError` while unwinding a session context.
|
||||
- Corrected frontend report-status method/body contract.
|
||||
- Added cookie-session auth store, public login route, protected workspace router guard, and localized login view.
|
||||
|
||||
## Evidence
|
||||
|
||||
- Resource auth tests: 3 passed.
|
||||
- Full backend suite: 41 passed.
|
||||
- Frontend contract suite: 10 passed.
|
||||
- Vite build: pass; existing chunk/import warnings remain.
|
||||
- Python compileall: pass.
|
||||
- Alembic SQLite upgrade/check: pass.
|
||||
- Staged diff whitespace check: pass after staging the continuation changes.
|
||||
|
||||
## Prevention notes
|
||||
|
||||
- Any route that accepts a resource ID must pass through a shared scope helper before touching filesystem or in-memory state.
|
||||
- Any task exposed through an API must carry `organization_id` and `owner_user_id`; legacy tasks without metadata fail closed.
|
||||
- `ApiError` must remain a normal mutable exception class; frozen dataclasses cannot safely unwind through context managers because Python assigns traceback fields.
|
||||
- Frontend API contract tests should assert HTTP method and request body against the backend route, not only function existence.
|
||||
|
||||
## Remaining blocker
|
||||
|
||||
Independent reviewer delegation exited before returning the required JSON verdict. This is recorded as `VERIFICATION BLOCKED`, not as an approval or security finding.
|
||||
|
||||
## Follow-up — local cutover and safe-error gate
|
||||
|
||||
- Added local graph activity updater coverage and graph → profile → report golden contract coverage.
|
||||
- Replaced raw exception text in API response paths and persisted simulation/report/project/task/run state with generic localized errors; server logs retain only operation, identifier, exit code where needed, and exception type.
|
||||
- Added `backend/tests/test_api_no_raw_exception_details.py` as a source-level regression gate.
|
||||
- Verification: backend **67 passed**, frontend Node contract tests **10 passed**, Vite build **PASS**, Python compileall **PASS**, `git diff --check` **PASS**.
|
||||
- `npm test` was attempted but the repository has no `test` script; the supported `node --test tests/*.mjs` command passed.
|
||||
- Reviewer status remains `VERIFICATION BLOCKED` until the fresh independent reviewer returns a readable schema-valid terminal verdict.
|
||||
- `deleg_289b16ce` also timed out without a terminal verdict after broad read-only inspection. Its intermediate targeted-test failure was not reproducible on the current tree: `tests/test_api_no_raw_exception_details.py` reran **1 passed**. Reviewer `deleg_e0e7bac1` was narrowed via steering and remains the active pending gate.
|
||||
35
docs/engineering-log/2026-08-24-graph-scope-review.md
Normal file
35
docs/engineering-log/2026-08-24-graph-scope-review.md
Normal file
@@ -0,0 +1,35 @@
|
||||
# Graph Scope Review — 2026-08-24
|
||||
|
||||
## Scope
|
||||
Closed the two blockers from the earlier independent review:
|
||||
|
||||
1. Project deletion must be organization-scoped and owner-scoped for regular users.
|
||||
2. Graph read/delete routes must use the local adapter when `MEMORY_BACKEND=local`, without constructing or silently falling back to Zep.
|
||||
|
||||
## Changes verified
|
||||
|
||||
- `backend/app/api/graph.py` resolves the authenticated actor and routes project/graph access through scoped helpers.
|
||||
- `backend/app/models/project.py` requires organization scope for project deletion and applies the user-owner restriction where applicable.
|
||||
- `backend/app/services/local_graph_builder.py` requires organization and project scope and passes graph operations to the tenant-scoped repository.
|
||||
- `backend/tests/test_graph_backend_routes.py` covers backend dispatch, cross-tenant project/graph behavior, and local graph deletion with nodes and an edge.
|
||||
|
||||
## Verification evidence
|
||||
|
||||
- Focused regression set: **15 passed**.
|
||||
- Full backend suite: **72 passed**.
|
||||
- Frontend contract tests: **10 passed**.
|
||||
- Frontend production build: **PASS**; existing chunk-size/dynamic-import warnings remain.
|
||||
- Python `compileall`: **PASS**.
|
||||
- Added-lines security scan: **0 findings** for hardcoded secrets, shell injection, eval/exec, unsafe pickle, and interpolated SQL patterns.
|
||||
- `git diff --check`: **PASS**.
|
||||
- `ruff`: unavailable in the current backend environment; no lint verdict claimed.
|
||||
|
||||
## Review gate
|
||||
|
||||
Independent review attempts before this entry did not return readable terminal JSON and therefore were fail-closed. Fresh narrow read-only reviewer `deleg_dec1c4b0` then returned the required exact five-key JSON with `passed=true`, empty `security_concerns`, and empty `logic_errors` after reviewing all seven target files. The bounded blocker gate is closed. This does not claim approval, parity completion, production readiness, or full Zep removal.
|
||||
|
||||
## Remaining work
|
||||
|
||||
- The bounded graph/project/local-memory review gate is closed; continue the staged local-memory consumer cutover and durable resource work.
|
||||
- Broader migration review, parity, production hardening, and end-to-end tenant matrix remain open.
|
||||
- No commit, push, or deploy was made during this verification.
|
||||
@@ -0,0 +1,40 @@
|
||||
# Local Memory Parity Slice — 2026-08-24
|
||||
|
||||
## Scope
|
||||
|
||||
Bounded local-memory compatibility work only:
|
||||
|
||||
- `LocalMemoryTools.panorama_search` now preserves the legacy all-graph inventory contract while relevance-ranking active and historical facts.
|
||||
- `LocalMemoryTools.insight_forge` now derives entity insights and relationship chains from matching edges with the legacy-compatible output shape.
|
||||
- `LocalEntityReader` rejects explicitly supplied graph IDs that differ from its repository-bound graph ID.
|
||||
- `MEMORY_BACKEND=zep` remains the default compatibility path; this slice does not remove Zep or cut over ReportAgent/ProfileGenerator globally.
|
||||
|
||||
## TDD evidence
|
||||
|
||||
1. Added `backend/tests/fixtures/memory_parity/tools_fixture.json` with three entities, current and invalidated temporal edges, and one unrelated active edge.
|
||||
2. Added `backend/tests/test_memory_parity.py` covering panorama inventory/temporal behavior, insight entity/relationship shape, and five conflicting graph-ID call forms.
|
||||
3. Ran the new tests before the production change; the suite failed for the expected missing parity and scope behaviors.
|
||||
4. Implemented the minimal adapter and reader changes.
|
||||
5. Re-ran the focused parity suite: **8 passed**.
|
||||
|
||||
## Verification
|
||||
|
||||
- Focused consumer regression set: **17 passed**.
|
||||
- Backend full suite: **81 passed**.
|
||||
- Python `compileall`: **PASS**.
|
||||
- `git diff --check`: **PASS**.
|
||||
- Added-lines security scan: **0 findings**.
|
||||
- `ruff`: unavailable in the backend environment; no lint pass is claimed.
|
||||
- A probe run from repository root failed because that cwd cannot resolve the backend pytest executable; the same probe rerun from `backend/` passed 8/8. This is a command-location issue, not a test failure.
|
||||
|
||||
## Independent review gate
|
||||
|
||||
- The first bounded reviewer was stopped after exceeding the context/time budget; its silence was not treated as approval.
|
||||
- Replacement reviewer: `deleg_73fac0de`.
|
||||
- Required five-key JSON verdict: `passed=true`, `security_concerns=[]`, `logic_errors=[]`.
|
||||
- Non-blocking suggestion: consider future LLM query decomposition for `insight_forge`; local `sub_queries=[query]` remains an explicit semantic gap outside this slice.
|
||||
- This closes the review gate for this slice only. It is not approval of full semantic parity, the broader migration, or production readiness.
|
||||
|
||||
## Remaining boundary
|
||||
|
||||
This proves only the bounded panorama/insight/entity-reader contract. It does not prove semantic LLM parity (`sub_queries` remains local `[query]`), complete graph/profile/simulation/report E2E, all local consumer cutovers, migration parity, or production readiness.
|
||||
@@ -0,0 +1,46 @@
|
||||
# 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
|
||||
|
||||
- `TaskManager` now 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=ON` for every connection, including migration/test engines.
|
||||
- Durable `get_task()` and `list_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 `GraphBuilderService` accepts 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_persisted` and `test_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`, and `GraphBuilderService` do not eagerly load `zep_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.
|
||||
- `ruff` and real browser viewport QA remain blocked/unavailable.
|
||||
- Fresh independent bounded reviewer `deleg_fb0f80b0`: **PASS** (`passed=true`, empty `security_concerns` and `logic_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 in `HANDOFF.md`.
|
||||
- Fresh independent auxiliary reviewer `deleg_d128494e`: **PASS** (`passed=true`, empty `security_concerns` and `logic_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`, empty `security_concerns` and `logic_errors`) after running the focused boundary command (**6 passed**) and isolated probes for five fresh local imports, shared `AgentActivity`, explicit Zep paths, the `graph_builder.Zep` seam, 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.
|
||||
@@ -0,0 +1,53 @@
|
||||
# Simulation Memory Fallback Hardening — 2026-08-24
|
||||
|
||||
## Scope
|
||||
|
||||
Close one local-memory safety gap without changing the migration decision:
|
||||
|
||||
- `MEMORY_BACKEND=zep` remains the default compatibility path.
|
||||
- `MEMORY_BACKEND=local` must use an explicitly injected, request-scoped entity-reader factory.
|
||||
- Local mode must not silently construct `ZepEntityReader` when the factory is absent.
|
||||
|
||||
## TDD evidence
|
||||
|
||||
1. Added `test_local_backend_refuses_unscoped_zep_reader_fallback` to
|
||||
`backend/tests/test_simulation_memory_injection.py`.
|
||||
2. The test failed against the pre-change fallback behavior.
|
||||
3. `SimulationManager.create_entity_reader()` now raises the stable internal
|
||||
sentinel `local_entity_reader_factory_required` in local mode when no
|
||||
factory is injected.
|
||||
4. Explicit factory injection remains unchanged.
|
||||
|
||||
## Verification
|
||||
|
||||
- Local-memory regression slice: **11 passed**.
|
||||
- Backend full suite: **73 passed**.
|
||||
- Frontend contract tests: **10 passed**.
|
||||
- Frontend production build: **PASS**; existing chunk-size/dynamic-import warnings remain.
|
||||
- Python `compileall`: **PASS**.
|
||||
- `git diff --check`: **PASS**.
|
||||
- Target-slice added-lines security scan: **0 findings**.
|
||||
- Full-diff scan matched two password patterns in test-only fixtures; no production credential was identified by that scan.
|
||||
- `ruff` is unavailable in the current backend environment; no lint pass is claimed.
|
||||
|
||||
## Independent review gate
|
||||
|
||||
- Fresh read-only reviewer: `deleg_029828b6`.
|
||||
- Scope was limited to `SimulationManager.create_entity_reader()` and
|
||||
`test_local_backend_refuses_unscoped_zep_reader_fallback()`.
|
||||
- Required five-key verdict: `passed=true`, `security_concerns=[]`,
|
||||
`logic_errors=[]`.
|
||||
- Reviewer-reported runtime spy: local mode made **0** Zep constructor calls;
|
||||
the default path made **1** Zep constructor call.
|
||||
- Reviewer suggestions are non-blocking: add explicit constructor-spy
|
||||
regression coverage for the local no-call path and the legacy/default path.
|
||||
They are deferred to a later test-hardening pass; no production behavior gap
|
||||
was found in this bounded slice.
|
||||
- This closes the review gate for this slice only. It is not approval of the
|
||||
full migration, semantic parity, or production readiness.
|
||||
|
||||
## Remaining migration boundary
|
||||
|
||||
This is a fail-closed guard, not Zep removal or semantic parity proof. Remaining
|
||||
work includes complete local consumer cutover, parity fixtures, durable resource
|
||||
persistence, production workers/object storage, and the full tenant E2E matrix.
|
||||
27
docs/test-evidence/2026-08-23-baseline.md
Normal file
27
docs/test-evidence/2026-08-23-baseline.md
Normal file
@@ -0,0 +1,27 @@
|
||||
# Baseline Test Evidence — 2026-08-23
|
||||
|
||||
**Repo:** `/Users/kunthawat/Gitea/MiroFish`
|
||||
**HEAD:** `89d04e7`
|
||||
**Purpose:** architecture-study baseline; no production implementation changes
|
||||
|
||||
## Commands
|
||||
|
||||
| Command | Result | Notes |
|
||||
|---|---|---|
|
||||
| `git status --short --branch` | PASS | clean at baseline; plan/log artifacts were added after study |
|
||||
| `git diff --check` | PASS | no whitespace errors |
|
||||
| `npm run build` | PASS | Vite build succeeded; chunk-size warning and dynamic/static import warning remain |
|
||||
| `python3 -m compileall -q backend/app backend/run.py backend/scripts` | PASS | syntax-only; host Python is 3.14, project requires `<3.13` |
|
||||
|
||||
## Not yet run
|
||||
|
||||
- Backend runtime tests (no project test suite/runner configured for the application)
|
||||
- Authentication/tenant isolation tests (not implemented)
|
||||
- Zep replacement parity tests (not implemented)
|
||||
- Thai/English UI visual/E2E tests
|
||||
- Production Docker/worker/restart tests
|
||||
- CJK-free build gate (currently expected to fail)
|
||||
|
||||
## Verification rule
|
||||
|
||||
A future milestone is not complete from code presence alone. Attach command output, relevant fixture counts, and role/locale/worker evidence here or in a dated evidence file before marking it complete.
|
||||
50
docs/test-evidence/2026-08-23-saas-foundation.md
Normal file
50
docs/test-evidence/2026-08-23-saas-foundation.md
Normal file
@@ -0,0 +1,50 @@
|
||||
# SaaS Foundation Batch Evidence — 2026-08-23
|
||||
|
||||
**Branch:** `feat/saas-foundation`
|
||||
**Scope:** locale/error foundation, identity schema/session, auth/admin API, project/graph/simulation/report/task scope, frontend login guard, report API contract, local memory schema/repository, strict LLM extraction contract
|
||||
**Status:** in progress — reviewer gate blocked by delegation owner exits; simulation/report/task scope and login foundation are now implemented, but Zep replacement and durable SaaS storage are not complete
|
||||
|
||||
## Verified commands
|
||||
|
||||
| Command | Result |
|
||||
|---|---|
|
||||
| `PYTHONPATH=backend uv run --project backend --python 3.11 pytest backend/tests -q` | **55 passed** |
|
||||
| `node --test frontend/tests/i18n-contract.test.mjs frontend/tests/api-error.test.mjs frontend/tests/report-api-contract.test.mjs frontend/tests/auth-contract.test.mjs` | **10 passed** |
|
||||
| `npm run build` | **PASS**; existing chunk-size and pendingUpload import warnings remain |
|
||||
| `python3.11 -m compileall -q backend/app backend/migrations` | **PASS** |
|
||||
| `alembic upgrade head` on temporary SQLite database | **PASS**; revisions `0001_identity`, `0002_sessions` applied |
|
||||
| `alembic check` on temporary SQLite database | **PASS**; no new upgrade operations |
|
||||
| `git diff --cached --check` via subprocess | **PASS** |
|
||||
| text-artifact CJK gate | **PASS**; binary icon excluded and visually checked as graphic-only |
|
||||
|
||||
## Delivered production surfaces
|
||||
|
||||
- `th/en` locale policy; legacy `zh` normalizes to Thai
|
||||
- structured API errors and raw request-body redaction
|
||||
- SQLAlchemy identity/organization/membership/session models
|
||||
- Alembic migrations
|
||||
- Argon2 password hashing
|
||||
- HttpOnly session cookie + signed CSRF cookie/header check
|
||||
- `/api/auth/login`, `/api/auth/me`, `/api/auth/logout`
|
||||
- `/api/admin/users` GET/POST with role policy and redacted serializer
|
||||
- tenant/owner fields and scoped lookup/list for projects
|
||||
- auth + owner scope on graph project get/list/delete/reset and graph build/ontology entry points
|
||||
- blueprint-wide auth + CSRF enforcement on simulation/report APIs
|
||||
- fail-closed project/simulation/report/task scope checks for IDs in paths, query strings, and JSON bodies
|
||||
- frontend auth store, `/login` route, protected workspace route guard, and localized login view
|
||||
- report-status frontend client corrected to POST `{ task_id, simulation_id }`
|
||||
- durable local memory tables (`memory_graphs`, `memory_episodes`, `memory_nodes`, `memory_edges`) and tenant-scoped SQLAlchemy repository
|
||||
- strict Pydantic LLM extraction schema, bounded prompt builder, JSON parser, and deterministic extraction-to-repository service
|
||||
- golden parity fixture for `LocalEntityReader` and `LocalMemoryTools.quick_search` against the legacy entity/search output shapes
|
||||
- explicit `MEMORY_BACKEND=local` switch for simulation entity routes and preparation; default remains `zep` until migration completes
|
||||
- `SimulationManager` entity-reader injection and worker-owned local-reader session lifecycle
|
||||
- explicit CORS allowlist with credential support
|
||||
|
||||
## Known incomplete surfaces
|
||||
|
||||
- Existing product resources still use filesystem/in-memory persistence; no durable simulation/report/job tables yet.
|
||||
- Legacy route handlers still return raw `str(e)` in some untouched modules.
|
||||
- No admin user-management UI or super-admin settings UI yet.
|
||||
- No durable worker queue or object-storage artifact layer.
|
||||
- Zep dependency/runtime remains; local memory schema/contract exists, but GraphBuilder/ProfileGenerator/ReportAgent consumers have not been switched.
|
||||
- Independent reviewer verdict is blocked: delegation owners exited before recording JSON results.
|
||||
@@ -0,0 +1,42 @@
|
||||
# SaaS Foundation Verification Evidence — 2026-08-24
|
||||
|
||||
**Repo:** `/Users/kunthawat/Gitea/MiroFish`
|
||||
**Branch:** `feat/saas-foundation`
|
||||
**Status:** verified bounded changes; broader migration remains in progress
|
||||
|
||||
## Commands and results
|
||||
|
||||
| Command | Result |
|
||||
|---|---|
|
||||
| `PYTHONPATH= uv run pytest -q` from `backend` after app/factory isolation, auxiliary/idempotency hardening, and local consumer boundary fixes | **123 passed**; four Alembic deprecation warnings |
|
||||
| Schema regression (`tests/test_saas_operations_schema.py` + TaskManager isolation tests) | **16 passed** on the current tree; migration round-trip/model alignment, PRAGMA/FK/delete, cross-app binding, and background-use coverage |
|
||||
| Auxiliary API security regression | **8 passed**; all five auxiliary routes require authentication, LLM mutations require CSRF/idempotency, safe errors are redacted, and both mutation replays are verified |
|
||||
| Idempotency API regression | **5 passed**; same-route replay/conflict semantics, cross-route/body fingerprint separation, multipart content hashing, and repeated-file coverage are verified |
|
||||
| Local memory/Zep import-boundary regression (`tests/test_local_service_import.py` + `tests/test_local_zep_boundary.py`) | **5 fresh-import tests passed** plus the activity-dict boundary regression; fresh subprocesses import the local updater, `SimulationManager`, `SimulationRunner`, `ReportAgent`, and `GraphBuilderService` without loading `zep_cloud` |
|
||||
| `node --test tests/*.mjs` from `frontend` | **10 passed** |
|
||||
| `npm run build` from `frontend` | **PASS**; Vite warns about `pendingUpload` dynamic/static imports and one chunk over 500 kB |
|
||||
| `PYTHONPATH= uv run python -m compileall -q app tests` | **PASS** |
|
||||
| Temporary SQLite Alembic upgrade → downgrade `0003_memory` → upgrade head → `alembic check` | **PASS** |
|
||||
| Diff-only security scan over first-party backend runtime code | **0 hits** for `shell=True`, `eval/exec`, pickle loading, or hardcoded-secret assignment |
|
||||
| `ruff --version` | **unavailable**; not a pass |
|
||||
| Dev server `curl http://127.0.0.1:3000/login` | **PASS** |
|
||||
| Independent bounded schema/TaskManager review `deleg_fb0f80b0` | **PASS**; reviewer snapshot ran 15 tests with `passed=true` and empty blocking arrays; the current tree rerun is **16 passed**. Current-code app/factory, SQLite FK, migration round-trip, and ORM/migration parity probes passed. Non-blocking suggestions recorded in `HANDOFF.md`; this is not full-system approval |
|
||||
| Independent auxiliary API review `deleg_d128494e` | **PASS**; `passed=true`, empty `security_concerns` and `logic_errors`; current auxiliary security regression is **8 passed**. This is only the template/agent-group slice, not full-system approval |
|
||||
| Independent current-tree local import-boundary review `deleg_65bce3de` | **PASS**; `passed=true`, empty blocking arrays; reviewer ran the focused boundary command (**6 passed**) and isolated probes for five fresh local imports, shared `AgentActivity`, explicit Zep paths, the `graph_builder.Zep` seam, and subprocess isolation. Its non-blocking suggestions (permanent regression guards for explicit Zep-path imports, `AgentActivity` identity, and the `graph_builder.Zep` seam) were incorporated; the focused boundary file now runs **9 passed** and the full backend suite is **127 passed**. This closes only the bounded import-boundary slice, not semantic parity or full consumer cutover |
|
||||
| Real browser 320×568 and 500×768 capture | **not verified**; Chrome requested OS remote-debugging permission and no dialog was clicked |
|
||||
|
||||
## Scope of the green evidence
|
||||
|
||||
- Durable TaskManager binds to the current app/session factory, fails closed if an app context has no factory or a manager crosses app/factory boundaries, and supports SQL-level organization/owner predicates.
|
||||
- Shared SQLite engine creation enables foreign keys; invalid org/owner references and `SET NULL`/`CASCADE` delete actions are covered.
|
||||
- GraphBuilder async task metadata carries organization/session context.
|
||||
- Local bounded flow reaches persisted report output without constructing a Zep adapter.
|
||||
- Frontend active routes use Thai/English locale contracts; build artifacts contain no CJK code points.
|
||||
|
||||
## Not proven by this evidence
|
||||
|
||||
- Full Zep/local semantic parity or complete consumer cutover.
|
||||
- Production WSGI/worker/object-storage deployment.
|
||||
- Mobile visual QA.
|
||||
- Complete idempotency coverage for every retryable mutation.
|
||||
- Independent full-system approval; broad reviewer `deleg_949ab2f4` timed out, prior narrow reviewer `deleg_2435ad1e` returned `passed=false` before the operation-time guard remediation, and fresh reviewer `deleg_fb0f80b0` passed only the bounded schema/TaskManager slice. Wider SaaS/Zep/deployment work is still not approved.
|
||||
Reference in New Issue
Block a user