Realign stale thesis tests to the 7-member positioning group
ensure_positioning_framings adds 5 Architect framings to the core positioning variant group alongside Option A/B, so the group holds 7 candidates and choose_variant retires 6. The two thesis tests still asserted the pre-framings count of 2 — the tests were stale, not the seed. Realign them, document the 2+5=7 seed structure in the thesis guide, and refresh AGENTS.md Current state (13/13 tests green).
This commit is contained in:
@@ -102,7 +102,8 @@ _Phase 0 substrate + Phase 1 thesis/outreach are built; current package is **v0.
|
|||||||
- **Working (all draft-only):** CRM + ingest (chunk→embed→Qdrant + retrieval) + redaction boundary; Gmail capture (DWD) + email-activity propose→approve; Thesis Workshop + Architect (Claude) with dual-approval gate; Outreach Draft Assistant + follow-up radar + per-user voice + Tier-B in-thread Gmail draft creation.
|
- **Working (all draft-only):** CRM + ingest (chunk→embed→Qdrant + retrieval) + redaction boundary; Gmail capture (DWD) + email-activity propose→approve; Thesis Workshop + Architect (Claude) with dual-approval gate; Outreach Draft Assistant + follow-up radar + per-user voice + Tier-B in-thread Gmail draft creation.
|
||||||
- **Deployed:** v0.1.0:74 is committed, pushed (`main` @ `aec2b77`), built, and **installed to the box** (`$START9_BOX_HOST` / immense-voyage.local now reports v0.1.0:74, up from v72). On boot, `ensure_thesis_v2_promoted` makes the v2.0 reserve-asset spine the working *approved* spine (node-level, reversible). **Unverified post-deploy:** service health after the v72→v74 migration, and the security fixes behaving live (no box CRM URL/auth on hand).
|
- **Deployed:** v0.1.0:74 is committed, pushed (`main` @ `aec2b77`), built, and **installed to the box** (`$START9_BOX_HOST` / immense-voyage.local now reports v0.1.0:74, up from v72). On boot, `ensure_thesis_v2_promoted` makes the v2.0 reserve-asset spine the working *approved* spine (node-level, reversible). **Unverified post-deploy:** service health after the v72→v74 migration, and the security fixes behaving live (no box CRM URL/auth on hand).
|
||||||
- **Shipped in v0.1.0:74** (security/privacy hardening from the 2026-06-12 full-eval; report in `EVALUATION.md`): closed a pre-auth `/assets/` path traversal (could read crm.db / JWT secret / Gmail key); wired the local-Qwen NER backstop into the outreach redaction boundary (free-prose email bodies were reaching Claude with unknown names in the clear); added `deleted_at IS NULL` to every get-by-id + nested sub-select read path. Verified locally (py_compile, query exec, redaction/outreach tests, containment logic) + two reviewer passes.
|
- **Shipped in v0.1.0:74** (security/privacy hardening from the 2026-06-12 full-eval; report in `EVALUATION.md`): closed a pre-auth `/assets/` path traversal (could read crm.db / JWT secret / Gmail key); wired the local-Qwen NER backstop into the outreach redaction boundary (free-prose email bodies were reaching Claude with unknown names in the clear); added `deleted_at IS NULL` to every get-by-id + nested sub-select read path. Verified locally (py_compile, query exec, redaction/outreach tests, containment logic) + two reviewer passes.
|
||||||
|
- **Local verification (2026-06-12):** all documented commands run clean — `py_compile` OK, **13/13 backend tests green**, `./start.sh`/`./start_beta.sh` boot (health 200, auth 401), `make` builds the x86 s9pk (v0.1.0:74), `/assets/` traversal 404s locally (incl. URL-encoded). The 2 stale thesis tests are fixed (seed structure now documented in `docs/guides/thesis.md`). Box-only checks still open: live service health + security fixes on `$START9_BOX_HOST`.
|
||||||
- **Decided, not yet built:** CRM as canonical thesis backbone with the signal-engine reading from it (reconciliation unwired); reply-all for Tier-B drafts (drafts currently reply to the LP only).
|
- **Decided, not yet built:** CRM as canonical thesis backbone with the signal-engine reading from it (reconciliation unwired); reply-all for Tier-B drafts (drafts currently reply to the LP only).
|
||||||
- **Known debt (P2, not deploy-blocking):** 2 thesis tests red vs. the v73 seed + no aggregate runner; `?limit=abc` crashes the request thread; scrub-gateway TLS verify off; `cryptography==42.0.5`; unpkg/no-SRI frontend; stale user-visible `start9/0.4/assets/ABOUT.md`; hardcoded Spark/Qdrant IPs in the s9pk; the 5.4k-line `server.py` monolith. P3 batch + full list in `EVALUATION.md`.
|
- **Known debt (P2, not deploy-blocking):** no aggregate test runner (the Commands `for` loop is it); `?limit=abc` crashes the request thread (authenticated list path); scrub-gateway TLS verify off; `cryptography==42.0.5`; unpkg/no-SRI frontend; stale user-visible `start9/0.4/assets/ABOUT.md`; hardcoded Spark/Qdrant IPs in the s9pk; the 5.4k-line `server.py` monolith. P3 batch + full list in `EVALUATION.md`.
|
||||||
- **Other gaps:** the v2.0 spine is the *working* spine but **not a canonical `thesis_version`** (needs Grant + Jonathan dual sign-off); Appendix-A conviction/exposure (incl. ~40% Strike) stay Grant's working read, not canonical, not fed to the engine; live features (Claude/Qdrant/Gmail) unverified on the box.
|
- **Other gaps:** the v2.0 spine is the *working* spine but **not a canonical `thesis_version`** (needs Grant + Jonathan dual sign-off); Appendix-A conviction/exposure (incl. ~40% Strike) stay Grant's working read, not canonical, not fed to the engine; live features (Claude/Qdrant/Gmail) unverified on the box.
|
||||||
- **Next:** 1) verify v0.1.0:74 live on the box — service health + `curl --path-as-is .../assets/../../data/crm.db` → 404; 2) clear P2 debt (start: 2 red thesis tests + aggregate runner + add traversal/soft-delete/NER regression tests); 3) Grant + Jonathan freeze v2.0 canonical; 4) build reply-all; 5) confirm Appendix-A + Maple/OpenSecret/Primal, then promote.
|
- **Next:** 1) verify v0.1.0:74 live on the box — service health + `curl --path-as-is .../assets/../../data/crm.db` → 404; 2) clear P2 debt (next: aggregate test runner + add traversal/soft-delete/NER regression tests; 2 stale thesis tests already realigned); 3) Grant + Jonathan freeze v2.0 canonical; 4) build reply-all; 5) confirm Appendix-A + Maple/OpenSecret/Primal, then promote.
|
||||||
|
|||||||
@@ -35,12 +35,14 @@ def main():
|
|||||||
c = sqlite3.connect(db)
|
c = sqlite3.connect(db)
|
||||||
c.row_factory = sqlite3.Row
|
c.row_factory = sqlite3.Row
|
||||||
|
|
||||||
# choose-variant: pick Option A in the positioning group -> Option B retired
|
# choose-variant: pick Option A in the positioning group -> every other option retired.
|
||||||
|
# The group holds Option A/B plus the 5 Architect framings (ensure_positioning_framings) = 7.
|
||||||
opts = c.execute("SELECT id, title FROM thesis_nodes WHERE variant_group='positioning' AND deleted_at IS NULL").fetchall()
|
opts = c.execute("SELECT id, title FROM thesis_nodes WHERE variant_group='positioning' AND deleted_at IS NULL").fetchall()
|
||||||
check(len(opts) == 2, f"positioning starts with 2 options (got {len(opts)})")
|
check(len(opts) == 7, f"positioning starts with 7 options: Option A/B + 5 Architect framings (got {len(opts)})")
|
||||||
a = next(o for o in opts if "Option A" in (o["title"] or ""))
|
a = next(o for o in opts if "Option A" in (o["title"] or ""))
|
||||||
res = at.choose_variant(a["id"], db=db)
|
res = at.choose_variant(a["id"], db=db)
|
||||||
check(res.get("retired_siblings") == 1, "choose_variant retired the 1 sibling")
|
expected_retired = len(opts) - 1 # 6: Option B + the 5 framings
|
||||||
|
check(res.get("retired_siblings") == expected_retired, f"choose_variant retired the {expected_retired} siblings (got {res.get('retired_siblings')})")
|
||||||
live = c.execute("SELECT COUNT(*) FROM thesis_nodes WHERE variant_group='positioning' AND deleted_at IS NULL").fetchone()[0]
|
live = c.execute("SELECT COUNT(*) FROM thesis_nodes WHERE variant_group='positioning' AND deleted_at IS NULL").fetchone()[0]
|
||||||
check(live == 1, f"positioning now has 1 live option (got {live})")
|
check(live == 1, f"positioning now has 1 live option (got {live})")
|
||||||
check(c.execute("SELECT status FROM thesis_nodes WHERE id=?", (a["id"],)).fetchone()[0] == "approved",
|
check(c.execute("SELECT status FROM thesis_nodes WHERE id=?", (a["id"],)).fetchone()[0] == "approved",
|
||||||
|
|||||||
@@ -3,8 +3,9 @@
|
|||||||
|
|
||||||
Runs the real init_db against a throwaway DB (applies migration 0002 and the
|
Runs the real init_db against a throwaway DB (applies migration 0002 and the
|
||||||
auto-seed), then asserts the Workshop substrate: a core line, one line per segment,
|
auto-seed), then asserts the Workshop substrate: a core line, one line per segment,
|
||||||
the Option A/B banner as a 2-member variant group, the pillars/proof, and the
|
the positioning variant group (Option A/B banner + the 5 Architect framings seeded
|
||||||
segments table — and that re-seeding is a no-op.
|
by ensure_positioning_framings), the pillars/proof, and the segments table — and
|
||||||
|
that re-seeding is a no-op.
|
||||||
|
|
||||||
Run: cd backend && python3 test_thesis_seed.py
|
Run: cd backend && python3 test_thesis_seed.py
|
||||||
"""
|
"""
|
||||||
@@ -47,7 +48,11 @@ def main():
|
|||||||
check("throughline" in types, "core has a throughline node")
|
check("throughline" in types, "core has a throughline node")
|
||||||
check("proof_point" in types, "core has a proof_point node")
|
check("proof_point" in types, "core has a proof_point node")
|
||||||
variants = [n for n in nodes if n["variant_group"] == "positioning"]
|
variants = [n for n in nodes if n["variant_group"] == "positioning"]
|
||||||
check(len(variants) == 2, f"Option A/B banner is a 2-member variant group (got {len(variants)})")
|
banner = [n for n in variants if (n["title"] or "").startswith(("Option A", "Option B"))]
|
||||||
|
framings = [n for n in variants if "(Architect," in (n["title"] or "")]
|
||||||
|
check(len(banner) == 2, f"Option A/B banner present in positioning group (got {len(banner)})")
|
||||||
|
check(len(framings) == 5, f"five Architect positioning framings seeded into the group (got {len(framings)})")
|
||||||
|
check(len(variants) == 7, f"positioning variant group = Option A/B + 5 framings = 7 (got {len(variants)})")
|
||||||
pillars = [n for n in nodes if n["node_type"] == "claim" and n["title"] and n["title"][0] in "123"]
|
pillars = [n for n in nodes if n["node_type"] == "claim" and n["title"] and n["title"][0] in "123"]
|
||||||
check(len(pillars) == 3, f"three pillar claims (got {len(pillars)})")
|
check(len(pillars) == 3, f"three pillar claims (got {len(pillars)})")
|
||||||
|
|
||||||
|
|||||||
@@ -24,5 +24,6 @@ Read this before editing thesis nodes, versions, the review flow, or the Archite
|
|||||||
## Boot behavior
|
## Boot behavior
|
||||||
|
|
||||||
- On boot, `ensure_thesis_v2_promoted` makes the v2.0 reserve-asset spine the working *approved* spine (node-level, reversible) — it does **not** freeze a canonical version. Promotion to canonical still waits on dual sign-off in the Workshop.
|
- On boot, `ensure_thesis_v2_promoted` makes the v2.0 reserve-asset spine the working *approved* spine (node-level, reversible) — it does **not** freeze a canonical version. Promotion to canonical still waits on dual sign-off in the Workshop.
|
||||||
|
- **The core `positioning` variant group has 7 members, not 2.** The seed plants Option A/B, then `ensure_positioning_framings` (2026-06-05 Architect pass) additively inserts 5 competing framings (titles `Option C–G … (Architect, NN/60)`) into the same group. So `choose_variant` on any member retires the other 6. Tests (`test_thesis_seed.py`, `test_thesis_actions.py`) assert this 2+5=7 shape — keep them in sync if the framing set changes.
|
||||||
|
|
||||||
See also `docs/thesis-handoff.md` for the current thesis content state.
|
See also `docs/thesis-handoff.md` for the current thesis content state.
|
||||||
|
|||||||
Reference in New Issue
Block a user