chore: establish governance regression baseline
This commit is contained in:
@@ -0,0 +1,60 @@
|
|||||||
|
# Stage 01: Governance Baseline
|
||||||
|
|
||||||
|
Date: 2026-07-29
|
||||||
|
|
||||||
|
Baseline commit: `0030bb8cc18c5aa7107dd8fd866355a6507bd6da`
|
||||||
|
|
||||||
|
## Purpose
|
||||||
|
|
||||||
|
This baseline freezes observable behavior before architecture governance begins. Later stages
|
||||||
|
may move code and introduce internal contracts, but must not change API behavior, account
|
||||||
|
boundaries, persisted user data, desktop presentation, or existing workflows unless a change
|
||||||
|
is approved separately.
|
||||||
|
|
||||||
|
## Deployment Contract
|
||||||
|
|
||||||
|
- One Python process serves the API and static browser client.
|
||||||
|
- One SQLite database is persisted at `data/review.db`.
|
||||||
|
- Docker exposes `0.0.0.0:8765` and mounts only `./data` at `/app/data`.
|
||||||
|
- Public market data is shared; private records are scoped by `user_id`.
|
||||||
|
- Market-data and LLM credentials remain server-side.
|
||||||
|
|
||||||
|
## Verified Baseline
|
||||||
|
|
||||||
|
- Python: 169 tests passed.
|
||||||
|
- Playwright: 45 tests passed at desktop and mobile viewports.
|
||||||
|
- JavaScript entry points pass `node --check`.
|
||||||
|
- `git diff --check` reports no patch errors.
|
||||||
|
- SQLite `PRAGMA integrity_check` returns `ok`.
|
||||||
|
- The database was backed up with the SQLite backup API to
|
||||||
|
`data/backups/governance-stage-01-0030bb8.db` and the backup independently passed
|
||||||
|
`PRAGMA integrity_check`. The backup is intentionally excluded from Git.
|
||||||
|
|
||||||
|
## Repeatable Verification
|
||||||
|
|
||||||
|
Run the fast baseline on every structural change:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python tools/verify_baseline.py
|
||||||
|
```
|
||||||
|
|
||||||
|
Run the browser suite at phase boundaries:
|
||||||
|
|
||||||
|
```shell
|
||||||
|
python tools/verify_baseline.py --e2e
|
||||||
|
```
|
||||||
|
|
||||||
|
## Regression Gates
|
||||||
|
|
||||||
|
1. API routes and response fields remain compatible until a versioned contract says otherwise.
|
||||||
|
2. Existing SQLite files must upgrade without deleting or reassigning user-owned rows.
|
||||||
|
3. A failed external data request must not synthesize market prices.
|
||||||
|
4. Ordinary, member, and administrator permissions must remain distinct.
|
||||||
|
5. Desktop visual refactors require 1080P and 4K comparison in both themes.
|
||||||
|
6. Mobile work is isolated from the approved desktop shell and workflows.
|
||||||
|
7. Old code is deleted only after its replacement is active and reference scans are clean.
|
||||||
|
|
||||||
|
## Rollback
|
||||||
|
|
||||||
|
Code can return to this point with Git commit `0030bb8`. Database rollback must use the
|
||||||
|
matching backup above; a code rollback alone is not sufficient after a schema migration.
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import argparse
|
||||||
|
import shutil
|
||||||
|
import sqlite3
|
||||||
|
import subprocess
|
||||||
|
import sys
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
ROOT = Path(__file__).resolve().parents[1]
|
||||||
|
|
||||||
|
|
||||||
|
def run(label: str, command: list[str]) -> None:
|
||||||
|
print(f"\n[{label}] {' '.join(command)}", flush=True)
|
||||||
|
subprocess.run(command, cwd=ROOT, check=True)
|
||||||
|
|
||||||
|
|
||||||
|
def verify_database() -> None:
|
||||||
|
database = ROOT / "data" / "review.db"
|
||||||
|
if not database.exists():
|
||||||
|
print("\n[database] skipped: data/review.db does not exist")
|
||||||
|
return
|
||||||
|
with sqlite3.connect(f"file:{database.as_posix()}?mode=ro", uri=True) as connection:
|
||||||
|
result = connection.execute("PRAGMA integrity_check").fetchone()
|
||||||
|
if not result or result[0] != "ok":
|
||||||
|
raise RuntimeError(f"SQLite integrity check failed: {result}")
|
||||||
|
print(f"\n[database] integrity_check=ok size={database.stat().st_size}")
|
||||||
|
|
||||||
|
|
||||||
|
def main() -> int:
|
||||||
|
parser = argparse.ArgumentParser(description="Verify the governance regression baseline")
|
||||||
|
parser.add_argument(
|
||||||
|
"--e2e",
|
||||||
|
action="store_true",
|
||||||
|
help="also run the Playwright browser suite",
|
||||||
|
)
|
||||||
|
args = parser.parse_args()
|
||||||
|
|
||||||
|
run("python", [sys.executable, "-m", "unittest", "discover", "-s", "tests"])
|
||||||
|
node = shutil.which("node")
|
||||||
|
if not node:
|
||||||
|
raise RuntimeError("node is required for JavaScript syntax checks")
|
||||||
|
for script in ("static/app.js", "static/heaven-loading-v2.js"):
|
||||||
|
run("javascript", [node, "--check", script])
|
||||||
|
run("patch", ["git", "diff", "--check"])
|
||||||
|
verify_database()
|
||||||
|
|
||||||
|
if args.e2e:
|
||||||
|
npm = shutil.which("npm.cmd" if sys.platform == "win32" else "npm")
|
||||||
|
if not npm:
|
||||||
|
raise RuntimeError("npm is required for the Playwright suite")
|
||||||
|
run("playwright", [npm, "run", "test:e2e"])
|
||||||
|
print("\nBaseline verification passed.")
|
||||||
|
return 0
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
raise SystemExit(main())
|
||||||
Reference in New Issue
Block a user