secondbrain-py 0.2.1__py3-none-any.whl
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- brain/__init__.py +0 -0
- brain/__main__.py +18 -0
- brain/_capture_command.py +445 -0
- brain/_compose.py +52 -0
- brain/activity.py +206 -0
- brain/ask.py +631 -0
- brain/audio.py +591 -0
- brain/backfill/__init__.py +12 -0
- brain/backfill/search_extras.py +141 -0
- brain/backfill/source_rows.py +101 -0
- brain/bin/__init__.py +1 -0
- brain/bin/_launcher.py +107 -0
- brain/bin/down.py +8 -0
- brain/bin/launchd.py +268 -0
- brain/bin/monitor.py +570 -0
- brain/bin/rebuild.py +8 -0
- brain/bin/status.py +8 -0
- brain/bin/up.py +8 -0
- brain/brief.py +272 -0
- brain/capture.py +49 -0
- brain/chat.py +293 -0
- brain/cli.py +9760 -0
- brain/cli_claude.py +81 -0
- brain/cli_connect.py +285 -0
- brain/cli_demo.py +266 -0
- brain/config.py +1949 -0
- brain/connect.py +925 -0
- brain/db.py +540 -0
- brain/demo/__init__.py +452 -0
- brain/demo/corpus/manifest.json +403 -0
- brain/demo/embedder.py +74 -0
- brain/durations.py +84 -0
- brain/edit_session.py +156 -0
- brain/editor.py +67 -0
- brain/elicit/__init__.py +16 -0
- brain/elicit/detectors.py +250 -0
- brain/elicit/drafter.py +70 -0
- brain/elicit/queue.py +220 -0
- brain/elicit/schema.py +48 -0
- brain/elicit/session.py +445 -0
- brain/embedding_targets.py +54 -0
- brain/embeddings.py +424 -0
- brain/enrichment.py +808 -0
- brain/errors.py +357 -0
- brain/eval/__init__.py +129 -0
- brain/eval/answer_eval.py +281 -0
- brain/eval/baseline.py +265 -0
- brain/eval/concept_extraction.py +378 -0
- brain/eval/corpus.py +152 -0
- brain/eval/errors.py +19 -0
- brain/eval/graph_baseline.py +226 -0
- brain/eval/graph_retrieval.py +202 -0
- brain/eval/graph_runner.py +319 -0
- brain/eval/metrics.py +101 -0
- brain/eval/runner.py +223 -0
- brain/format.py +783 -0
- brain/gaps.py +390 -0
- brain/graph_rag/__init__.py +94 -0
- brain/graph_rag/_retrieval_common.py +113 -0
- brain/graph_rag/aggregates.py +303 -0
- brain/graph_rag/aliases/__init__.py +583 -0
- brain/graph_rag/backends/__init__.py +10 -0
- brain/graph_rag/backends/_age_helpers.py +473 -0
- brain/graph_rag/backends/age.py +782 -0
- brain/graph_rag/backends/base.py +272 -0
- brain/graph_rag/build.py +344 -0
- brain/graph_rag/communities.py +644 -0
- brain/graph_rag/communities_summary.py +437 -0
- brain/graph_rag/concepts.py +202 -0
- brain/graph_rag/cooccur.py +193 -0
- brain/graph_rag/cross_type.py +312 -0
- brain/graph_rag/extract.py +885 -0
- brain/graph_rag/fuse.py +371 -0
- brain/graph_rag/global_.py +412 -0
- brain/graph_rag/grouping.py +372 -0
- brain/graph_rag/person_resolver.py +167 -0
- brain/graph_rag/reconcile.py +792 -0
- brain/graph_rag/relational.py +353 -0
- brain/graph_rag/retrieve.py +526 -0
- brain/graph_rag/router.py +288 -0
- brain/graph_rag/schema.py +320 -0
- brain/graph_rag/sync.py +237 -0
- brain/graph_rag/tenancy.py +43 -0
- brain/graph_rag/themes.py +501 -0
- brain/graph_rag/weighting.py +202 -0
- brain/ingest/__init__.py +1926 -0
- brain/ingest/chunker.py +249 -0
- brain/ingest/docx.py +40 -0
- brain/ingest/gmail.py +621 -0
- brain/ingest/markdown.py +37 -0
- brain/ingest/pdf.py +61 -0
- brain/ingest/stdin.py +22 -0
- brain/ingest/sub_tokens.py +91 -0
- brain/ingest/text.py +16 -0
- brain/interactions.py +205 -0
- brain/maintenance.py +355 -0
- brain/mcp_server.py +3405 -0
- brain/migrations/001_init.sql +43 -0
- brain/migrations/002_qwen3_embedding.sql +17 -0
- brain/migrations/003_vault_model.sql +41 -0
- brain/migrations/004_relax_content_hash_uniqueness.sql +18 -0
- brain/migrations/005_derived_links.sql +67 -0
- brain/migrations/006_dedup_file_by_source_path.sql +25 -0
- brain/migrations/007_email_thread_and_draft.sql +15 -0
- brain/migrations/008_gmail_thread_unique.sql +11 -0
- brain/migrations/009_chunks_weighted_tsv.sql +28 -0
- brain/migrations/010_interactions.sql +30 -0
- brain/migrations/011_documents_summary.sql +23 -0
- brain/migrations/012_graphrag.sql +171 -0
- brain/migrations/013_graphrag_communities.sql +125 -0
- brain/migrations/014_graphrag_community_summary_hash.sql +33 -0
- brain/migrations/015_interactions_graph_targets.sql +89 -0
- brain/migrations/016_index_hygiene.sql +61 -0
- brain/migrations/017_elicit.sql +30 -0
- brain/migrations/018_review_gap_signal_kinds.sql +40 -0
- brain/migrations/019_search_queries.sql +35 -0
- brain/migrations/020_link_suggestions.sql +40 -0
- brain/migrations/021_timeline_doc_date.sql +34 -0
- brain/migrations/022_link_suggestions_undirected.sql +84 -0
- brain/migrations/023_search_queries_fts_count.sql +28 -0
- brain/quartz_overrides/__init__.py +8 -0
- brain/quartz_overrides/quartz/bootstrap-cli.mjs +65 -0
- brain/quartz_overrides/quartz/build.ts +568 -0
- brain/quartz_overrides/quartz/cli/args.js +152 -0
- brain/quartz_overrides/quartz/cli/build_partial_handler.js +544 -0
- brain/quartz_overrides/quartz/cli/handlers.js +636 -0
- brain/quartz_overrides/quartz/components/CommandPalette.tsx +172 -0
- brain/quartz_overrides/quartz/components/Explorer.tsx +198 -0
- brain/quartz_overrides/quartz/components/Footer.tsx +27 -0
- brain/quartz_overrides/quartz/components/Graph.tsx +468 -0
- brain/quartz_overrides/quartz/components/PageTitle.tsx +72 -0
- brain/quartz_overrides/quartz/components/RelatedDocs.tsx +38 -0
- brain/quartz_overrides/quartz/components/Search.tsx +161 -0
- brain/quartz_overrides/quartz/components/SummaryLede.tsx +72 -0
- brain/quartz_overrides/quartz/components/index.ts +92 -0
- brain/quartz_overrides/quartz/components/pages/TagContent.tsx +272 -0
- brain/quartz_overrides/quartz/components/scripts/commandPalette.inline.ts +665 -0
- brain/quartz_overrides/quartz/components/scripts/explorer.inline.ts +768 -0
- brain/quartz_overrides/quartz/components/scripts/graph.inline.ts +2302 -0
- brain/quartz_overrides/quartz/components/scripts/relatedDocs.inline.ts +163 -0
- brain/quartz_overrides/quartz/components/scripts/search.inline.ts +1011 -0
- brain/quartz_overrides/quartz/plugins/emitters/contentIndex.ts +546 -0
- brain/quartz_overrides/quartz/plugins/transformers/codeCopy.ts +94 -0
- brain/quartz_overrides/quartz/plugins/transformers/derivedFenceMark.ts +302 -0
- brain/quartz_overrides/quartz/plugins/transformers/emailThread.ts +148 -0
- brain/quartz_overrides/quartz/plugins/transformers/emptyDoorFilter.ts +213 -0
- brain/quartz_overrides/quartz/plugins/transformers/index.ts +114 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkKindMark.ts +205 -0
- brain/quartz_overrides/quartz/plugins/transformers/linkSourceTag.ts +104 -0
- brain/quartz_overrides/quartz/plugins/transformers/relativeDate.ts +100 -0
- brain/quartz_overrides/quartz/plugins/transformers/reloadSignal.ts +131 -0
- brain/quartz_overrides/quartz/processors/parse.ts +371 -0
- brain/quartz_overrides/quartz/processors/parser_cache.ts +78 -0
- brain/quartz_overrides/quartz/static/brain-logo-dark.png +0 -0
- brain/quartz_overrides/quartz/static/brain-logo-light.png +0 -0
- brain/quartz_overrides/quartz/static/codeCopy.js +196 -0
- brain/quartz_overrides/quartz/static/emailThread.js +334 -0
- brain/quartz_overrides/quartz/static/favicon.ico +0 -0
- brain/quartz_overrides/quartz/static/icon.png +0 -0
- brain/quartz_overrides/quartz/static/linkSourceTag.js +104 -0
- brain/quartz_overrides/quartz/static/relativeDate.js +142 -0
- brain/quartz_overrides/quartz/static/reload.js +168 -0
- brain/quartz_overrides/quartz/styles/brain/_article.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_atmosphere.scss +113 -0
- brain/quartz_overrides/quartz/styles/brain/_callouts.scss +180 -0
- brain/quartz_overrides/quartz/styles/brain/_cmdk.scss +7 -0
- brain/quartz_overrides/quartz/styles/brain/_code.scss +208 -0
- brain/quartz_overrides/quartz/styles/brain/_command_palette.scss +369 -0
- brain/quartz_overrides/quartz/styles/brain/_email_thread.scss +228 -0
- brain/quartz_overrides/quartz/styles/brain/_explorer.scss +142 -0
- brain/quartz_overrides/quartz/styles/brain/_home.scss +182 -0
- brain/quartz_overrides/quartz/styles/brain/_links.scss +322 -0
- brain/quartz_overrides/quartz/styles/brain/_marginalia.scss +117 -0
- brain/quartz_overrides/quartz/styles/brain/_motion.scss +175 -0
- brain/quartz_overrides/quartz/styles/brain/_people_hub.scss +100 -0
- brain/quartz_overrides/quartz/styles/brain/_related_docs.scss +137 -0
- brain/quartz_overrides/quartz/styles/brain/_search.scss +252 -0
- brain/quartz_overrides/quartz/styles/brain/_sidebar.scss +468 -0
- brain/quartz_overrides/quartz/styles/brain/_summary_lede.scss +56 -0
- brain/quartz_overrides/quartz/styles/brain/_surface.scss +43 -0
- brain/quartz_overrides/quartz/styles/brain/_tag_content.scss +118 -0
- brain/quartz_overrides/quartz/styles/brain/_tokens.scss +197 -0
- brain/quartz_overrides/quartz/styles/brain/_typography.scss +92 -0
- brain/quartz_overrides/quartz/styles/custom.scss +89 -0
- brain/quartz_overrides/quartz/styles/graph.scss +505 -0
- brain/quartz_overrides/quartz/util/ctx.ts +92 -0
- brain/quartz_overrides/quartz/util/fastpath_manifest.ts +608 -0
- brain/quartz_overrides/quartz/util/path.ts +358 -0
- brain/quartz_overrides/quartz/util/sourceIcons.ts +55 -0
- brain/quartz_overrides/quartz.config.ts +270 -0
- brain/quartz_overrides/quartz.layout.ts +314 -0
- brain/queries.py +1188 -0
- brain/rank_fusion.py +8 -0
- brain/resurface.py +210 -0
- brain/review/__init__.py +26 -0
- brain/review/emit.py +27 -0
- brain/review/queries.py +436 -0
- brain/review/render.py +196 -0
- brain/review/scans.py +355 -0
- brain/review/weekly.py +413 -0
- brain/search.py +704 -0
- brain/set_similarity.py +15 -0
- brain/setup.py +1205 -0
- brain/tags.py +56 -0
- brain/templates/Caddyfile.j2 +9 -0
- brain/templates/__init__.py +1 -0
- brain/templates/bin/__init__.py +1 -0
- brain/templates/bin/_brain-brief-fg.sh +25 -0
- brain/templates/bin/_brain-build-fg.sh +53 -0
- brain/templates/bin/_brain-watcher-fg.sh +65 -0
- brain/templates/bin/brain-down.sh +89 -0
- brain/templates/bin/brain-status.sh +83 -0
- brain/templates/bin/brain-up.sh +221 -0
- brain/templates/docker/age/Dockerfile +79 -0
- brain/templates/docker-compose.stock.yml.j2 +26 -0
- brain/templates/docker-compose.yml.j2 +34 -0
- brain/templates/env.example +190 -0
- brain/templates/launchd/__init__.py +1 -0
- brain/templates/launchd/com.brain.brief.plist.j2 +45 -0
- brain/templates/launchd/com.brain.build.plist.j2 +46 -0
- brain/templates/launchd/com.brain.watcher.plist.j2 +46 -0
- brain/templates/skill/SKILL.md +63 -0
- brain/templates/skill/__init__.py +1 -0
- brain/timeline.py +834 -0
- brain/todo.py +124 -0
- brain/uninstall.py +185 -0
- brain/vault/__init__.py +115 -0
- brain/vault/_atomic.py +25 -0
- brain/vault/daily_index.py +228 -0
- brain/vault/derived_links/__init__.py +50 -0
- brain/vault/derived_links/directory.py +683 -0
- brain/vault/derived_links/fence.py +408 -0
- brain/vault/derived_links/gws.py +64 -0
- brain/vault/derived_links/participants.py +143 -0
- brain/vault/derived_links/pass_runner.py +362 -0
- brain/vault/derived_links/rules.py +137 -0
- brain/vault/export.py +683 -0
- brain/vault/frontmatter.py +165 -0
- brain/vault/graph.py +620 -0
- brain/vault/graph_format.py +388 -0
- brain/vault/link_rewrite.py +235 -0
- brain/vault/links.py +260 -0
- brain/vault/note_builder.py +211 -0
- brain/vault/paths.py +55 -0
- brain/vault/quartz_overlay.py +236 -0
- brain/vault/rename.py +591 -0
- brain/vault/resolver.py +304 -0
- brain/vault/slug.py +127 -0
- brain/vault/sync.py +1513 -0
- brain/vault/sync_summaries.py +264 -0
- brain/vault/templates.py +145 -0
- brain/vault/watch.py +1052 -0
- brain/wiki/__init__.py +6 -0
- brain/wiki/_github_slugger.py +76 -0
- brain/wiki/_person_name.py +314 -0
- brain/wiki/build_homepage.py +541 -0
- brain/wiki/build_partial.py +273 -0
- brain/wiki/build_people.py +934 -0
- brain/wiki/build_related.py +758 -0
- brain/wiki/build_swap.py +585 -0
- brain/wiki/build_watcher.py +975 -0
- brain/wiki/edit_classifier.py +215 -0
- brain/wiki/errors.py +10 -0
- brain/wiki/fastpath_manifest.py +475 -0
- brain/wiki/fastpath_state.py +174 -0
- brain/wiki/install.py +296 -0
- brain/wiki/slug.py +111 -0
- secondbrain_py-0.2.1.dist-info/METADATA +195 -0
- secondbrain_py-0.2.1.dist-info/RECORD +273 -0
- secondbrain_py-0.2.1.dist-info/WHEEL +5 -0
- secondbrain_py-0.2.1.dist-info/entry_points.txt +11 -0
- secondbrain_py-0.2.1.dist-info/licenses/LICENSE +21 -0
- secondbrain_py-0.2.1.dist-info/top_level.txt +1 -0
|
@@ -0,0 +1,403 @@
|
|
|
1
|
+
[
|
|
2
|
+
{
|
|
3
|
+
"external_id": "demo-manual-0001",
|
|
4
|
+
"source": "manual",
|
|
5
|
+
"title": "Compliance Horror Stories — collected war stories",
|
|
6
|
+
"date": "2026-06-02",
|
|
7
|
+
"content_type": "note",
|
|
8
|
+
"tags": [
|
|
9
|
+
"compliance",
|
|
10
|
+
"soc2",
|
|
11
|
+
"war-stories"
|
|
12
|
+
],
|
|
13
|
+
"participants": [],
|
|
14
|
+
"duration_min": null,
|
|
15
|
+
"thread_id": null,
|
|
16
|
+
"body": "A running list of the compliance horror stories I never want to repeat. Every entry is a real lesson dressed up as a war story: the audit we failed because nobody owned evidence collection, the vendor who shipped customer data to an unencrypted bucket, the deletion request that took three weeks because no one could say where the data actually lived. I keep these stories close because they are cheaper to learn from on paper than in a board meeting. The pattern is always the same — a control looked green on a dashboard while the reality underneath quietly rotted. When a new engineer joins Larkspur I walk them through this list first. Compliance is not paperwork; it is the difference between a calm quarter and a genuine horror story with our name on it. Add yours below, keep the collection honest, and please write the moral at the bottom so the next reader gets the lesson without living it."
|
|
17
|
+
},
|
|
18
|
+
{
|
|
19
|
+
"external_id": "demo-krisp-0002",
|
|
20
|
+
"source": "krisp",
|
|
21
|
+
"title": "SOC 2 Type II readiness sync — Sam & Priya",
|
|
22
|
+
"date": "2026-05-11",
|
|
23
|
+
"content_type": "transcript",
|
|
24
|
+
"tags": [
|
|
25
|
+
"soc2",
|
|
26
|
+
"compliance",
|
|
27
|
+
"audit"
|
|
28
|
+
],
|
|
29
|
+
"participants": [
|
|
30
|
+
"Sam Rivera",
|
|
31
|
+
"Priya Okafor"
|
|
32
|
+
],
|
|
33
|
+
"duration_min": 45,
|
|
34
|
+
"thread_id": null,
|
|
35
|
+
"body": "Priya walked me through where our SOC 2 Type II controls actually stand versus where the dashboard claims they stand, and the gap is uncomfortable. Change management has no consistent approval trail, access reviews are three months overdue, and our logging retention quietly resets every fourteen days. Priya has seen this movie before — she told me two compliance horror stories from other startups that failed their first Type II because evidence was reconstructed the night before fieldwork. We agreed to stop treating readiness as a checkbox and start treating it as a habit. Action items: assign a control owner for every trust-services criterion, automate evidence capture, and rehearse the auditor interview so nobody freezes. The stories Priya shared were sobering, but better a rehearsal now than a real horror when fieldwork starts. We meet again in two weeks to review the evidence backlog and confirm every criterion finally has a name attached to it."
|
|
36
|
+
},
|
|
37
|
+
{
|
|
38
|
+
"external_id": "demo-krisp-0003",
|
|
39
|
+
"source": "krisp",
|
|
40
|
+
"title": "Incident postmortem — payments outage",
|
|
41
|
+
"date": "2026-04-28",
|
|
42
|
+
"content_type": "transcript",
|
|
43
|
+
"tags": [
|
|
44
|
+
"incident",
|
|
45
|
+
"postmortem",
|
|
46
|
+
"compliance"
|
|
47
|
+
],
|
|
48
|
+
"participants": [
|
|
49
|
+
"Sam Rivera",
|
|
50
|
+
"Riley Nakamura"
|
|
51
|
+
],
|
|
52
|
+
"duration_min": 30,
|
|
53
|
+
"thread_id": null,
|
|
54
|
+
"body": "Riley led the blameless postmortem for the six-hour payments outage. Root cause: a config change disabled retries against the payment processor, and because our logging retention had silently dropped to seven days, we lost the evidence we needed to reconstruct the first ninety minutes. That missing window turned a bad night into a compliance problem — we could not prove to the auditor when the incident actually started. Riley reframed it as one of those on-call horror stories that teaches two lessons at once: fix the retry logic, and never let observability retention drift below the compliance floor. We are the sum of the stories we refuse to repeat. Action items: restore ninety-day retention, add a synthetic payment probe that pages before customers notice, and write the runbook we wished we had at two in the morning. Nobody was blamed, but everybody left the room with homework and a healthier respect for boring guardrails."
|
|
55
|
+
},
|
|
56
|
+
{
|
|
57
|
+
"external_id": "demo-slack-0004",
|
|
58
|
+
"source": "slack",
|
|
59
|
+
"title": "#compliance — audit war stories thread",
|
|
60
|
+
"date": "2026-05-14",
|
|
61
|
+
"content_type": "message",
|
|
62
|
+
"tags": [
|
|
63
|
+
"compliance",
|
|
64
|
+
"audit",
|
|
65
|
+
"war-stories"
|
|
66
|
+
],
|
|
67
|
+
"participants": [
|
|
68
|
+
"Sam Rivera",
|
|
69
|
+
"Marcus Chen"
|
|
70
|
+
],
|
|
71
|
+
"duration_min": null,
|
|
72
|
+
"thread_id": null,
|
|
73
|
+
"body": "sam.rivera: ok drop your worst-audit-ever war stories here, I'll start — the auditor asked for our access review evidence and we had a spreadsheet last touched eleven months ago\nmarcus.chen: rookie numbers. at my last shop the SOC 2 auditor found a service account with a password of 'password' still live in production. genuine horror.\nsam.rivera: that is a real horror story. mine at least was just neglect, not malice\nmarcus.chen: the compliance stories that keep me up are the boring ones — no evidence, no owner, no trail. malice is rare, entropy is constant\nsam.rivera: writing that on a sticky note, entropy is the real auditor\nmarcus.chen: exactly. every one of these stories is just a control nobody watered. let's turn the thread into a checklist before we forget the lesson\nsam.rivera: agreed. I'll collect the war stories into the horror-stories doc so new folks read them on day one"
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
"external_id": "demo-gmail-0005",
|
|
77
|
+
"source": "gmail",
|
|
78
|
+
"title": "Re: SOC 2 Type II — evidence request (URGENT)",
|
|
79
|
+
"date": "2026-05-09",
|
|
80
|
+
"content_type": "email",
|
|
81
|
+
"tags": [
|
|
82
|
+
"soc2",
|
|
83
|
+
"audit",
|
|
84
|
+
"compliance"
|
|
85
|
+
],
|
|
86
|
+
"participants": [
|
|
87
|
+
"Priya Okafor",
|
|
88
|
+
"Sam Rivera"
|
|
89
|
+
],
|
|
90
|
+
"duration_min": null,
|
|
91
|
+
"thread_id": null,
|
|
92
|
+
"body": "Sam,\n\nAhead of fieldwork next week I need the evidence package uploaded to the shared portal. There are forty artifacts on the request list; the ones most likely to slip are the quarterly access reviews, the change-management approvals for the last two release trains, and the vendor risk assessments for anyone touching customer data. Please prioritize those three.\n\nI know forty artifacts in forty-eight hours sounds brutal, but most of it already exists — it just needs to be exported, dated, and named consistently. Inconsistent file names are the number-one reason auditors send follow-up questions, which is what actually blows the timeline.\n\nIf a control genuinely has no evidence, tell me now rather than at fieldwork. A known gap with a remediation plan is a manageable finding; a surprise gap is a scramble. I will hold thirty minutes tomorrow to unblock anything you flag.\n\nBest,\nPriya Okafor\npriya.okafor@example.com"
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"external_id": "demo-gmail-0006",
|
|
96
|
+
"source": "gmail",
|
|
97
|
+
"title": "Vendra security review — findings",
|
|
98
|
+
"date": "2026-05-20",
|
|
99
|
+
"content_type": "email",
|
|
100
|
+
"tags": [
|
|
101
|
+
"vendor-risk",
|
|
102
|
+
"compliance",
|
|
103
|
+
"legal"
|
|
104
|
+
],
|
|
105
|
+
"participants": [
|
|
106
|
+
"Dana Whitfield",
|
|
107
|
+
"Sam Rivera"
|
|
108
|
+
],
|
|
109
|
+
"duration_min": null,
|
|
110
|
+
"thread_id": null,
|
|
111
|
+
"body": "Sam,\n\nLegal finished reviewing Vendra's security questionnaire and I have concerns before we counter-sign. Vendra could not produce a current SOC 2 report — they pointed to a Type I from two years ago and a Type II 'in progress.' Their answers on data residency were vague, and they declined to commit to a breach-notification window shorter than thirty days, which is outside our standard.\n\nNone of this is automatically disqualifying, but it changes the shape of the deal. I want a security addendum that pins a seventy-two-hour breach-notification clause, a right-to-audit provision, and a hard requirement that customer data stays in-region. If Vendra will not sign that, we should treat this as a vendor-risk red flag and escalate to the risk committee.\n\nCan you loop in Jordan on their side so we are negotiating from the same document? I would rather fix this in the contract now than discover it during an incident later.\n\nDana Whitfield\ndana.whitfield@example.com"
|
|
112
|
+
},
|
|
113
|
+
{
|
|
114
|
+
"external_id": "demo-krisp-0007",
|
|
115
|
+
"source": "krisp",
|
|
116
|
+
"title": "PCI scope creep review — Marcus & Sam",
|
|
117
|
+
"date": "2026-06-01",
|
|
118
|
+
"content_type": "transcript",
|
|
119
|
+
"tags": [
|
|
120
|
+
"pci",
|
|
121
|
+
"compliance",
|
|
122
|
+
"scope-creep"
|
|
123
|
+
],
|
|
124
|
+
"participants": [
|
|
125
|
+
"Marcus Chen",
|
|
126
|
+
"Sam Rivera"
|
|
127
|
+
],
|
|
128
|
+
"duration_min": 40,
|
|
129
|
+
"thread_id": null,
|
|
130
|
+
"body": "Marcus flagged that a new checkout API is quietly pulling almost our entire production environment into PCI scope. The moment that endpoint started handling raw card numbers instead of tokens, every system it touches inherited the compliance burden — logging, network segmentation, quarterly scans, the works. What should have been a narrow cardholder-data enclave is now sprawling. Marcus called it a classic scope-creep horror story: nobody decided to expand PCI scope, it just crept because one shortcut skipped tokenization. We traded a two-week integration for a six-month audit surface. The fix is to reintroduce the tokenization proxy so raw card data never lands in our services, shrinking scope back to the enclave. These are the stories I want in the postmortem library so the next team feels the cost before they take the shortcut. Action items: front the tokenization proxy, redraw the network diagram, and brief the assessor before the next scan so nothing surprises them."
|
|
131
|
+
},
|
|
132
|
+
{
|
|
133
|
+
"external_id": "demo-slack-0008",
|
|
134
|
+
"source": "slack",
|
|
135
|
+
"title": "#security — GDPR deletion request panic",
|
|
136
|
+
"date": "2026-05-22",
|
|
137
|
+
"content_type": "message",
|
|
138
|
+
"tags": [
|
|
139
|
+
"gdpr",
|
|
140
|
+
"compliance",
|
|
141
|
+
"privacy"
|
|
142
|
+
],
|
|
143
|
+
"participants": [
|
|
144
|
+
"Sam Rivera",
|
|
145
|
+
"Dana Whitfield"
|
|
146
|
+
],
|
|
147
|
+
"duration_min": null,
|
|
148
|
+
"thread_id": null,
|
|
149
|
+
"body": "dana.whitfield: we got a GDPR erasure request and the clock is ticking — thirty days to prove the person is gone from every system\nsam.rivera: define every system, because that's the scary part. prod db, the analytics warehouse, backups, the email tool, and the support desk all hold copies\ndana.whitfield: five systems, five different deletion mechanisms, and backups can't be selectively purged\nsam.rivera: right, so the compliance answer is a documented retention policy that ages backups out, plus a deletion runbook for the four live systems\ndana.whitfield: can we actually attest deletion today or are we improvising\nsam.rivera: improvising, which is exactly why this keeps happening. let's build the runbook now so the next erasure request is boring instead of a fire drill\ndana.whitfield: agreed, boring is the goal. I'll draft the retention policy, you map the five systems and their deletion paths"
|
|
150
|
+
},
|
|
151
|
+
{
|
|
152
|
+
"external_id": "demo-manual-0009",
|
|
153
|
+
"source": "manual",
|
|
154
|
+
"title": "Larkspur security controls cheatsheet",
|
|
155
|
+
"date": "2026-03-30",
|
|
156
|
+
"content_type": "note",
|
|
157
|
+
"tags": [
|
|
158
|
+
"soc2",
|
|
159
|
+
"controls",
|
|
160
|
+
"reference"
|
|
161
|
+
],
|
|
162
|
+
"participants": [],
|
|
163
|
+
"duration_min": null,
|
|
164
|
+
"thread_id": null,
|
|
165
|
+
"body": "A one-page reference for the controls we care about most, so nobody has to rediscover them mid-audit. Access management: SSO everywhere, quarterly access reviews, no shared accounts. Change management: every production change rides a pull request with a second reviewer and an automated deploy record. Logging: ninety-day retention minimum, centralized and tamper-evident. Encryption: TLS in transit, disk encryption at rest, secrets in the vault and never in code. Vendor management: a signed security addendum and a current attestation before any vendor touches customer data. Incident response: a named on-call, a severity rubric, and a blameless postmortem for anything customer-facing. Backups: encrypted, tested quarterly, with a documented retention schedule. None of this is exotic; the value is in doing it consistently and being able to show the evidence on demand. Print it, pin it above your desk, and update it the day a control changes. Consistency beats heroics, and a boring cheatsheet beats a clever memory every single time."
|
|
166
|
+
},
|
|
167
|
+
{
|
|
168
|
+
"external_id": "demo-gmail-0010",
|
|
169
|
+
"source": "gmail",
|
|
170
|
+
"title": "Board update — audit status",
|
|
171
|
+
"date": "2026-06-05",
|
|
172
|
+
"content_type": "email",
|
|
173
|
+
"tags": [
|
|
174
|
+
"audit",
|
|
175
|
+
"board",
|
|
176
|
+
"soc2"
|
|
177
|
+
],
|
|
178
|
+
"participants": [
|
|
179
|
+
"Sam Rivera"
|
|
180
|
+
],
|
|
181
|
+
"duration_min": null,
|
|
182
|
+
"thread_id": null,
|
|
183
|
+
"body": "Board,\n\nA short update on our audit posture heading into the second half. We are on track for our SOC 2 Type II observation window to close on schedule. The evidence backlog that worried me last quarter is now largely automated — access reviews, change approvals, and logging retention all generate their own trail without a human chasing screenshots.\n\nTwo open items I want visible at the board level. First, a vendor security review surfaced gaps with one of our data processors; legal is negotiating a stronger security addendum and I will report the outcome next month. Second, a checkout integration briefly expanded our PCI scope, and we have a remediation underway to shrink it back.\n\nNeither item is a fire, but I would rather you hear about them from me early than in a findings report later. Our compliance maturity is trending in the right direction, and the automation investment is already paying for itself in reduced audit thrash.\n\nSam Rivera\nsam.rivera@example.com"
|
|
184
|
+
},
|
|
185
|
+
{
|
|
186
|
+
"external_id": "demo-krisp-0011",
|
|
187
|
+
"source": "krisp",
|
|
188
|
+
"title": "Vendor risk committee — Q2",
|
|
189
|
+
"date": "2026-05-18",
|
|
190
|
+
"content_type": "transcript",
|
|
191
|
+
"tags": [
|
|
192
|
+
"vendor-risk",
|
|
193
|
+
"compliance",
|
|
194
|
+
"risk"
|
|
195
|
+
],
|
|
196
|
+
"participants": [
|
|
197
|
+
"Marcus Chen",
|
|
198
|
+
"Sam Rivera"
|
|
199
|
+
],
|
|
200
|
+
"duration_min": 45,
|
|
201
|
+
"thread_id": null,
|
|
202
|
+
"body": "The Q2 vendor risk committee reviewed our top data processors. Vendra dominated the conversation — no current attestation, vague data-residency answers, and a thirty-day breach clause we will not accept. Marcus then told the committee a genuine vendor horror story from his last company: a marketing tool with over-broad access was breached, and because the contract had no notification clause, they learned about it from a customer, not the vendor. That single story reset the room's risk appetite. We agreed compliance for vendors has to be contractual, not aspirational: current attestation on file, right-to-audit, and a seventy-two-hour breach window or we walk. These stories are exactly why the committee exists — to price risk before it becomes an incident with our name on it. Action items: put Vendra on a remediation clock, standardize the security addendum across all processors, and re-score the portfolio next quarter. A vendor breach becomes our headline whether or not the contract admits it."
|
|
203
|
+
},
|
|
204
|
+
{
|
|
205
|
+
"external_id": "demo-slack-0012",
|
|
206
|
+
"source": "slack",
|
|
207
|
+
"title": "#eng — logging retention gotcha",
|
|
208
|
+
"date": "2026-04-30",
|
|
209
|
+
"content_type": "message",
|
|
210
|
+
"tags": [
|
|
211
|
+
"incident",
|
|
212
|
+
"logging",
|
|
213
|
+
"eng"
|
|
214
|
+
],
|
|
215
|
+
"participants": [
|
|
216
|
+
"Riley Nakamura",
|
|
217
|
+
"Sam Rivera"
|
|
218
|
+
],
|
|
219
|
+
"duration_min": null,
|
|
220
|
+
"thread_id": null,
|
|
221
|
+
"body": "riley.nakamura: PSA for everyone — the logging retention gotcha that bit us in the payments outage is a config default, not a bug\nsam.rivera: the one where retention silently drops to seven days?\nriley.nakamura: that one. the log shipper resets to its packaged default whenever the config map is recreated, and nobody notices until you need the old logs\nsam.rivera: which is always the worst possible moment. see the payments postmortem for the full write-up\nriley.nakamura: exactly. I've pinned an alert that fires if retention drops below ninety days so it can't drift silently again\nsam.rivera: this is the kind of compliance guardrail I love — invisible until the day it saves you\nriley.nakamura: cheap insurance. the alert took an hour, the outage cost us six. adding it to the controls cheatsheet so it outlives both of us"
|
|
222
|
+
},
|
|
223
|
+
{
|
|
224
|
+
"external_id": "demo-gmail-0013",
|
|
225
|
+
"source": "gmail",
|
|
226
|
+
"title": "Re: PCI DSS 4.0 gap assessment",
|
|
227
|
+
"date": "2026-05-25",
|
|
228
|
+
"content_type": "email",
|
|
229
|
+
"tags": [
|
|
230
|
+
"pci",
|
|
231
|
+
"compliance"
|
|
232
|
+
],
|
|
233
|
+
"participants": [
|
|
234
|
+
"Marcus Chen",
|
|
235
|
+
"Sam Rivera"
|
|
236
|
+
],
|
|
237
|
+
"duration_min": null,
|
|
238
|
+
"thread_id": null,
|
|
239
|
+
"body": "Sam,\n\nI finished a first pass at our PCI DSS 4.0 gap assessment and the headline is good news with an asterisk. Most of the tightened 4.0 requirements — stronger authentication, more granular access, and the new targeted-risk-analysis language — we already meet because of the SOC 2 work. The asterisk is scope. As long as the checkout integration keeps raw card data in our environment, the number of in-scope systems roughly triples, and so does the assessment cost.\n\nMy recommendation is to treat the tokenization remediation as the single highest-leverage compliance investment this quarter. Shrinking scope back to the cardholder-data enclave does more for our 4.0 readiness than any individual control tweak.\n\nI have attached the requirement-by-requirement matrix. The red rows are all downstream of scope, not of missing controls, which is the encouraging part. Let's align with the assessor before the next quarterly scan so we are not surprised.\n\nMarcus Chen\nmarcus.chen@example.com"
|
|
240
|
+
},
|
|
241
|
+
{
|
|
242
|
+
"external_id": "demo-manual-0014",
|
|
243
|
+
"source": "manual",
|
|
244
|
+
"title": "Interview prep — tell me about a compliance failure",
|
|
245
|
+
"date": "2026-06-08",
|
|
246
|
+
"content_type": "note",
|
|
247
|
+
"tags": [
|
|
248
|
+
"interview-prep",
|
|
249
|
+
"compliance",
|
|
250
|
+
"career"
|
|
251
|
+
],
|
|
252
|
+
"participants": [],
|
|
253
|
+
"duration_min": null,
|
|
254
|
+
"thread_id": null,
|
|
255
|
+
"body": "STAR notes for the inevitable 'tell me about a time a compliance effort failed' question. Situation: we walked into our first SOC 2 Type II observation window with controls that looked green on a dashboard but had no evidence trail underneath. Task: I owned readiness and had to turn a paperwork exercise into an operating habit before fieldwork. Action: I assigned a control owner to every trust-services criterion, automated evidence capture so nobody was chasing screenshots, and ran a mock auditor interview so the team would not freeze under real questions. Result: we closed the window with a small, well-documented set of findings and a remediation plan the auditor accepted, instead of a last-minute scramble.\n\nThe lesson I lead with: compliance fails quietly, as entropy, not as malice. The teams that pass treat controls like habits they water daily. Keep the answer specific, own the early mistake honestly, and finish on the system you built so the failure cannot recur next cycle."
|
|
256
|
+
},
|
|
257
|
+
{
|
|
258
|
+
"external_id": "demo-krisp-0015",
|
|
259
|
+
"source": "krisp",
|
|
260
|
+
"title": "SOC 2 kickoff — scoping call",
|
|
261
|
+
"date": "2026-04-15",
|
|
262
|
+
"content_type": "transcript",
|
|
263
|
+
"tags": [
|
|
264
|
+
"soc2",
|
|
265
|
+
"kickoff",
|
|
266
|
+
"compliance"
|
|
267
|
+
],
|
|
268
|
+
"participants": [
|
|
269
|
+
"Priya Okafor",
|
|
270
|
+
"Sam Rivera"
|
|
271
|
+
],
|
|
272
|
+
"duration_min": 35,
|
|
273
|
+
"thread_id": null,
|
|
274
|
+
"body": "Kickoff call with Priya to scope our SOC 2 Type II engagement. We agreed on the trust-services criteria in scope: security is mandatory, and we are adding availability and confidentiality because our customers ask about uptime and data handling. Privacy stays out of scope for this cycle to keep the first observation window achievable. Priya walked through the timeline — a three-month observation window, then fieldwork — and set expectations that the work is mostly evidence discipline, not heroics. We mapped the systems in scope: the production platform, the deploy pipeline, the logging stack, and the vendor processors that touch customer data. Priya's guidance was to start capturing evidence on day one rather than reconstructing it later, because reconstruction is where compliance programs quietly go wrong. Action items: finalize the system description, name control owners, and stand up the evidence portal before the window opens. A calm, well-scoped kickoff sets the tone for the entire engagement, and Priya has run enough of these to know."
|
|
275
|
+
},
|
|
276
|
+
{
|
|
277
|
+
"external_id": "demo-slack-0016",
|
|
278
|
+
"source": "slack",
|
|
279
|
+
"title": "#compliance — evidence automation idea",
|
|
280
|
+
"date": "2026-06-03",
|
|
281
|
+
"content_type": "message",
|
|
282
|
+
"tags": [
|
|
283
|
+
"compliance",
|
|
284
|
+
"automation",
|
|
285
|
+
"ideas"
|
|
286
|
+
],
|
|
287
|
+
"participants": [
|
|
288
|
+
"Sam Rivera"
|
|
289
|
+
],
|
|
290
|
+
"duration_min": null,
|
|
291
|
+
"thread_id": null,
|
|
292
|
+
"body": "sam.rivera: idea I can't shake — most of our audit pain is evidence collection, so what if we automated it end to end?\nsam.rivera: every access review, every change approval, every backup test already emits a signal. we just don't capture it in one place\nsam.rivera: imagine a job that snapshots the evidence weekly, timestamps it, and drops it in the portal auditors already use. no human chasing screenshots\nsam.rivera: the compliance win is huge — evidence stops being a fire drill and becomes a byproduct of doing the work\nsam.rivera: bonus, it kills the number-one auditor follow-up, which is inconsistent or undated artifacts\nsam.rivera: going to prototype this against the SOC 2 request list and see how many of the forty artifacts we can generate automatically. if it is even half, that is a week of my life back every quarter"
|
|
293
|
+
},
|
|
294
|
+
{
|
|
295
|
+
"external_id": "demo-gmail-0017",
|
|
296
|
+
"source": "gmail",
|
|
297
|
+
"title": "Re: Vendra contract — security addendum",
|
|
298
|
+
"date": "2026-05-28",
|
|
299
|
+
"content_type": "email",
|
|
300
|
+
"tags": [
|
|
301
|
+
"vendor-risk",
|
|
302
|
+
"legal",
|
|
303
|
+
"contract"
|
|
304
|
+
],
|
|
305
|
+
"participants": [
|
|
306
|
+
"Jordan Alvarez",
|
|
307
|
+
"Dana Whitfield",
|
|
308
|
+
"Sam Rivera"
|
|
309
|
+
],
|
|
310
|
+
"duration_min": null,
|
|
311
|
+
"thread_id": null,
|
|
312
|
+
"body": "Sam and Dana,\n\nThanks for sending the redlined security addendum. I have taken it to our security lead at Vendra and here is where we land. We can commit to the seventy-two-hour breach-notification window and the data-residency requirement — both are already close to our internal standard. The right-to-audit clause is where we need to negotiate; an unlimited annual on-site audit is a lot for a company our size, so we would propose one scheduled audit per year plus our SOC 2 report on request once the Type II completes next quarter.\n\nI think that gets us most of the way to what Dana's risk committee wants without either side signing something we cannot operationally honor. If the once-yearly cadence works, I can turn the addendum around by end of week.\n\nHappy to jump on a call if it is faster.\n\nBest,\nJordan Alvarez\njordan.alvarez@example.com"
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
"external_id": "demo-manual-0018",
|
|
316
|
+
"source": "manual",
|
|
317
|
+
"title": "Postmortem template + blameless norms",
|
|
318
|
+
"date": "2026-04-10",
|
|
319
|
+
"content_type": "note",
|
|
320
|
+
"tags": [
|
|
321
|
+
"postmortem",
|
|
322
|
+
"template",
|
|
323
|
+
"process"
|
|
324
|
+
],
|
|
325
|
+
"participants": [],
|
|
326
|
+
"duration_min": null,
|
|
327
|
+
"thread_id": null,
|
|
328
|
+
"body": "Our standard postmortem template and the norms that make it work. Header: incident name, severity, start and end time, and the customer impact in one plain sentence. Timeline: a factual sequence of events with timestamps, written without blame or hindsight language. Root cause: the honest technical and organizational reasons, plural — single-cause postmortems are usually incomplete. Contributing factors: the conditions that made the incident possible or worse. What went well: the recovery steps and detection that actually helped, so we reinforce them. Action items: each with an owner and a due date, tracked to completion, not filed and forgotten.\n\nThe norms matter more than the template. We assume everyone acted reasonably with the information they had. We critique systems, not people. We write for the engineer who joins next year and needs to learn without shame. A blameless culture is what makes people report the near-misses, and near-misses are the cheapest lessons we ever get. Protect that, and the template mostly fills itself in."
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
"external_id": "demo-krisp-0019",
|
|
332
|
+
"source": "krisp",
|
|
333
|
+
"title": "Weekly staff — risk register review",
|
|
334
|
+
"date": "2026-05-30",
|
|
335
|
+
"content_type": "transcript",
|
|
336
|
+
"tags": [
|
|
337
|
+
"risk",
|
|
338
|
+
"compliance",
|
|
339
|
+
"staff"
|
|
340
|
+
],
|
|
341
|
+
"participants": [
|
|
342
|
+
"Marcus Chen",
|
|
343
|
+
"Sam Rivera"
|
|
344
|
+
],
|
|
345
|
+
"duration_min": 30,
|
|
346
|
+
"thread_id": null,
|
|
347
|
+
"body": "Weekly staff meeting, risk-register review. Marcus walked the leadership team through the top five risks and the near-misses from the past month. The one that got attention was a near-miss where a deploy almost shipped a logging change that would have dropped our retention below the compliance floor — caught in review by the new alert Riley added. Marcus made the point that a near-miss is a free lesson: the same signal as an incident with none of the cost. We agreed to start logging near-misses in the risk register alongside real incidents so the pattern is visible over time. Other items: the Vendra remediation clock is ticking, PCI scope reduction is on track, and the SOC 2 evidence backlog is now automated. The register is healthier than last quarter, but Marcus reminded everyone that a quiet register is earned weekly, not granted permanently. Action item: add a near-miss column and start populating it this sprint."
|
|
348
|
+
},
|
|
349
|
+
{
|
|
350
|
+
"external_id": "demo-slack-0020",
|
|
351
|
+
"source": "slack",
|
|
352
|
+
"title": "#random — worst on-call ever",
|
|
353
|
+
"date": "2026-05-05",
|
|
354
|
+
"content_type": "message",
|
|
355
|
+
"tags": [
|
|
356
|
+
"on-call",
|
|
357
|
+
"incident",
|
|
358
|
+
"war-stories"
|
|
359
|
+
],
|
|
360
|
+
"participants": [
|
|
361
|
+
"Riley Nakamura",
|
|
362
|
+
"Sam Rivera"
|
|
363
|
+
],
|
|
364
|
+
"duration_min": null,
|
|
365
|
+
"thread_id": null,
|
|
366
|
+
"body": "riley.nakamura: #random poll — what is your worst on-call ever? I'll go: paged at three in the morning because a certificate expired that nobody owned, fixed it, then got paged again an hour later because the fix rotated a second cert that ALSO nobody owned\nsam.rivera: that is a genuine horror story\nriley.nakamura: it gets better — the expired cert was on the log shipper, so we had a gap in the exact logs we needed, which turned an ops incident into a compliance one\nsam.rivera: the crossover episode nobody asked for. on-call horror meets audit finding\nriley.nakamura: the moral of most of my on-call stories is the same: unowned things fail at the worst time. every cert, every job, every control needs a name on it\nsam.rivera: adding 'ownership' to the compliance cheatsheet in giant letters. best worst-on-call story yet, honestly"
|
|
367
|
+
},
|
|
368
|
+
{
|
|
369
|
+
"external_id": "demo-gmail-0021",
|
|
370
|
+
"source": "gmail",
|
|
371
|
+
"title": "SOC 2 report DELIVERED",
|
|
372
|
+
"date": "2026-06-12",
|
|
373
|
+
"content_type": "email",
|
|
374
|
+
"tags": [
|
|
375
|
+
"soc2",
|
|
376
|
+
"milestone",
|
|
377
|
+
"compliance"
|
|
378
|
+
],
|
|
379
|
+
"participants": [
|
|
380
|
+
"Priya Okafor",
|
|
381
|
+
"Sam Rivera"
|
|
382
|
+
],
|
|
383
|
+
"duration_min": null,
|
|
384
|
+
"thread_id": null,
|
|
385
|
+
"body": "Sam,\n\nIt is done — your SOC 2 Type II report is final and uploaded to the portal. Congratulations to you and the whole Larkspur team. This was one of the cleaner first-time engagements I have run: a small number of well-documented findings, each with a remediation plan already in motion, and no last-minute evidence scramble because you automated capture early.\n\nA few notes for next cycle. Keep the evidence automation running — the thing that makes a second Type II easy is never letting the trail go cold. Close out the two open remediations, the vendor addendum and the PCI scope reduction, before the next window opens so they do not reappear as repeat findings. And keep telling the team why this matters; culture is the control that holds all the others up.\n\nIt has been a pleasure working with you. Share the report with your board and your customers with confidence.\n\nBest,\nPriya Okafor\npriya.okafor@example.com"
|
|
386
|
+
},
|
|
387
|
+
{
|
|
388
|
+
"external_id": "demo-manual-0022",
|
|
389
|
+
"source": "manual",
|
|
390
|
+
"title": "Lessons learned — audit season retro",
|
|
391
|
+
"date": "2026-06-15",
|
|
392
|
+
"content_type": "note",
|
|
393
|
+
"tags": [
|
|
394
|
+
"retro",
|
|
395
|
+
"compliance",
|
|
396
|
+
"lessons-learned"
|
|
397
|
+
],
|
|
398
|
+
"participants": [],
|
|
399
|
+
"duration_min": null,
|
|
400
|
+
"thread_id": null,
|
|
401
|
+
"body": "Audit season is over; here is what I want to carry forward. The single biggest lever was evidence automation — the moment collecting evidence became a byproduct of doing the work instead of a quarterly fire drill, the whole program relaxed. Second lever: ownership. Every control, cert, vendor, and job that caused pain this year did so because nobody owned it, and naming an owner is the cheapest reliability and compliance upgrade we have. Third: contracts beat hope. The vendor risk that scared us most was the one with no notification clause, and the fix was legal, not technical. Finally, culture. Blameless postmortems and honest risk reviews are what surfaced the near-misses early enough to fix them cheaply.\n\nThe throughline across every meeting, thread, and postmortem this cycle is the same: compliance is not a document you produce at the end, it is a set of habits you maintain all year. We got measurably better at the habits. Let's not let the trail go cold over the summer."
|
|
402
|
+
}
|
|
403
|
+
]
|
brain/demo/embedder.py
ADDED
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
"""Deterministic, Ollama-free embedder for the `brain demo` sandbox.
|
|
2
|
+
|
|
3
|
+
:class:`DemoEmbedder` hashes each text into a stable unit-norm vector via a
|
|
4
|
+
sha256-seeded PRNG. It satisfies the :class:`brain.ingest.Embedder` Protocol
|
|
5
|
+
(``dim`` / ``embed`` / ``count_tokens``) so the demo reuses the real ingest +
|
|
6
|
+
search pipeline unchanged — but produces vectors instantly and offline, with no
|
|
7
|
+
model download and no network I/O. Token counting uses the same offline
|
|
8
|
+
``tiktoken`` ``cl100k_base`` tokenizer every production backend uses for chunk
|
|
9
|
+
budgeting.
|
|
10
|
+
"""
|
|
11
|
+
from __future__ import annotations
|
|
12
|
+
|
|
13
|
+
import hashlib
|
|
14
|
+
import math
|
|
15
|
+
import random
|
|
16
|
+
|
|
17
|
+
import tiktoken
|
|
18
|
+
|
|
19
|
+
# Default output dimension. 1024 matches the arctic/voyage schema so the demo
|
|
20
|
+
# exercises the ≤2000-dim (HNSW-indexable) code path in
|
|
21
|
+
# ``queries.finalize_embedding_index``.
|
|
22
|
+
DEFAULT_DEMO_DIM = 1024
|
|
23
|
+
|
|
24
|
+
# Bytes of the sha256 digest folded into the PRNG seed. 8 bytes (64 bits) is
|
|
25
|
+
# ample entropy to make distinct texts map to distinct vectors.
|
|
26
|
+
_SEED_BYTES = 8
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
class DemoEmbedder:
|
|
30
|
+
"""Deterministic hash-vector embedder (no Ollama, no network).
|
|
31
|
+
|
|
32
|
+
``produces_embeddings`` advertises that this backend yields real vectors
|
|
33
|
+
(as opposed to an FTS-only null backend), so callers that branch on
|
|
34
|
+
embedding availability can treat the demo as vector-capable.
|
|
35
|
+
"""
|
|
36
|
+
|
|
37
|
+
produces_embeddings = True
|
|
38
|
+
|
|
39
|
+
def __init__(self, dim: int = DEFAULT_DEMO_DIM) -> None:
|
|
40
|
+
if dim < 1:
|
|
41
|
+
raise ValueError(f"DemoEmbedder dim must be >= 1 (got {dim})")
|
|
42
|
+
self.dim = dim
|
|
43
|
+
self._tokenizer = tiktoken.get_encoding("cl100k_base")
|
|
44
|
+
|
|
45
|
+
def embed(
|
|
46
|
+
self, texts: list[str], *, input_type: str = "document"
|
|
47
|
+
) -> list[list[float]]:
|
|
48
|
+
"""Return one stable unit-norm vector per input text.
|
|
49
|
+
|
|
50
|
+
``input_type`` is folded into the hash so a text embedded as a query
|
|
51
|
+
differs from the same text embedded as a document — mirroring the
|
|
52
|
+
prompt-side asymmetry of the real backends — while staying fully
|
|
53
|
+
deterministic. An empty input list returns an empty list.
|
|
54
|
+
"""
|
|
55
|
+
return [self._vector(text, input_type) for text in texts]
|
|
56
|
+
|
|
57
|
+
def count_tokens(self, text: str) -> int:
|
|
58
|
+
"""Return the token count per the offline ``cl100k_base`` tokenizer."""
|
|
59
|
+
return len(self._tokenizer.encode(text))
|
|
60
|
+
|
|
61
|
+
def _vector(self, text: str, input_type: str) -> list[float]:
|
|
62
|
+
"""Build the deterministic unit-norm vector for ``(input_type, text)``."""
|
|
63
|
+
digest = hashlib.sha256(f"{input_type}:{text}".encode()).digest()
|
|
64
|
+
seed = int.from_bytes(digest[:_SEED_BYTES], "big")
|
|
65
|
+
rng = random.Random(seed)
|
|
66
|
+
raw = [rng.gauss(0.0, 1.0) for _ in range(self.dim)]
|
|
67
|
+
norm = math.sqrt(sum(component * component for component in raw))
|
|
68
|
+
if norm == 0.0:
|
|
69
|
+
# Astronomically unlikely (all-zero gaussians); return a valid unit
|
|
70
|
+
# vector along the first axis rather than divide by zero.
|
|
71
|
+
unit = [0.0] * self.dim
|
|
72
|
+
unit[0] = 1.0
|
|
73
|
+
return unit
|
|
74
|
+
return [component / norm for component in raw]
|
brain/durations.py
ADDED
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
"""Shared ``--since`` duration parsing for CLI lookback windows.
|
|
2
|
+
|
|
3
|
+
Every ``brain`` command with a ``--since`` window accepts either a bare number
|
|
4
|
+
— interpreted in that command's *native* unit (days for most commands, hours
|
|
5
|
+
for ``brain brief --since``) — or an explicit suffix that is unit-independent:
|
|
6
|
+
``7d`` (days), ``24h`` (hours), ``90m`` (minutes). Centralising the grammar
|
|
7
|
+
here keeps every command on one set of accepted forms and one set of
|
|
8
|
+
rejections, and lets bare numbers keep their historical per-command meaning
|
|
9
|
+
(zero breaking changes).
|
|
10
|
+
"""
|
|
11
|
+
import re
|
|
12
|
+
from datetime import timedelta
|
|
13
|
+
from typing import Literal
|
|
14
|
+
|
|
15
|
+
import typer
|
|
16
|
+
|
|
17
|
+
# Bare numbers are read in the command's native unit; suffixes override it.
|
|
18
|
+
_BareUnit = Literal["days", "hours"]
|
|
19
|
+
|
|
20
|
+
# ``<digits>`` with an optional single ``d``/``h``/``m`` suffix. ``fullmatch``
|
|
21
|
+
# rejects a leading sign ("-3d"), a leading letter ("d7"), the empty string,
|
|
22
|
+
# decimals ("7.5"), and doubled suffixes ("7dd").
|
|
23
|
+
_SINCE_RE = re.compile(r"(?P<value>\d+)(?P<unit>[dhm])?")
|
|
24
|
+
|
|
25
|
+
_SECONDS_PER: dict[_BareUnit, int] = {"days": 86_400, "hours": 3_600}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
def parse_since(value: str, *, bare_unit: _BareUnit) -> timedelta:
|
|
29
|
+
"""Parse a ``--since`` argument into a :class:`~datetime.timedelta`.
|
|
30
|
+
|
|
31
|
+
A bare number (``"7"``) is interpreted in ``bare_unit``; a suffixed value
|
|
32
|
+
(``"7d"`` / ``"24h"`` / ``"90m"``) is unit-independent and always wins.
|
|
33
|
+
|
|
34
|
+
Raises:
|
|
35
|
+
typer.BadParameter: When ``value`` is empty, negative, non-integer, or
|
|
36
|
+
otherwise not ``<digits>[d|h|m]`` (e.g. ``""``, ``"d7"``, ``"-3d"``).
|
|
37
|
+
"""
|
|
38
|
+
text = value.strip()
|
|
39
|
+
match = _SINCE_RE.fullmatch(text)
|
|
40
|
+
if match is None:
|
|
41
|
+
raise typer.BadParameter(
|
|
42
|
+
f"invalid duration {value!r}; expected a bare number (interpreted "
|
|
43
|
+
f"as {bare_unit}) or a suffixed value like '7d', '24h', '90m'"
|
|
44
|
+
)
|
|
45
|
+
magnitude = int(match.group("value"))
|
|
46
|
+
unit = match.group("unit")
|
|
47
|
+
if unit == "d":
|
|
48
|
+
return timedelta(days=magnitude)
|
|
49
|
+
if unit == "h":
|
|
50
|
+
return timedelta(hours=magnitude)
|
|
51
|
+
if unit == "m":
|
|
52
|
+
return timedelta(minutes=magnitude)
|
|
53
|
+
# Bare number — read in the command's native unit.
|
|
54
|
+
if bare_unit == "days":
|
|
55
|
+
return timedelta(days=magnitude)
|
|
56
|
+
return timedelta(hours=magnitude)
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
def whole_units(duration: timedelta, *, unit: _BareUnit) -> int:
|
|
60
|
+
"""Round ``duration`` UP to whole ``unit``s (days or hours).
|
|
61
|
+
|
|
62
|
+
Downstream consumers take an integer day/hour window bound straight into
|
|
63
|
+
SQL, so a fractional window has to become a whole number. Rounding *up*
|
|
64
|
+
keeps a positive sub-unit window (e.g. ``90m`` in a days-unit command) from
|
|
65
|
+
collapsing to ``0`` — which every consumer treats as "no window". A value
|
|
66
|
+
already sitting on a whole-``unit`` boundary (every bare number, and any
|
|
67
|
+
coarser-or-equal suffix) converts exactly.
|
|
68
|
+
"""
|
|
69
|
+
# ``parse_since`` only ever yields whole days/hours/minutes, so microseconds
|
|
70
|
+
# are always zero — integer arithmetic here is exact (no float rounding).
|
|
71
|
+
total_seconds = duration.days * 86_400 + duration.seconds
|
|
72
|
+
per = _SECONDS_PER[unit]
|
|
73
|
+
whole, remainder = divmod(total_seconds, per)
|
|
74
|
+
return whole + (1 if remainder else 0)
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
def since_window(value: str, *, unit: _BareUnit) -> int:
|
|
78
|
+
"""Parse a ``--since`` value and return its whole-``unit`` window.
|
|
79
|
+
|
|
80
|
+
The one-call CLI convenience: ``since_window("24h", unit="days") == 1``.
|
|
81
|
+
``unit`` is both the bare-number interpretation and the output unit (they
|
|
82
|
+
are always the same for a given command).
|
|
83
|
+
"""
|
|
84
|
+
return whole_units(parse_since(value, bare_unit=unit), unit=unit)
|