Welcome back! In Contextual Relevance Engine, we taught our system to be smart enough to know which tips fit the current situation (like showing Python tips only when editing Python files).
However, being smart isn't enough. We also need to be polite.
Imagine a friend who tells you the same joke every time they see you. Even if the joke is funny (relevant), hearing it five times in a row makes it annoying.
The Problem: Without memory, our system is that annoying friend. It sees you open a Python file and immediately shouts the Python tipβeven if it just told you that 10 seconds ago.
The Solution: We need Session History Tracking. We need a way to remember when a tip was last shown so we can enforce a "Cooldown" period.
Think of our system as a Librarian.
Let's say we have a helpful tip called dont-panic.
To do this, we need to persist data to the user's disk.
In our system, we don't track time in minutes or hours. We track Startups.
Every time the user runs the command tengu, we increment a global counter called numStartups.
numStartups was 100.numStartups becomes 101.numStartups becomes 103.This counter is our "Clock."
Before we look at the code, let's visualize the flow when the system decides if a tip is "on cooldown."
If the tip requires a cooldown of 10 sessions, and it has only been 5, the App knows to stay silent.
Let's look at tipHistory.ts to see how we implement this memory.
getSessionsSinceLastShown)This function answers the question: "How long has it been?"
export function getSessionsSinceLastShown(tipId: string): number {
const config = getGlobalConfig() // Load the disk storage
// 1. Look up the "stamp" for this specific tip
const lastShown = config.tipsHistory?.[tipId]
// 2. If never shown, return Infinity (it's been forever!)
if (!lastShown) return Infinity
// 3. Calculate the difference: Current - Last
return config.numStartups - lastShown
}
Explanation:
numStartups).tipsHistory, which is a simple list like { "dont-panic": 95, "other-tip": 99 }.numStartups is 100 and lastShown was 95, the result is 5.recordTipShown)When we actually decide to show a tip, we must record it so we don't show it again too soon.
export function recordTipShown(tipId: string): void {
// 1. Get the current "Time" (Session number)
const currentSession = getGlobalConfig().numStartups
// 2. Save it to the permanent config file
saveGlobalConfig(config => {
const history = config.tipsHistory ?? {}
// Update the record for this specific tip ID
return {
...config,
tipsHistory: { ...history, [tipId]: currentSession }
}
})
}
Explanation:
tipsHistory object.
In Chapter 1: Tip Registry, we saw a property called cooldownSessions. Now we can see how that is used in the main logic.
This logic usually resides in our filtering step:
// Inside our filtering logic...
const sessionsPassed = getSessionsSinceLastShown(tip.id)
// Check if the gap is big enough
if (sessionsPassed >= tip.cooldownSessions) {
// Safe to show!
} else {
// Still on cooldown. Hide it.
}
We also use this moment to log data. In tipScheduler.ts, when a tip is recorded, we often send a signal to our analytics system.
export function recordShownTip(tip: Tip): void {
// 1. Update the local history file (The "Librarian Stamp")
recordTipShown(tip.id)
// 2. Log to analytics (for our own stats)
logEvent('tengu_tip_shown', {
tipIdLength: tip.id,
cooldownSessions: tip.cooldownSessions,
})
}
This helps us track which tips are most popular, but the crucial part for the user is recordTipShown.
You have learned how to give the system Memory.
numStartups).Where we are now:
The Final Problem: Imagine we run our filters and we are left with 3 valid tips.
We can only show one. Which one do we pick? Randomly? Alphabetically?
In the next chapter, we will build the logic to pick the most urgent tip.
Next Chapter: Priority Scheduler
Generated by Code IQ