How Feedback Signals Are Attached to Page Versions in ai-memory
Feedback signals are attached to specific page versions by inserting immutable rows into an append-only page_feedback table that references the current page_id, binding signals like helpful or stale to exact versions rather than the page path.
The akitaonrails/ai-memory project implements a versioned memory system where feedback signals play a critical role in content retention and quality assessment. When users mark content as helpful, stale, or wrong, these signals must be tied to specific revisions to maintain accurate historical records. The system achieves this through a strict append-only schema design that immutably links feedback to the exact page version it references.
The Append-Only Feedback Schema
The storage mechanism relies on the page_feedback table, defined in migration V37 at [crates/ai-memory-store/migrations/V37__page_feedback.sql](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/migrations/V37__page_feedback.sql). This table implements an append-only architecture where each feedback signal is recorded as a new, immutable row.
The schema includes:
page_id: References the specific version in thepagestablekind: Enum values includinghelpful,not_helpful,stale, andwrongreason: Optional text explaining the feedbacksalience_after: Optional numeric value affecting page importanceauthor_id: Identifier of the user providing feedback
Because the table never updates existing rows, every signal remains permanently attached to the page_id of the version current at the time of submission.
Recording Feedback via the Writer API
When feedback is submitted through the MCP tool memory_feedback, the system routes the request to store::Writer::record_page_feedback in [crates/ai-memory-store/src/ops.rs](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/ops.rs#L1659). This function executes an INSERT statement that specifically targets the current version by filtering for is_latest = 1 in the pages table.
The binding process works as follows:
- The MCP tool validates the signal against the
PageFeedbackKindenum defined in [crates/ai-memory-core/src/page.rs](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/page.rs#L269) - The writer resolves the page path to the current
page_id(whereis_latest = 1) - A new row is inserted into
page_feedbackwith that specificpage_id
This ensures that even if the page is rewritten later (receiving a new page_id), the feedback remains attached to the specific version the user actually reviewed.
Querying Feedback with the Reader
To retrieve feedback for display or linting, the Reader implementation in [crates/ai-memory-store/src/reader.rs](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs#L3227) joins the pages table with page_feedback rows matching the specific page_id. The query returns all feedback records associated with that version, making the signals visible alongside the page content.
This join operation enables:
- Display of historical feedback when viewing specific versions
- Calculation of derived metrics like
pages.salience - Lint findings that highlight problematic or outdated content
Impact on Page Salience and Retention
Feedback signals directly influence the salience score stored in the pages table. As documented in [docs/ARCHITECTURE.md](https://github.com/akitaonrails/ai-memory/blob/main/docs/ARCHITECTURE.md#L70), the salience column (added in V37) is calculated from the most recent salience_after values in the feedback table.
The retention sweep uses this derived score to determine whether to keep or decay episodic pages. When a page receives stale or wrong feedback with low salience values, the system prioritizes it for cleanup, while helpful signals with high salience extend retention periods.
Practical Code Examples
Command-Line Interface
Mark the current version of a document as helpful using the CLI:
ai-memory memory_feedback \
--path docs/usage.md \
--signal helpful \
--reason "Answered my question" \
--author "$(whoami)"
MCP Tool Payload
Call the memory_feedback tool directly via JSON:
{
"tool": "memory_feedback",
"args": {
"path": "docs/ARCHITECTURE.md",
"signal": "stale",
"reason": "Content out‑of‑date",
"author_id": "user-42"
}
}
Rust API Implementation
Record feedback programmatically using the Writer handle:
use ai_memory_store::Writer;
let result = writer
.record_page_feedback(
"docs/architecture.md".into(),
ai_memory_core::page::PageFeedbackKind::Stale,
Some("Content no longer reflects the design".into()),
None,
author_id,
)
.await?;
Reading Feedback Records
Retrieve feedback for analysis:
use ai_memory_store::Reader;
let feedback = reader
.page_feedback("docs/architecture.md")
.await?;
for fb in feedback {
println!("{} – {}", fb.kind, fb.reason.unwrap_or_default());
}
Summary
- Feedback signals are stored in the append-only
page_feedbacktable created in migration V37 - Each signal binds to a specific version via the
page_idof the current row (is_latest = 1) - The
PageFeedbackKindenum inai-memory-corevalidates signals likehelpful,stale,not_helpful, andwrong - The writer API in
crates/ai-memory-store/src/ops.rshandles immutable INSERT operations - Reader queries join feedback to pages for display and linting purposes
- Derived
saliencescores drive the retention sweep algorithm
Frequently Asked Questions
How does ai-memory prevent feedback from being lost when a page is rewritten?
When a page is rewritten, it receives a new page_id in the pages table. Because page_feedback rows reference specific page_id values rather than paths, historical feedback remains attached to the old version. The new version starts with a clean feedback slate, ensuring signals always describe the content they were attached to.
Can feedback signals be updated or deleted after submission?
No. The page_feedback table is designed as append-only with immutable rows. If a user changes their opinion, they must submit a new feedback record with a different kind value. The system treats all feedback records as permanent audit history.
What is the difference between stale and wrong feedback types?
Both signal content problems, but stale indicates the information is outdated or no longer relevant, while wrong indicates factual errors or incorrect content. These distinctions help the retention algorithm prioritize which pages to decay first, with wrong typically triggering more aggressive cleanup than stale.
How does the salience_after parameter affect page storage?
The optional salience_after field in a feedback record directly updates the derived pages.salience column. Higher values extend how long the system retains the page version, while lower values or negative signals accelerate decay during the retention sweep process.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →