Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
64 commits
Select commit Hold shift + click to select a range
1120179
Add procedural memory: review-gated recipes with trigger-based recall
gowtham0992 Jul 8, 2026
eb9de8c
Add two-layer echo guard: Link never re-ingests its own voice
gowtham0992 Jul 8, 2026
2c2c2c1
Add conditional memory: applies_when scoping with honest applicabilit…
gowtham0992 Jul 8, 2026
7749c3e
Add supersedes chains and as-of temporal recall; fix claim-dilution i…
gowtham0992 Jul 8, 2026
cc96e05
Add memory-hygiene benchmark; harden conflict detection and echo guar…
gowtham0992 Jul 8, 2026
a7bee8d
Add lnk recipes, recall --type, and recurring-theme detection in cons…
gowtham0992 Jul 8, 2026
22f0bce
Fix deep-review findings: visible safety, one field rule, honest benc…
gowtham0992 Jul 8, 2026
e0412e9
Mine architecture decision records into review-gated decision memory
gowtham0992 Jul 8, 2026
0519e72
Fix automatic capture attributing assistant prose to the user
gowtham0992 Jul 9, 2026
864e59f
Stop tracking local .claude/ preview config
gowtham0992 Jul 9, 2026
9ddb5c0
Make hook-command path assertions portable to Windows
gowtham0992 Jul 9, 2026
231ead1
Merge branch 'develop' into feature/procedural-memory
gowtham0992 Jul 9, 2026
e863d4c
Merge branch 'develop' into feature/procedural-memory
gowtham0992 Jul 10, 2026
8962f3d
Drop stale feature-branch label from the Unreleased changelog section
gowtham0992 Jul 10, 2026
4fd74e1
Fail closed on malformed applies_when conditions
gowtham0992 Jul 10, 2026
7fb2339
Fix three 1.7 defects found cold-walking the new flows as a fresh user
gowtham0992 Jul 10, 2026
7e581c1
Add retrieval context to memory records; +5pt LoCoMo from failure ana…
gowtham0992 Jul 10, 2026
0c017e2
Let MCP recall evaluate path-fenced memories via context_path
gowtham0992 Jul 10, 2026
4a631dc
Document three failed ranking ablations from the LoCoMo fusion study
gowtham0992 Jul 10, 2026
2ff5e20
Add optional local rerank tier: cross-encoder blending on explicit re…
gowtham0992 Jul 10, 2026
79b894e
Finish the rerank tier's setup path; group the CLI help
gowtham0992 Jul 10, 2026
c2f6159
State the development-set caveat on LoCoMo retrieval numbers
gowtham0992 Jul 10, 2026
1c59acc
Add the 'memory that stays true' animation to Getting Started
gowtham0992 Jul 10, 2026
4d4fed7
Show the memory-that-stays-true demo on the landing home page
gowtham0992 Jul 10, 2026
32f97b8
Record the memory-that-stays-true GIF from real commands; keep tooling
gowtham0992 Jul 10, 2026
7b4f8aa
Fix first-run friction: pathless commands, lnk display, landing fallback
gowtham0992 Jul 10, 2026
d2b9341
Add ARCHITECTURE.md, .mailmap, dependency ceilings, and a type ratchet
gowtham0992 Jul 10, 2026
d112348
Make don't-know a first-class recall verdict; cite independent research
gowtham0992 Jul 10, 2026
8e4c20e
Wire the abstention verdict into CLI recall --json output
gowtham0992 Jul 10, 2026
1bee35d
Add Track 4: end-to-end QA under mem0's open harness
gowtham0992 Jul 10, 2026
3eb6d46
Add a Benchmarks section to the README and homepage
gowtham0992 Jul 10, 2026
6de2595
Catch standing-rule phrasings in preference proposals
gowtham0992 Jul 10, 2026
efa7ff5
Make MCP work out of the box: connect --write provisions the runtime
gowtham0992 Jul 10, 2026
2f98a43
verify-mcp accepts agent names and checks the written config
gowtham0992 Jul 10, 2026
4421e96
Trim conversational preambles from proposed memory text
gowtham0992 Jul 10, 2026
d95c668
Changelog: MCP out-of-box provisioning, verify-mcp agent mode, captur…
gowtham0992 Jul 10, 2026
3ad230b
Make the semantic tier reachable from externally-managed pythons
gowtham0992 Jul 10, 2026
21ed4df
Docs: semantic setup is one command; PEP 668 path explained
gowtham0992 Jul 10, 2026
7bc5a10
Add the link-remembers animation: the automatic memory loop
gowtham0992 Jul 10, 2026
df4c697
Animations: SMIL everywhere, payoff scene as the frozen frame
gowtham0992 Jul 10, 2026
48e0a6f
Detect and repair stale workspace runtimes after upgrades
gowtham0992 Jul 10, 2026
88c01cc
Docs: answer the three questions every newcomer asks (privacy, contex…
gowtham0992 Jul 10, 2026
4090ea0
Add issue templates: bug, friction, feature request
gowtham0992 Jul 10, 2026
03dad49
CI: cold-install walks on clean macOS and Windows runners
gowtham0992 Jul 10, 2026
27494ee
RESULTS: LoCoMo win confirmed under a second, independent judge (hy3)
gowtham0992 Jul 12, 2026
60bb0d3
Context in captures: retrieval context flows through every write path
gowtham0992 Jul 12, 2026
c835811
Secrets refused at the gate; forget scrubs the log; captures show the…
gowtham0992 Jul 12, 2026
eb69722
Capture provenance: reviewable, correctly-attributed session context
gowtham0992 Jul 12, 2026
849a90c
Fix capture filename race: concurrent session-ends silently lost capt…
gowtham0992 Jul 12, 2026
32961e7
Normalize applies_when project conditions to a matchable slug
gowtham0992 Jul 12, 2026
ca71ed9
RESULTS: complete neutral-judge LongMemEval table, both directions
gowtham0992 Jul 12, 2026
7c58014
Harden core against hostile inputs: frontmatter injection, slug bounds
gowtham0992 Jul 12, 2026
8b393be
Benchmarks final: second-judge LoCoMo confirmation + neutral LME table
gowtham0992 Jul 12, 2026
f3d1635
Precision: mem0 LoCoMo under hy3 judge is 83.6 (1285/1538), not 83.5
gowtham0992 Jul 12, 2026
2e945e1
Rank proposals by durability; single-hero Quick Start; quieter onboard
gowtham0992 Jul 12, 2026
57cd490
CI: fix type ratchet, Windows tests, PowerShell guard; MCP smoke diag…
gowtham0992 Jul 12, 2026
2ee3860
CI: fix MCP smoke recall assertions and Windows cold-walk wiki
gowtham0992 Jul 13, 2026
d54bae1
CI: give the macOS MCP smoke a fresh demo wiki per surface
gowtham0992 Jul 13, 2026
9c8990d
CI: richer slim-recall diagnostic to pinpoint the macOS smoke failure
gowtham0992 Jul 13, 2026
2147c1a
CI: run slim MCP smoke first on one pristine demo (macOS runner fix)
gowtham0992 Jul 13, 2026
27ab277
CI: fully instrument the macOS demo so the empty-wiki cause is visible
gowtham0992 Jul 13, 2026
188bb0f
CI: point the macOS cold-onboard MCP smoke at a demo, not the empty w…
gowtham0992 Jul 13, 2026
ca6b533
Release 1.7.0: version stamps and changelog
gowtham0992 Jul 17, 2026
09bf7eb
Drop stray apps/LinkBar/.gitignore from core — LinkBar ships from its…
gowtham0992 Jul 17, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
41 changes: 41 additions & 0 deletions .github/ISSUE_TEMPLATE/bug-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
name: Bug report
description: Something in Link behaved wrong or broke.
labels: ["bug"]
body:
- type: textarea
id: what-happened
attributes:
label: What happened?
description: What did you run, what did you expect, what did you get instead?
placeholder: |
I ran `lnk ...` and expected ... but got ...
validations:
required: true
- type: textarea
id: doctor
attributes:
label: lnk doctor output
description: |
Paste the output of `lnk doctor <your-workspace>` and `lnk --version`.
It contains no memory content — only workspace structure and health.
render: text
validations:
required: true
- type: dropdown
id: install-path
attributes:
label: How did you install Link?
options:
- Homebrew (brew install gowtham0992/link/link)
- pip (link-mcp)
- Source checkout
- Other / not sure
validations:
required: true
- type: input
id: platform
attributes:
label: OS and agent
placeholder: "macOS 15 · Claude Code (or Codex, Cursor, ...)"
validations:
required: true
8 changes: 8 additions & 0 deletions .github/ISSUE_TEMPLATE/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: "First 10 minutes guide"
url: https://gowtham0992.github.io/link/getting-started.html
about: "Setup, hooks, MCP, and the three questions everyone asks — check here before filing setup issues."
- name: "Does Link read my conversations? (FAQ)"
url: https://gowtham0992.github.io/link/getting-started.html#faq
about: "Privacy, context windows, and memory scoping, answered plainly."
29 changes: 29 additions & 0 deletions .github/ISSUE_TEMPLATE/feature-request.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
name: Feature request
description: Something Link should be able to do.
labels: ["enhancement"]
body:
- type: markdown
attributes:
value: |
Link's direction is deliberately narrow: local, source-backed personal
memory with review-gated writes and no LLM in the memory layer.
Requests that fit that shape land fastest.
- type: textarea
id: problem
attributes:
label: What problem would this solve?
description: The situation you keep hitting, before any proposed solution.
validations:
required: true
- type: textarea
id: proposal
attributes:
label: Proposed behavior
description: Optional — what you imagine the feature looking like.
- type: checkboxes
id: fit
attributes:
label: Fit
options:
- label: This works with plain local files and no cloud service.
- label: This keeps the user in control of what becomes durable memory.
27 changes: 27 additions & 0 deletions .github/ISSUE_TEMPLATE/friction-report.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
name: Friction report
description: Nothing crashed, but something was confusing, surprising, or harder than it should be.
labels: ["friction"]
body:
- type: markdown
attributes:
value: |
These reports shape Link more than feature requests do — the fresh-user
experience is tested before every release, and real friction reports are
how we find what those walkthroughs miss.
- type: textarea
id: friction
attributes:
label: What was confusing or harder than expected?
description: Where were you in the flow, what did you expect to happen, and what actually happened?
validations:
required: true
- type: textarea
id: expectation
attributes:
label: What would have felt right?
description: Optional — how you expected it to work.
- type: input
id: platform
attributes:
label: Install path, OS, and agent
placeholder: "Homebrew · macOS 15 · Claude Code"
121 changes: 120 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,12 @@ jobs:
- name: Run ruff
run: python -m ruff check .

- name: Install mypy
run: python -m pip install "mypy>=1.20,<1.21"

- name: Type-error ratchet (count may only go down)
run: python scripts/check_type_ratchet.py

test:
runs-on: ubuntu-latest
strategy:
Expand Down Expand Up @@ -107,6 +113,116 @@ jobs:
$env:PYTHONPATH = "mcp_package"
python link.py verify-mcp $demo --python python

# A brand-new user on a machine that has never seen Link: onboard must
# provision the MCP runtime itself and every advertised surface must
# work. The local wheel is offered via PIP_FIND_LINKS so the pinned
# link-mcp==LINK_VERSION resolves even before that version is on PyPI.
macos-clean-install:
runs-on: macos-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Build the link-mcp wheel
run: python -m pip wheel ./mcp_package -w dist --no-deps

- name: Assert the driving python has no link_mcp (a dirty runner would mask real breakage)
run: |
if python -c "import link_mcp" 2>/dev/null; then
echo "runner is not clean: link_mcp already importable" >&2
exit 1
fi

- name: Cold onboard with hooks and MCP
env:
PIP_FIND_LINKS: ${{ github.workspace }}/dist
run: |
export HOME="$RUNNER_TEMP/cleanhome"
mkdir -p "$HOME"
python link.py onboard --agent claude-code --hooks --write
test -f "$HOME/.claude.json"
grep -q '"link"' "$HOME/.claude.json"
grep -q "SessionStart" "$HOME/.claude/settings.json"
test -x "$HOME/.link-mcp-venv/bin/python"

- name: Session-start hook produces the memory brief
run: |
export HOME="$RUNNER_TEMP/cleanhome"
echo '{"session_id":"ci-cold","source":"startup"}' \
| python "$HOME/link/link.py" hook session-start "$HOME/link" | grep -q "Link memory"

- name: MCP server answers over stdio from the provisioned venv
run: |
export HOME="$RUNNER_TEMP/cleanhome"
python -m pip install "mcp>=1.0.0,<2"
# The smoke asserts demo content is recallable, so point it at a
# populated demo wiki — the onboarded workspace ($HOME/link) is
# intentionally empty. Still drives the provisioned venv python,
# which is what this step verifies. (Mirrors the Windows fix.)
python link.py demo "$RUNNER_TEMP/mcp-demo" --force
python scripts/smoke_mcp_stdio.py "$RUNNER_TEMP/mcp-demo/wiki" \
--python "$HOME/.link-mcp-venv/bin/python" --surface slim

# The same walk on Windows, where nobody has ever cold-walked by hand:
# pip/source is the install path, venv layout is Scripts\python.exe.
windows-cold-walk:
runs-on: windows-latest
steps:
- uses: actions/checkout@v4

- uses: actions/setup-python@v5
with:
python-version: "3.12"

- name: Build the link-mcp wheel
run: python -m pip wheel ./mcp_package -w dist --no-deps

- name: Assert the driving python has no link_mcp
shell: pwsh
run: |
python -c "import link_mcp" 2>$null
if ($LASTEXITCODE -eq 0) { Write-Error "runner is not clean: link_mcp already importable"; exit 1 }
exit 0

- name: Cold onboard with hooks and MCP
shell: pwsh
env:
PIP_FIND_LINKS: ${{ github.workspace }}\dist
run: |
$env:USERPROFILE = Join-Path $env:RUNNER_TEMP "cleanhome"
New-Item -ItemType Directory -Force $env:USERPROFILE | Out-Null
python link.py onboard --agent claude-code --hooks --write
if (-not (Test-Path "$env:USERPROFILE\.claude.json")) { Write-Error "no MCP config written"; exit 1 }
if (-not (Select-String -Quiet '"link"' "$env:USERPROFILE\.claude.json")) { Write-Error "link server missing from config"; exit 1 }
if (-not (Select-String -Quiet "SessionStart" "$env:USERPROFILE\.claude\settings.json")) { Write-Error "hooks not written"; exit 1 }
if (-not (Test-Path "$env:USERPROFILE\.link-mcp-venv\Scripts\python.exe")) { Write-Error "MCP venv not provisioned"; exit 1 }

- name: Session-start hook produces the memory brief
shell: pwsh
run: |
$env:USERPROFILE = Join-Path $env:RUNNER_TEMP "cleanhome"
# Join multi-line stdout into one string: PowerShell captures command
# output as a string array, and `-notmatch` on an array returns the
# non-matching lines (truthy) rather than a boolean, so a brief that
# DOES contain "Link memory" still trips the guard.
$out = ('{"session_id":"ci-cold","source":"startup"}' | python "$env:USERPROFILE\link\link.py" hook session-start "$env:USERPROFILE\link") -join "`n"
if ($out -notmatch "Link memory") { Write-Error "hook produced no brief: $out"; exit 1 }

- name: MCP server answers over stdio from the provisioned venv
shell: pwsh
run: |
$env:USERPROFILE = Join-Path $env:RUNNER_TEMP "cleanhome"
python -m pip install "mcp>=1.0.0,<2"
# The smoke asserts demo content is recallable, so point it at a
# populated demo wiki (the onboarded workspace is intentionally
# empty). Still drives the provisioned venv python — that's what
# this step verifies.
python link.py demo "$env:RUNNER_TEMP\mcp-demo" --force
python scripts/smoke_mcp_stdio.py "$env:RUNNER_TEMP\mcp-demo\wiki" --python "$env:USERPROFILE\.link-mcp-venv\Scripts\python.exe" --surface slim

installer-syntax:
runs-on: ubuntu-latest
steps:
Expand Down Expand Up @@ -167,5 +283,8 @@ jobs:
- name: MCP stdio smoke test
run: |
python link.py demo /tmp/link-mcp-smoke --force
python scripts/smoke_mcp_stdio.py /tmp/link-mcp-smoke/wiki --surface full
# Slim (read-only) first on the pristine demo; the full smoke
# exercises mutating tools (rebuild_index, migrate_wiki,
# backup_wiki), so it runs last.
python scripts/smoke_mcp_stdio.py /tmp/link-mcp-smoke/wiki
python scripts/smoke_mcp_stdio.py /tmp/link-mcp-smoke/wiki --surface full
2 changes: 2 additions & 0 deletions .mailmap
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
Gowtham Sarveswaran <gowtham0992@gmail.com> Gowtham <gowtham0992@gmail.com>
Gowtham Sarveswaran <gowtham0992@gmail.com> gowtham0992 <gowtham0992@gmail.com>
137 changes: 137 additions & 0 deletions ARCHITECTURE.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,137 @@
# Link Architecture

The map a new maintainer needs before touching anything. For what Link *is*,
read the [README](README.md); this is how it works and where things live.

## The one-paragraph version

Link is a local memory layer for AI agents. A **workspace** (default
`~/link`) holds immutable raw sources (`raw/`), an agent-compiled Markdown
wiki (`wiki/`), and reviewed **memory pages** (`wiki/memories/*.md` — plain
Markdown with YAML frontmatter). Four surfaces read and write it through one
shared core: the CLI (`link.py`), the MCP server (`mcp_package/link_mcp/`),
agent session hooks, and a local read-only web viewer (`serve.py`). Nothing
durable is written without user approval, no LLM runs inside the memory
layer, and the runtime never touches the network.

## Components

```
link.py CLI shell: arg wiring, output rendering, hooks entry
serve.py local web viewer (127.0.0.1 only; --host refused)
mcp_package/
link_core/ ALL shared logic lives here
memory.py memory model: write path, recall ranking, review,
conflicts/duplicates/echo, supersedes, applies_when
semantic.py optional local embeddings + rerank tier (offline-only)
capture.py raw session captures + proposal accept flow
agent_hooks.py session-hook config writing + transcript extraction
consolidate.py read-only backlog plans, duplicate/theme clustering
project_seed.py source-backed project seeding, ADR decision mining
cli_parser.py argparse tree + grouped help + dispatch
cli_memory.py, cli_runtime.py CLI rendering helpers
web_*.py viewer page builders (server-rendered HTML strings)
mcp_verify.py MCP config generation + `lnk`-vs-path command display
link_mcp/server.py MCP tool surface (slim = canonical, full = compat)
scripts/ benchmarks (recall, LoCoMo, hygiene), release prep,
CI guards (see Guards below)
docs/ public site, served from main branch by GitHub Pages
```

`link.py` and `serve.py` are intentionally standalone-runnable (a workspace
carries copies so `python3 link.py` works with zero installs); the
`check_runtime_duplication.py` guard keeps root and package logic from
drifting apart.

## The data model

A memory page's frontmatter is the whole schema — there is no database:

- `memory_type` (preference | decision | project | fact | note | procedure),
`scope` (user | project | global), `visibility` (private | project | team)
- lifecycle: `status` (active | archived | stale), `date_captured`,
`review_status`/`reviewed_at`, `review_after`, `expires_at`,
`archived_at` + `archive_reason`
- 1.7 fields, one rule of thumb: **finding it** → `trigger` (recipes),
**fencing it** → `applies_when` (`project:` / `path:` / `task:`,
OR semantics, fail-closed on bad syntax), **replacing it** →
`supersedes`/`superseded_by` (lineage chain), plus retrieval `context`
(text that helps recall find a memory but is never part of its claim)

`raw/` is user-owned and immutable; `wiki/` pages are agent-compiled and
source-linked; `.link-cache/` holds derived state only (semantic index as
plain JSON, hook dedup fingerprints) and is always safe to delete.

## The two write paths (both review-gated)

1. **Explicit** — `remember` (CLI or MCP): conflict detection runs first
(negation-XOR, option groups, revision-shape rule on head-claim tokens),
then duplicate detection; a conflicting write is refused with a
paste-ready `--supersedes` resolution; supersession archives the
predecessor with lineage inside one operation journal entry.
2. **Automatic** — session-end hooks: transcript text is extracted with
Link's own injected output dropped (echo guard layer 1), proposals are
mined **from user turns only**, proposals restating existing memories are
dropped (echo layer 2, core-claim containment), trivial and duplicate
sessions are skipped, and everything lands as a *proposal-only capture*
awaiting `accept-capture`. Nothing durable happens without approval.

## The recall pipeline

Query → field-weighted lexical scoring (title/tldr+trigger/tags/body+context)
→ optional semantic similarity from a local embedding model, merged by
*standout* (z-score vs the corpus, never raw cosine thresholds; semantic-only
matches capped at moderate confidence) → rank boosts (project affinity,
temporal, applicability match) with out-of-context conditional memories
demoted and labeled → optional rerank tier (local cross-encoder blended via
reciprocal-rank fusion over the top 50; explicit recall only, hooks never pay
the latency). Results carry honest labels: `match` (lexical/semantic/hybrid),
`confidence`, `applicability`, `rerank`. `--as-of DATE` reconstructs what was
active on a past date from lifecycle fields alone.

Measured behavior lives in `benchmarks/RESULTS.md` — including the
experiments that failed. Keep it that way.

## Invariants (the things you must not break)

1. **Review-gated writes**: no code path may create durable memory without
explicit user approval. Automatic paths produce proposals only.
2. **No LLM in the memory layer**: extraction, recall, dedup, conflicts are
deterministic. LLMs are consumers, never components.
3. **Offline runtime**: models load with the offline guard; only explicit
`--setup` may download. CI greps the runtime for outbound-network code
and the tests must pass with sockets blocked.
4. **Claims stay clean**: echo/duplicate/conflict checks compare
`memory_claim_text` (title + TLDR + Memory section) — never retrieval
`context`, never template boilerplate.
5. **Plain files**: every state change must remain legible as a Markdown
diff. If a feature needs a database, redesign the feature.

## Guards and gates (run before every push)

```
python3 -m pytest tests -q # includes benchmark regression gates
python3 scripts/check_release_hygiene.py # secrets + outbound-network scan
python3 scripts/check_runtime_duplication.py # root vs package drift
python3 scripts/check_tool_contract.py # MCP surface contract
uvx ruff check .
```

CI (`.github/workflows/ci.yml`) runs on pull requests: Linux 3.10/3.12/3.14,
Windows, package build + twine + wheel install + MCP stdio smoke, installer
syntax, large-wiki smoke.

## Adding things

- **New CLI command**: subparser in `cli_parser.py`, handler in `link.py`,
dispatch entry in `main()`, a group in `COMMAND_GROUPS` (guard test fails
if you forget), tests in `tests/test_link_cli.py`.
- **New memory field**: frontmatter write in `write_memory_page`, read in
`memory_record_from_page`, decide its role against the field rule above,
keep it out of `memory_claim_text` unless it *is* claim.
- **New MCP tool/param**: `server.py` slim surface first;
`check_tool_contract.py` must stay green; document in the instructions
resource.
- **Release**: `scripts/prepare_release.py <version>` on develop → PR to
main → tag → PyPI → mcp-publisher → Homebrew tap bump
(gowtham0992/homebrew-link). The script prints the full runbook.
Loading
Loading