# Understanding the ai-memory Feedback System: How It Controls Page Salience and Linting

> Discover the ai-memory feedback system. Learn how it uses explicit judgments to control page salience for retrieval ranking and surfaces unresolved signals as lint findings.

- Repository: [Fabio Akita/ai-memory](https://github.com/akitaonrails/ai-memory)
- Tags: deep-dive
- Published: 2026-08-19

---

**The ai-memory feedback system lets agents record explicit judgments about page versions in an append-only `page_feedback` table, which directly adjusts a bounded `salience` score for retrieval ranking and surfaces unresolved signals as `feedback_flagged` lint findings.**

The `akitaonrails/ai-memory` project implements a lightweight but precise feedback loop that gives agents quantitative control over long-term memory retention. By recording structured judgments through the `memory_feedback` MCP tool, the system updates per-page importance metrics and exposes quality issues without ever deleting source content.

## How Feedback Is Recorded

Agents submit feedback through the `memory_feedback` MCP tool. On the server side, the handler in [`crates/ai-memory-mcp/src/server.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-mcp/src/server.rs) validates the payload and delegates to `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).

That function inserts a new row into the append-only `page_feedback` table, computes an updated **salience** value, and writes it back to the `pages.salience` column for the targeted version.

```rust
// Example payload sent to the MCP "memory_feedback" tool
{
  "workspace_id": "w1",
  "project_id": "p1",
  "path": "/docs/overview.md",
  "kind": "helpful",               // one of: helpful, not_helpful, stale, wrong
  "reason": "The explanation is clear",
  "author_id": "u42"
}

```

```rust
// In crates/ai-memory-mcp/src/server.rs → memory_feedback
let reason = sanitize_feedback_reason(&self.sanitizer, args.reason.as_deref());
store
    .writer
    .record_page_feedback(
        ws,
        proj,
        &args.path,
        args.kind.into(),
        reason,
        args.author_id,
        &params,
    )
    .await?;

```

The `FeedbackKind` enum, defined in [`crates/ai-memory-core/src/page.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/page.rs), restricts inputs to four variants: `Helpful`, `NotHelpful`, `Stale`, and `Wrong`. Each variant maps to a string stored in the `kind` column.

## How Feedback Affects Page Salience

**Salience** is a bounded float that expresses how important a page version is for retrieval. When feedback is recorded, the pure function `salience_after_feedback` in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) applies a deterministic step:

- `FeedbackKind::Helpful` increases salience.
- `FeedbackKind::NotHelpful` decreases salience.

The result is clamped to the range `[SALINCE_MIN, SALINCE_MAX]` and persisted. During retrieval, the scorer automatically multiplies the term score by `page.salience` when salience-aware ranking is enabled.

```rust
let result = store
    .searcher
    .search(
        query,
        SearchOptions {
            // the scorer automatically multiplies the term score by
            // page.salience (which reflects any feedback adjustments)
            use_salience: true,
            ..Default::default()
        },
    )
    .await?;

```

This mechanism lets agents **tilt retention** toward explicitly useful pages and away from stale or wrong content without removing any data.

## How Feedback Surfaces in Linting

During a lint run (`memory_lint`), the consolidator scans `page_feedback` for open signals. The reader helper `open_feedback_findings` in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) returns any feedback still attached to the latest page version.

The lint pass in [`crates/ai-memory-consolidate/src/lint.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/lint.rs) then emits a **`feedback_flagged`** finding, which appears in the JSON lint output.

```rust
let feedback_findings = store
    .reader
    .open_feedback_findings(workspace_id, project_id)
    .await?;   // returns Vec<FeedbackFinding>

```

These findings act as actionable reminders that a specific version has been explicitly marked as `stale` or `wrong` and may need human or agent attention.

## Retirement Rules on Page Rewrite

When a page is rewritten and a new version supersedes the old one, the previous feedback is **retired automatically**. As noted in the comments around `record_page_feedback`, the new version starts with the default salience again.

Because the lint finder only reports feedback attached to the latest version, `feedback_flagged` findings disappear from the lint output once the rewrite is committed. This creates a clean lifecycle: feedback applies to a specific version, not the conceptual page indefinitely.

## Summary

- The ai-memory feedback system stores explicit judgments in an append-only `page_feedback` table via `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).
- `salience_after_feedback` in [`crates/ai-memory-store/src/decay.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/decay.rs) translates each `FeedbackKind` into a bounded salience step, directly influencing retrieval scores.
- The retrieval scorer multiplies term relevance by `page.salience` when `use_salience` is enabled.
- Unresolved feedback is exposed through `open_feedback_findings` in [`crates/ai-memory-store/src/reader.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-store/src/reader.rs) and reported as `feedback_flagged` lint findings by [`crates/ai-memory-consolidate/src/lint.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/lint.rs).
- Feedback is version-scoped; rewriting a page retires old signals and resets salience to the default.

## Frequently Asked Questions

### What are the valid feedback kinds in ai-memory?

The `FeedbackKind` enum in [`crates/ai-memory-core/src/page.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-core/src/page.rs) defines four variants: `Helpful`, `NotHelpful`, `Stale`, and `Wrong`. Each maps to a string stored in the `kind` column of the `page_feedback` table.

### How does feedback change a page's retrieval rank?

When feedback is recorded, `salience_after_feedback` adjusts the page version's bounded salience score up or down depending on the kind. The retrieval scorer then multiplies the base term score by this `salience` value, making helpful pages rank higher and unhelpful pages rank lower.

### Why do lint findings disappear after a page rewrite?

Old feedback is retired automatically when a new version is created. Since `open_feedback_findings` only scans the latest version, the lint pass in [`crates/ai-memory-consolidate/src/lint.rs`](https://github.com/akitaonrails/ai-memory/blob/main/crates/ai-memory-consolidate/src/lint.rs) stops emitting `feedback_flagged` entries once the superseded version is no longer current.

### Is feedback data ever deleted?

No. The `page_feedback` table is append-only. Even after a rewrite retires the salience impact, the historical feedback row remains in storage. Only the active salience adjustment and lint surface area are affected.