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).
Fixes from the 2026-06-12 full-eval (P0 + two P1s); code-only, no schema
change. Without these the "private CRM" premise was breachable on the LAN:
- P0: the /assets/ route joined the request path onto FRONTEND_DIR without
normalizing '..' (get_path/urlparse pass it through), so an unauthenticated
GET /assets/../../data/crm.db read any file the process could — the LP DB,
the JWT signing secret (-> admin-token forgery), the Gmail key. Add a realpath
containment check that 404s anything resolving outside FRONTEND_ROOT.
- P1: the LP-outreach drafter built its redaction Boundary with no ner_fn, so
unknown people/firms in raw email bodies reached Claude in the clear. Pass the
local-Qwen NER backstop (ner_fn=_ner_local), matching architect_grounding;
fails closed via the existing scrub_unavailable path if the local model is down.
- P1: get-by-id handlers leaked soft-deleted records by direct ID. Add
deleted_at IS NULL to every get-by-id path — contacts, organizations,
opportunities, lp_profiles — and to the nested related-data sub-selects in
the contact/opportunity detail payloads, matching the list-handler convention.
Bumps the package to v0.1.0:74 (utils.ts + versions/v0.1.0.74.ts + graph).
Full report in EVALUATION.md; remaining P2/P3 triaged in AGENTS.md Current state.
Move subsystem mechanics (migrations, thesis gate, redaction, ingest,
email, packaging) out of AGENTS.md into docs/guides/<topic>.md, each
scoped by paths: frontmatter and symlinked from .claude/rules/ so Claude
Code lazy-loads them. AGENTS.md keeps whole-repo facts and universal
guardrails plus a one-line index per guide. Fix the inaccurate
".claude/ is gitignored" note — it is tracked.
Add a concise day-one AGENTS.md (stack, exact build/run/test/deploy
commands, directory layout, conventions, Always/Never). Preserve the
existing CLAUDE.md project constitution as docs/ten31-constitution.md
(referenced from AGENTS.md) and point CLAUDE.md -> AGENTS.md so Claude
Code loads the canonical guide.
Swap the dead "scarcity as the connecting idea" / bitcoin-as-settlement
spine for the v2.0 reserve-asset spine (bitcoin = apex non-debasable
reserve asset; debasement = forcing function; AI = abundance engine;
throughline is an asset-value/capital-flow claim, not settlement; three
seams Energy<->Compute, Debasement<->Bitcoin, AI<->Data-Ownership)
everywhere it was still encoded in live code, the seed, and the docs.
- architect_agent.py / outreach_agent.py: both system prompts carried
"scarcity as the connecting idea" and shipped settlement framing into
every generated draft; rewritten to the reserve-asset spine.
- thesis_seed.py: THROUGHLINE, PILLAR_1, the AI/energy-operator segment
angle, and THESIS_V2 corrected and voice-cleaned (no em dash / "X, not
Y" / "bet"). PILLAR_2/3 (real revenue, founder access) kept.
- ensure_thesis_v2_promoted / revert_thesis_v2_promotion: make the v2.0
spine the working APPROVED spine and re-ground/clean the core nodes,
deployment-state-invariant (structural targeting, not body text) and
fully reversible (captures prior body/title/status/deleted_at). NODE
level only: never sets a thesis_version canonical (guardrail #4); no
hard deletes (guardrail #3). Wired into init_db after the v2 candidate
stage.
- docs/thesis-handoff.md replaced wholesale with the complete v2.0 doc;
Ten31_Agentic_Build_Plan.md + PHASE_1.md throughline glosses updated.
The v2.0 spine remains an unratified draft from the signal-engine
workstream: canonical freeze stays the partners' dual sign-off, and
Appendix-A conviction/exposure figures stay Grant's working read.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Incorporates the signal-engine workstream's v2.0 thesis correction: the spine is bitcoin
as the apex NON-DEBASABLE RESERVE ASSET (debasement = forcing function, AI = abundance
engine), NOT "infrastructure settles on bitcoin" (the settlement/payments claim — Strike's
payments thesis died in backtest). thesis_seed.ensure_thesis_v2_candidate stages the
v2.0 root/forcing-function, throughline, the verifiable-vs-contrarian decomposition, and
the 3 seams (Energy↔Compute, Debasement↔Bitcoin, AI↔Data-Ownership) as CANDIDATE nodes
under the core line (idempotent sentinel; provenance + "unratified, exposure unconfirmed"
on the section). Nothing canonical (guardrail #4). docs/thesis-handoff.md gets a
SUPERSEDED-spine banner pointing to v2.0.
NOT done (gated on partner ratification): the live THROUGHLINE/PILLAR_1 constants and
architect_agent.py's system prompt ("scarcity as the connecting idea") still encode the
old spine — until ratified+updated, Vary/Revise/outreach regenerate the old framing.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Phase 1 Workstream D. Lets the Architect ground the thesis in REAL recurring LP
objections without any LP identity reaching the Claude API. Layered, defense-in-depth,
fail-closed by construction (docs/redaction-rehydration.md).
backend/redaction/:
- scrub.py: the leak-proof core. Drops Tier-1 (labelled/structured account/wire/SSN/
IBAN/SWIFT/passport, separator-tolerant); tokenizes known LP entities (dictionary from
the canonical layer, unicode-folded + hyphen-extended) and structured PII (emails,
scheme-less/social URLs, intl+ext phones, currency-cued amounts, ISO/worded/numeric/
quarter dates, addresses, bare long digit runs); pre-neutralizes injected [TYPE_N]
strings; single-pass rehydrate; metadata-only audit logging (the pseudonym map is the
de-anon key — local-only, never logged/sent). Hardened across THREE adversarial
leak-hunts (worded/coded amounts, intl phones, NFD/ligature/zero-width names, slash/
comma SSN, SWIFT, alpha-prefixed accounts, substance-preserving false-positive fixes).
- client.py: Boundary — one scrub/rehydrate contract, SCRUB_BACKEND=local (default) or
gateway (Spark Control /scrub + /rehydrate). Fails closed (db_path required; dictionary
build errors propagate; strict rehydrate returns tokenized-not-de-anon text).
- test_scrub_leak.py, test_reidentification.py: golden-file leak + re-identification
suites (synthetic only, guardrail #9), regression-locking every leak-hunt vector.
backend/mcp/architect_grounding.py: the flow — retrieve (local) -> minimize-first
(local Qwen) -> scrub (+ local-Qwen NER backstop for unknown names) -> Claude over the
de-identified register only -> re-hydrate locally -> human review. FAILS CLOSED if the
local model is unreachable or a hallucinated token appears. test_grounding_boundary.py
proves nothing sensitive reaches Claude and the three fail-closed paths.
server.py: POST /api/architect/ground (admin) wires retrieval -> ground_objections.
docker_entrypoint.sh: SCRUB_BACKEND (default local). docs/spark-control-scrub-endpoints.md:
the gateway handover spec (Option 1 — caller supplies the entity dictionary).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Frontend: ThesisWorkshopPage / ThesisWorkshopNode / ThesisWorkshopOptions —
the collaborative iteration screen where partners generate a variable number
of competing thesis options (1, 2, 3, A1/A2/A3 ...) for any node, give
feedback, and regenerate. Reuses the shared api() helper; flexible option
count is the core UX constraint.
Backend Architect agent (architect_agent.py) + routes shipped in dd25bbc;
this completes the user-facing surface and bumps the StartOS package to
0.1.0:49 (anthropic dep already in the image, key loaded from
/data/secrets/anthropic-api-key — self-disabling until present).
Also lands thesis seed iterations v3 and v5 (voice/messaging corrections).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Dual sign-off is now the default (thesis_required_approvals defaults to 2).
- Entity-merge review queue (migration 0003): the fuzzy/Qwen tier no longer
auto-merges — it writes CANDIDATES (entity_merge_candidates) with a same/different
suggestion + confidence + reason for a human to approve (merge) or reject (keep
separate). entity_merge.py applies/rejects (durable via entity_merges, soft-delete,
repoint links+edges); decided pairs aren't re-surfaced.
- entity_jobs.py: UI-triggered background index jobs (rebuild/update/find-duplicates)
as subprocesses with a one-at-a-time lock; status in /api/system/status.
- server.py: /api/index/{rebuild,update}, /api/entities/find-duplicates,
/api/entities/merge-candidates [+ /{id} decide] — admin-gated.
- docs/thesis-seed-v2.md: concrete, plain-English rewrite per Grant's feedback.
Backend verified end-to-end on synthetic data (candidate gen -> approve/reject).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- entity_resolution: emit member_of relationship edges (contact -> investor),
so one investor entity owns many contacts (institution) and a HNWI is the N=1
case; crm_tools.get_investor_contacts + get_entity contacts/member_of; MCP tool.
- seed_synthetic: multi-contact institutions to exercise it (Harbor & Vine = 5).
- server.py: GET /api/system/status (index/entity/thesis/activity health) for an
in-app status view (no shell needed to verify the index).
- docs/thesis-seed-v1.md: grounded v1 thesis (throughline, 6 pillars, objections,
per-segment angles, voice) drawn from Ten31's newsletter/site/essays.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- Fuzzy tier (backend/ingest/fuzzy_resolve.py + llm.py): local Qwen adjudicates
the deterministic resolver's flagged name-variant candidates; merges are
durable via entity_merges (deterministic re-runs respect them), losers
soft-deleted, logged. Idempotent.
- Incremental sync (backend/ingest/sync.py): re-embeds only rows changed since a
watermark (ingest_sync_state); first run / --recreate = full. Tested full→0→1.
- Start9 packaging (start9/0.4): Dockerfile bundles ingest+mcp + fastembed/mcp;
"Build search index" action runs the init in a subcontainer; MCP shipped as a
manual stdio server (not a daemon); version 0.1.0:44. INGEST_PACKAGING.md.
- backfill.py: factored embed_and_upsert() shared with sync.
Verified end-to-end on synthetic data + live Sparks/Qwen/Qdrant.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>