mirror of
https://github.com/rowboatlabs/rowboat.git
synced 2026-04-28 01:46:23 +02:00
feat(suggested-topics): populate and integrate suggested topics
This commit is contained in:
parent
e9cdd3f6eb
commit
eaab438666
6 changed files with 448 additions and 66 deletions
|
|
@ -180,6 +180,21 @@ Workflow:
|
|||
|
||||
Ask one question: "Which note should this track live in?" Don't create a new note unless the user asks.
|
||||
|
||||
### Suggested Topics exploration flow
|
||||
|
||||
Sometimes the user arrives from the Suggested Topics panel and gives you a prompt like:
|
||||
- "I am exploring a suggested topic card from the Suggested Topics panel."
|
||||
- a title, category, description, and target folder such as ` + "`" + `knowledge/Topics/` + "`" + ` or ` + "`" + `knowledge/People/` + "`" + `
|
||||
|
||||
In that flow:
|
||||
1. On the first turn, **do not create or modify anything yet**. Briefly explain the tracking note you can set up and ask for confirmation.
|
||||
2. If the user clearly confirms ("yes", "set it up", "do it"), treat that as explicit permission to proceed.
|
||||
3. Before creating a new note, search the target folder for an existing matching note and update it if one already exists.
|
||||
4. If no matching note exists and the prompt gave you a target folder, create the new note there without bouncing back to ask "which note should this live in?".
|
||||
5. Use the card title as the default note title / filename unless a small normalization is clearly needed.
|
||||
6. Keep the surrounding note scaffolding minimal but useful. The track block should be the core of the note.
|
||||
7. If the target folder is one of the structured knowledge folders (` + "`" + `knowledge/People/` + "`" + `, ` + "`" + `knowledge/Organizations/` + "`" + `, ` + "`" + `knowledge/Projects/` + "`" + `, ` + "`" + `knowledge/Topics/` + "`" + `), mirror the local note style by quickly checking a nearby note or config before writing if needed.
|
||||
|
||||
## The Exact Text to Insert
|
||||
|
||||
Write it verbatim like this (including the blank line between fence and target):
|
||||
|
|
|
|||
|
|
@ -25,6 +25,12 @@ import { getTagDefinitions } from './tag_system.js';
|
|||
|
||||
const NOTES_OUTPUT_DIR = path.join(WorkDir, 'knowledge');
|
||||
const NOTE_CREATION_AGENT = 'note_creation';
|
||||
const SUGGESTED_TOPICS_REL_PATH = 'suggested-topics.md';
|
||||
const SUGGESTED_TOPICS_PATH = path.join(WorkDir, 'suggested-topics.md');
|
||||
const LEGACY_SUGGESTED_TOPICS_REL_PATH = 'config/suggested-topics.md';
|
||||
const LEGACY_SUGGESTED_TOPICS_PATH = path.join(WorkDir, 'config', 'suggested-topics.md');
|
||||
const LEGACY_SUGGESTED_TOPICS_KNOWLEDGE_REL_PATH = 'knowledge/Notes/Suggested Topics.md';
|
||||
const LEGACY_SUGGESTED_TOPICS_KNOWLEDGE_PATH = path.join(WorkDir, 'knowledge', 'Notes', 'Suggested Topics.md');
|
||||
|
||||
// Configuration for the graph builder service
|
||||
const SYNC_INTERVAL_MS = 15 * 1000; // 15 seconds
|
||||
|
|
@ -88,6 +94,49 @@ function extractPathFromToolInput(input: string): string | null {
|
|||
}
|
||||
}
|
||||
|
||||
function ensureSuggestedTopicsFileLocation(): string {
|
||||
if (fs.existsSync(SUGGESTED_TOPICS_PATH)) {
|
||||
return SUGGESTED_TOPICS_PATH;
|
||||
}
|
||||
|
||||
const legacyCandidates: Array<{ absPath: string; relPath: string }> = [
|
||||
{ absPath: LEGACY_SUGGESTED_TOPICS_PATH, relPath: LEGACY_SUGGESTED_TOPICS_REL_PATH },
|
||||
{ absPath: LEGACY_SUGGESTED_TOPICS_KNOWLEDGE_PATH, relPath: LEGACY_SUGGESTED_TOPICS_KNOWLEDGE_REL_PATH },
|
||||
];
|
||||
|
||||
for (const legacy of legacyCandidates) {
|
||||
if (!fs.existsSync(legacy.absPath)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
try {
|
||||
fs.renameSync(legacy.absPath, SUGGESTED_TOPICS_PATH);
|
||||
console.log(`[buildGraph] Moved suggested topics file from ${legacy.relPath} to ${SUGGESTED_TOPICS_REL_PATH}`);
|
||||
return SUGGESTED_TOPICS_PATH;
|
||||
} catch (error) {
|
||||
console.error(`[buildGraph] Failed to move suggested topics file from ${legacy.relPath} to ${SUGGESTED_TOPICS_REL_PATH}:`, error);
|
||||
return legacy.absPath;
|
||||
}
|
||||
}
|
||||
|
||||
return SUGGESTED_TOPICS_PATH;
|
||||
}
|
||||
|
||||
function readSuggestedTopicsFile(): string {
|
||||
try {
|
||||
const suggestedTopicsPath = ensureSuggestedTopicsFileLocation();
|
||||
if (!fs.existsSync(suggestedTopicsPath)) {
|
||||
return '_No existing suggested topics file._';
|
||||
}
|
||||
|
||||
const content = fs.readFileSync(suggestedTopicsPath, 'utf-8').trim();
|
||||
return content.length > 0 ? content : '_Existing suggested topics file is empty._';
|
||||
} catch (error) {
|
||||
console.error(`[buildGraph] Error reading suggested topics file:`, error);
|
||||
return '_Failed to read existing suggested topics file._';
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Get unprocessed voice memo files from knowledge/Voice Memos/
|
||||
* Voice memos are created directly in this directory by the UI.
|
||||
|
|
@ -203,6 +252,7 @@ async function createNotesFromBatch(
|
|||
const run = await createRun({
|
||||
agentId: NOTE_CREATION_AGENT,
|
||||
});
|
||||
const suggestedTopicsContent = readSuggestedTopicsFile();
|
||||
|
||||
// Build message with index and all files in the batch
|
||||
let message = `Process the following ${files.length} source files and create/update obsidian notes.\n\n`;
|
||||
|
|
@ -210,8 +260,9 @@ async function createNotesFromBatch(
|
|||
message += `- Use the KNOWLEDGE BASE INDEX below to resolve entities - DO NOT grep/search for existing notes\n`;
|
||||
message += `- Extract entities (people, organizations, projects, topics) from ALL files below\n`;
|
||||
message += `- Create or update notes in "knowledge" directory (workspace-relative paths like "knowledge/People/Name.md")\n`;
|
||||
message += `- You may also create or update "${SUGGESTED_TOPICS_REL_PATH}" to maintain curated suggested-topic cards\n`;
|
||||
message += `- If the same entity appears in multiple files, merge the information into a single note\n`;
|
||||
message += `- Use workspace tools to read existing notes (when you need full content) and write updates\n`;
|
||||
message += `- Use workspace tools to read existing notes or "${SUGGESTED_TOPICS_REL_PATH}" (when you need full content) and write updates\n`;
|
||||
message += `- Follow the note templates and guidelines in your instructions\n\n`;
|
||||
|
||||
// Add the knowledge base index
|
||||
|
|
@ -219,6 +270,11 @@ async function createNotesFromBatch(
|
|||
message += knowledgeIndex;
|
||||
message += `\n---\n\n`;
|
||||
|
||||
message += `# Current Suggested Topics File\n\n`;
|
||||
message += `Path: ${SUGGESTED_TOPICS_REL_PATH}\n\n`;
|
||||
message += suggestedTopicsContent;
|
||||
message += `\n\n---\n\n`;
|
||||
|
||||
// Add each file's content
|
||||
message += `# Source Files to Process\n\n`;
|
||||
files.forEach((file, idx) => {
|
||||
|
|
|
|||
|
|
@ -485,9 +485,9 @@ RESOLVED (use canonical name with absolute path):
|
|||
- "Acme", "Acme Corp", "@acme.com" → [[Organizations/Acme Corp]]
|
||||
- "the pilot", "the integration" → [[Projects/Acme Integration]]
|
||||
|
||||
NEW ENTITIES (create notes if source passes filters):
|
||||
NEW ENTITIES (create notes or suggestion cards if source passes filters):
|
||||
- "Jennifer" (CTO, Acme Corp) → Create [[People/Jennifer]] or [[People/Jennifer (Acme Corp)]]
|
||||
- "SOC 2" → Create [[Topics/Security Compliance]]
|
||||
- "SOC 2" → Add or update a suggestion card in \`suggested-topics.md\` with category \`Topics\`
|
||||
|
||||
AMBIGUOUS (flag or skip):
|
||||
- "Mike" (no context) → Mention in activity only, don't create note
|
||||
|
|
@ -508,8 +508,8 @@ For entities not resolved to existing notes, determine if they warrant new notes
|
|||
|
||||
**CREATE a note for people who are:**
|
||||
- External (not @user.domain)
|
||||
- Attendees in meetings
|
||||
- Email correspondents (emails that reach this step already passed label-based filtering)
|
||||
- People you directly interacted with in meetings
|
||||
- Email correspondents directly participating in the thread (emails that reach this step already passed label-based filtering)
|
||||
- Decision makers or contacts at customers, prospects, or partners
|
||||
- Investors or potential investors
|
||||
- Candidates you are interviewing
|
||||
|
|
@ -521,6 +521,7 @@ For entities not resolved to existing notes, determine if they warrant new notes
|
|||
- Large group meeting attendees you didn't interact with
|
||||
- Internal colleagues (@user.domain)
|
||||
- Assistants handling only logistics
|
||||
- People mentioned only as third parties ("we work with X", "I can introduce you to Y") when there has been no direct interaction yet
|
||||
|
||||
### Role Inference
|
||||
|
||||
|
|
@ -579,31 +580,155 @@ For people who don't warrant their own note, add to Organization note's Contacts
|
|||
- Sarah Lee — Support, handled wire transfer issue
|
||||
\`\`\`
|
||||
|
||||
### Direct Interaction Test (People and Organizations)
|
||||
|
||||
For **new canonical People and Organizations notes**, require **direct interaction**, not just mention.
|
||||
|
||||
**Direct interaction = YES**
|
||||
- The person sent the email, replied in the thread, or was directly addressed as part of the active exchange
|
||||
- The person participated in the meeting, and there is evidence the user actually interacted with them or the meeting centered on them
|
||||
- The organization is directly represented in the exchange by participants/senders and is part of an active first-degree relationship with the user or team
|
||||
- The user is directly evaluating, selling to, buying from, partnering with, interviewing, or coordinating with that person or organization
|
||||
|
||||
**Direct interaction = NO**
|
||||
- Someone else mentions them in passing
|
||||
- A sender says they work with someone at another company
|
||||
- A sender offers to introduce the user to someone
|
||||
- A company is referenced as a customer, partner, employer, competitor, or example, but nobody from that company is directly involved in the interaction
|
||||
- The source only establishes a second-degree relationship, not a direct one
|
||||
|
||||
**Canonical note rule:**
|
||||
- For **new People/Organizations**, create the canonical note only if both are true:
|
||||
1. There is **direct interaction**
|
||||
2. The entity clears the **weekly importance test**
|
||||
|
||||
If an entity seems strategically relevant but fails the direct interaction test, do **not** auto-create a canonical note. At most, create a suggestion card in \`suggested-topics.md\`.
|
||||
|
||||
### Weekly Importance Test (People and Organizations only)
|
||||
|
||||
For **People** and **Organizations**, the final gate for **creating a new canonical note** is an importance test:
|
||||
|
||||
**Ask:** _"If I were the user, would I realistically need to look at this note on a weekly basis over the near term?"_
|
||||
|
||||
This test is mainly for **People** and **Organizations**. **Do NOT use it as the decision rule for Topic or Project suggestions.**
|
||||
|
||||
**Strong YES signals:**
|
||||
- Active customer, prospect, investor, partner, candidate, advisor, or strategic vendor relationship
|
||||
- Repeated interaction or a likely ongoing cadence
|
||||
- Decision-maker, owner, blocker, evaluator, or approver in an active process
|
||||
- Material relevance to launch, sales, fundraising, hiring, compliance, product delivery, or another current priority
|
||||
- The user would benefit from a durable reference note instead of repeatedly reopening raw emails or meeting transcripts
|
||||
|
||||
**Strong NO signals:**
|
||||
- One-off logistics, scheduling, or transactional contact
|
||||
- Assistant, support rep, recruiter, or vendor rep with no ongoing strategic role
|
||||
- Incidental attendee mentioned once with no leverage on current work
|
||||
- Passing mention with no evidence of an ongoing relationship
|
||||
|
||||
**Borderline signals:**
|
||||
- Seems potentially important, but there isn't enough evidence yet that the user will need a weekly reference note
|
||||
- Might become important soon, but the role, relationship, or repeated relevance is still unclear
|
||||
- Important enough to track, but only through second-degree mention or an offered introduction rather than direct interaction
|
||||
|
||||
**Outcome rules for new People/Organizations:**
|
||||
- **Clear YES + direct interaction** → Create/update the canonical \`People/\` or \`Organizations/\` note
|
||||
- **Borderline or no direct interaction, but still strategically relevant** → Do **not** create the canonical note yet; instead create or update a card in \`suggested-topics.md\`
|
||||
- **Clear NO** → Skip note creation and do not add a suggestion unless the source strongly suggests near-term strategic relevance
|
||||
|
||||
**When a canonical note already exists:**
|
||||
- Update the existing note even if the current source is weaker; the importance test is mainly for deciding whether to create a **new** People/Organization note
|
||||
- If a previously tentative person/org is now clearly important enough for a canonical note, create/update the note and remove any tentative suggestion card for that exact entity from \`suggested-topics.md\`
|
||||
|
||||
## Organizations
|
||||
|
||||
**CREATE a note if:**
|
||||
- Someone from that org attended a meeting
|
||||
- They're a customer, prospect, investor, or partner
|
||||
- Someone from that org sent relevant personalized correspondence
|
||||
- There is direct interaction with that org in the source
|
||||
- They're a customer, prospect, investor, or partner in a direct first-degree interaction
|
||||
- Someone from that org sent relevant personalized correspondence or joined a meeting you actually had with them
|
||||
- They pass the weekly importance test above
|
||||
|
||||
**DO NOT create for:**
|
||||
- Tool/service providers mentioned in passing
|
||||
- One-time transactional vendors
|
||||
- Consumer service companies
|
||||
- Organizations only referenced through third-party mention or offered introductions
|
||||
|
||||
## Projects
|
||||
|
||||
**CREATE a note if:**
|
||||
**If a project note already exists:** update it.
|
||||
|
||||
**If no project note exists:** do **not** create a new canonical note in \`knowledge/Projects/\`.
|
||||
|
||||
Instead, create or update a **suggestion card** in \`suggested-topics.md\` if the project is strong enough:
|
||||
- Discussed substantively in a meeting or email thread
|
||||
- Has a goal and timeline
|
||||
- Involves multiple interactions
|
||||
|
||||
Otherwise skip it.
|
||||
|
||||
Projects do **not** use the weekly importance test above. For **new** projects, the default output is a suggestion card, not a canonical note.
|
||||
|
||||
## Topics
|
||||
|
||||
**CREATE a note if:**
|
||||
**If a topic note already exists:** update it.
|
||||
|
||||
**If no topic note exists:** do **not** create a new canonical note in \`knowledge/Topics/\`.
|
||||
|
||||
Instead, create or update a **suggestion card** in \`suggested-topics.md\` if the topic is strong enough:
|
||||
- Recurring theme discussed
|
||||
- Will come up again across conversations
|
||||
|
||||
Otherwise skip it.
|
||||
|
||||
Topics do **not** use the weekly importance test above. For **new** topics, the default output is a suggestion card, not a canonical note.
|
||||
|
||||
## Suggested Topics Curation
|
||||
|
||||
Also maintain \`suggested-topics.md\` as a **curated shortlist** of things worth exploring next.
|
||||
|
||||
Despite the filename, \`suggested-topics.md\` can contain cards for **People, Organizations, Topics, or Projects**.
|
||||
|
||||
There are **two reasons** to add or update a suggestion card:
|
||||
|
||||
1. **High-quality Topic/Project cards**
|
||||
- Use these for topics or projects that are timely, high-leverage, strategically important, or clearly worth exploring now
|
||||
- These are not a dump of every topic/project note. Be selective
|
||||
- For **new** topics and projects, cards are the default output from this pipeline
|
||||
|
||||
2. **Tentative People/Organization cards**
|
||||
- Use these when a person or organization seems important enough to track, but you are **not 100% sure** they clear the weekly-importance test for a canonical note yet
|
||||
- The card should capture why they might matter and what still needs verification
|
||||
|
||||
**Do NOT add cards for:**
|
||||
- Low-signal administrative or transactional entities
|
||||
- Stale or completed items with no near-term relevance
|
||||
- People/organizations that already have a clearly established canonical note, unless the card is about a distinct project/topic exploration rather than the entity itself
|
||||
|
||||
**Card guidance:**
|
||||
- For **Topics/Projects**, use category \`Topics\` or \`Projects\`
|
||||
- For tentative **People/Organizations**, use category \`People\` or \`Organizations\`
|
||||
- Title should be concise and canonical when possible
|
||||
- Description should explain why it matters **now**
|
||||
- For tentative People/Organizations, description should also mention what is still uncertain or what the user should verify
|
||||
|
||||
**Curation rules:**
|
||||
- Maintain a **high-quality set**, not an ever-growing backlog
|
||||
- Deduplicate by normalized title
|
||||
- Prefer current, actionable, recurring, or strategically important items
|
||||
- Keep only the strongest **8-12 cards total**
|
||||
- Preserve good existing cards unless the new source clearly supersedes them
|
||||
- Remove stale cards that are no longer relevant
|
||||
- If a tentative People/Organization card later becomes clearly important and you create a canonical note, remove the tentative card
|
||||
|
||||
**File format for \`suggested-topics.md\`:**
|
||||
\`\`\`suggestedtopic
|
||||
{"title":"Security Compliance","description":"Summarize the current compliance posture, blockers, and customer implications.","category":"Topics"}
|
||||
\`\`\`
|
||||
|
||||
The file should start with \`# Suggested Topics\` followed by one or more blocks in that format.
|
||||
|
||||
If the file does not exist, create it. If it exists, update it in place or rewrite the full file so the final result is clean, deduped, and curated.
|
||||
|
||||
---
|
||||
|
||||
# Step 6: Extract Content
|
||||
|
|
@ -824,7 +949,7 @@ If new info contradicts existing:
|
|||
|
||||
# Step 9: Write Updates
|
||||
|
||||
## 9a: Create and Update Notes
|
||||
## 9a: Create and Update Notes and Suggested Topic Cards
|
||||
|
||||
**IMPORTANT: Write sequentially, one file at a time.**
|
||||
- Generate content for exactly one note.
|
||||
|
|
@ -852,6 +977,12 @@ workspace-edit({
|
|||
})
|
||||
\`\`\`
|
||||
|
||||
**For \`suggested-topics.md\`:**
|
||||
- Use workspace-relative path \`suggested-topics.md\`
|
||||
- Read the current file if you need the latest content
|
||||
- Use \`workspace-writeFile\` to create or rewrite the file when that is simpler and cleaner
|
||||
- Use \`workspace-edit\` for small targeted edits only if that keeps the file deduped and readable
|
||||
|
||||
## 9b: Apply State Changes
|
||||
|
||||
For each state change identified in Step 7, update the relevant fields.
|
||||
|
|
@ -867,8 +998,9 @@ If you discovered new name variants during resolution, add them to Aliases field
|
|||
- Be concise: one line per activity entry
|
||||
- Note state changes with \`[Field → value]\` in activity
|
||||
- Escape quotes properly in shell commands
|
||||
- Write only one file per response (no multi-file write batches)
|
||||
- Write only one file per response (notes and \`suggested-topics.md\` follow the same rule)
|
||||
- **Always set \`Last update\`** in the Info section to the YYYY-MM-DD date of the source email or meeting. When updating an existing note, update this field to the new source event's date.
|
||||
- Keep \`suggested-topics.md\` curated, deduped, and capped to the strongest 8-12 cards
|
||||
|
||||
---
|
||||
|
||||
|
|
@ -957,8 +1089,12 @@ Before completing, verify:
|
|||
**Filtering:**
|
||||
- [ ] Excluded self (user.name, user.email, @user.domain)
|
||||
- [ ] Applied relevance test to each person
|
||||
- [ ] Applied the direct interaction test to new People/Organizations
|
||||
- [ ] Applied the weekly importance test to new People/Organizations
|
||||
- [ ] Transactional contacts in Org Contacts, not People notes
|
||||
- [ ] Source correctly classified (process vs skip)
|
||||
- [ ] Third-party mentions did not become new canonical People/Organizations notes
|
||||
- [ ] Borderline People/Organizations became suggestion cards instead of canonical notes
|
||||
|
||||
**Content Quality:**
|
||||
- [ ] Summaries describe relationship, not communication method
|
||||
|
|
@ -978,8 +1114,11 @@ Before completing, verify:
|
|||
- [ ] All entity mentions use \`[[Folder/Name]]\` absolute links
|
||||
- [ ] Activity entries are reverse chronological
|
||||
- [ ] No duplicate activity entries
|
||||
- [ ] \`suggested-topics.md\` stays deduped and curated
|
||||
- [ ] High-quality Topics/Projects were added to suggested topics only when timely and useful
|
||||
- [ ] New Topics/Projects were not auto-created as canonical notes
|
||||
- [ ] Dates are YYYY-MM-DD
|
||||
- [ ] Bidirectional links are consistent
|
||||
- [ ] New notes in correct folders
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue