Remove [[wiki-links]], backlinks and the graph
CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 6s
CI & Build / Build & push image (push) Skipped
CI & Build / Python tests (push) Successful in 8s
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 31s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Failing after 37s
Desktop (Tauri) / Update manifest (push) Skipped
Android / Kotlin + Rust (APK) (push) Failing after 1m56s
CI & Build / Build now, or wait for Android? (push) Successful in 2s
CI & Build / Python lint (push) Successful in 3s
CI & Build / TypeScript typecheck (push) Failing after 6s
CI & Build / Build & push image (push) Skipped
CI & Build / Python tests (push) Successful in 8s
Desktop (Tauri) / Tauri desktop (Linux) (push) Failing after 31s
Desktop (Tauri) / Windows installer (cross-compiled) (push) Failing after 37s
Desktop (Tauri) / Update manifest (push) Skipped
Android / Kotlin + Rust (APK) (push) Failing after 1m56s
Operator, 2026-08-22 (note 2897): ThoughtSync is an intermediary surface. You
write here because it's easy — a notebook in your pocket — and later you recall
the thing and go finish it somewhere else. Recall is the product; organization
is secondary. A linking system is organization, and it isn't what this is for.
So: `[[wiki-links]]`, backlinks, the `[[` autocomplete, the note_links table,
`/api/notes/link-search`, `/api/notes/<id>/backlinks`, the whole graph blueprint
and GraphView. Rust core loses `extract_links`, `backlinks`, `link_search` and
`create_titled`; the desktop loses the three Tauri commands that exposed them.
This subsumes 982d24c rather than reverting it. That commit bound links to a
note id so a rename would stop rewriting other notes' bodies — real infra, but
infra for a feature that is now gone, and nothing it added survives. Alembic
0023 stays in the chain anyway: it shipped in an image and may already be
applied, and deleting an applied revision strands a database's version pointer.
0024 drops the table and takes the column with it. The history stays honest
about the fact that it existed for a day.
Two things deliberately kept, because they were serving recall and only
incidentally serving links:
- `/api/notes/titles` and the titles store. The command palette lists them so
you can jump to a note by name. `resolve()` — the name→note lookup that only
linking needed — is gone.
- `display_title`. Every note still has a name for search results and export
filenames. What that name is FOR changed; that it exists did not.
`notes/links.py` is now `notes/tags.py`, holding the #tag→label reconciliation
it always also owned. A file called links.py with no links in it would have been
exactly the drift this removal is meant to end.
Also swept out on the way: `_escape_like`, whose only caller was link-search,
and the `graph` icon. Nothing lost that a person typed — note_links was always
derived, and the `[[text]]` is still sitting in every body it was written in.
This commit is contained in:
+6
-49
@@ -7,7 +7,6 @@ from thoughtsync.common import coerce_bool, parse_dt
|
||||
from thoughtsync.models.note import NOTE_COLORS, Note
|
||||
from thoughtsync.notes import (
|
||||
_attachment_ext,
|
||||
_escape_like,
|
||||
_header_filename,
|
||||
_keep_spec,
|
||||
_native_spec,
|
||||
@@ -19,7 +18,6 @@ from thoughtsync.notes import (
|
||||
next_occurrence,
|
||||
normalize_color,
|
||||
normalize_recurrence,
|
||||
parse_link_titles,
|
||||
parse_list_items,
|
||||
parse_tags,
|
||||
)
|
||||
@@ -40,7 +38,7 @@ def test_all_note_routes_registered(app):
|
||||
for name in (
|
||||
"list_notes", "search_notes", "list_reminders", "complete_reminder",
|
||||
"snooze_reminder", "export_notes", "import_notes", "list_titles",
|
||||
"link_search", "note_backlinks", "reorder_notes", "create_note",
|
||||
"reorder_notes", "create_note",
|
||||
"get_note", "update_note", "list_revisions", "restore_revision",
|
||||
"set_note_labels", "add_item", "update_item", "delete_item",
|
||||
"reorder_items", "upload_attachment", "get_attachment",
|
||||
@@ -117,32 +115,11 @@ async def test_reorder_requires_auth(app):
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
def test_parse_link_titles():
|
||||
titles = parse_link_titles("see [[Alpha]] and [[ beta ]] and [[Alpha]] again")
|
||||
assert titles == ["alpha", "beta"]
|
||||
|
||||
|
||||
def test_parse_link_titles_empty():
|
||||
assert parse_link_titles(None) == []
|
||||
assert parse_link_titles("no links here") == []
|
||||
|
||||
|
||||
# `rewrite_link_title` and `_rename_inbound_links` are gone (M13 step 1). They kept
|
||||
# backlinks alive across a rename by editing the [[text]] inside every note that
|
||||
# linked to the renamed one — which meant one note's edit silently rewrote another's
|
||||
# words. Links are bound to a note id now, so a rename needs no repair at all.
|
||||
#
|
||||
# What replaced them (`_resolve_target`, `_claim_unresolved_links`, and the id-first
|
||||
# resolution in backlinks / the graph / serialization) is all SQL, and this suite runs
|
||||
# without a database, so it is deliberately not asserted here. See the task log: that
|
||||
# behaviour was checked by hand, and this repo has no integration lane to hold it.
|
||||
|
||||
|
||||
def test_parse_link_titles_ignores_nesting():
|
||||
# The link regex refuses [ and ] inside a token, so a malformed nest yields the
|
||||
# inner name rather than something spanning both — worth pinning, since this is
|
||||
# the string that becomes a link's stored target.
|
||||
assert parse_link_titles("[[outer [[inner]] ]]") == ["inner"]
|
||||
# [[wiki-links]] are gone entirely (note 2897), and with them backlinks, the graph,
|
||||
# the name index and the `[[` autocomplete. So are the two helpers that used to keep
|
||||
# links alive across a rename, and the id-binding that briefly replaced them. Nothing
|
||||
# here asserts their absence — `test_all_note_routes_registered` below is what would
|
||||
# notice a route coming back, and the removal is one commit rather than a fossil.
|
||||
|
||||
|
||||
def test_derive_display_title_explicit_wins():
|
||||
@@ -188,14 +165,6 @@ def test_parse_list_items():
|
||||
assert parse_list_items([1, "x", None, {"a": 1}]) == ["x"]
|
||||
|
||||
|
||||
def test_escape_like():
|
||||
# LIKE wildcards in user input must be neutralized so they match literally.
|
||||
assert _escape_like("100%") == "100\\%"
|
||||
assert _escape_like("a_b") == "a\\_b"
|
||||
assert _escape_like("c:\\path") == "c:\\\\path"
|
||||
assert _escape_like("plain") == "plain"
|
||||
|
||||
|
||||
def test_parse_dt():
|
||||
# A full ISO instant round-trips (used to validate the Timeline date range).
|
||||
d = parse_dt("2026-07-19T12:30:00+00:00")
|
||||
@@ -217,18 +186,6 @@ async def test_titles_requires_auth(app):
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_link_search_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/notes/link-search?q=hi")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_graph_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/graph")
|
||||
assert resp.status_code == 401
|
||||
|
||||
|
||||
async def test_reminders_requires_auth(app):
|
||||
client = app.test_client()
|
||||
resp = await client.get("/api/notes/reminders")
|
||||
|
||||
Reference in New Issue
Block a user