Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
d7e715e
fix(search): group entity-choice menus by entity_choice (#66)
jm-rivera Oct 25, 2026
00f8954
chore(deps): vendor the client at sdk 97df221 (#69)
jm-rivera Oct 25, 2026
3785b95
fix(deps): re-lock the vendored client from its tarball (#70)
jm-rivera Oct 26, 2026
41dd678
fix(seo): let crawlers see noindex, add a sitemap, stop dead Markdown…
jm-rivera Oct 26, 2026
a887a6a
fix(seo): give gallery searches crawlable content and their own preview
jm-rivera Oct 26, 2026
9f24d1f
fix(db): wait for test connections to close before terminating them
jm-rivera Oct 26, 2026
e52667c
Merge pull request #71 from DevelopmentFinanceObservatory/fix/search-…
jm-rivera Oct 26, 2026
2b86a49
Merge pull request #72 from DevelopmentFinanceObservatory/fix/limit-c…
jm-rivera Oct 26, 2026
6fe733d
feat(proxy): forward the SDK clients' X-DFO-Client label upstream (#68)
jm-rivera Oct 26, 2026
107e12e
feat(studio): switch a plan's scope choices and a reading's chips (#73)
jm-rivera Oct 27, 2026
b158fb6
feat(studio): lead the plan card with its scope choices and keep the …
jm-rivera Oct 28, 2026
5a92892
fix(studio): let a tall plan card scroll so the approve bar stays on …
jm-rivera Oct 29, 2026
6dc181b
feat(studio): show planner decisions as switchable rows on the plan c…
jm-rivera Oct 29, 2026
350e539
fix: keep crawlers out of event counts, keep preview cards, and file …
jm-rivera Oct 29, 2026
95731cd
feat(motion-kit): render the Studio and capture site pages as Claude …
jm-rivera Oct 29, 2026
24e53dc
chore(deps): vendor the client at sdk 07dba3d (#84)
jm-rivera Oct 29, 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
53 changes: 53 additions & 0 deletions db/test/conversation-search.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import path from 'node:path';
import assert from 'node:assert/strict';
import test from 'node:test';
import { drizzle } from 'drizzle-orm/node-postgres';
import { searchConversations } from '../../src/lib/chat/conversationSearch.ts';
import { applyMigrations } from '../apply-migrations.ts';
import { conversation, user } from '../schema.ts';
import { createTestDatabase } from './harness.ts';

const MIGRATIONS_FOLDER = path.resolve(import.meta.dirname, '../migrations');

/**
* Conversation search on Postgres reads each saved message's text and the
* title of the card a user message names, and leaves out the approved plan
* stored beside a reply.
*/
test('conversation search matches message text and card titles, not stored plans', async (t) => {
const database = await createTestDatabase();
t.after(() => database.teardown());

await applyMigrations(database.migratorPool(), {
migrationsFolder: MIGRATIONS_FOLDER,
runtimeDbUser: database.runtimeUser,
migratorDbUser: database.migratorUser,
});

const db = drizzle(database.runtimePool());

await db.insert(user).values([{ id: 'owner', name: 'Owner', email: 'owner@example.org' }]);
await db.insert(conversation).values({
id: 'conv-1',
userId: 'owner',
title: 'Aid question',
data: {
messages: [
{ role: 'user', text: 'How much aid did Ghana get?' },
{
role: 'bot',
text: 'About 1 billion dollars.',
plan: { title: 'Net disbursements to Zanzibar', steps: [], choices: [] },
},
{ role: 'user', text: 'Make it a bar chart', card: { title: 'Health aid trend' } },
],
},
});

const hits = async (q: string) =>
(await searchConversations(db, 'owner', q)).map((hit) => [hit.id, hit.snippet]);

assert.deepEqual(await hits('ghana'), [['conv-1', 'How much aid did Ghana get?']]);
assert.deepEqual(await hits('health aid'), [['conv-1', 'Health aid trend']]);
assert.deepEqual(await hits('zanzibar'), []);
});
27 changes: 18 additions & 9 deletions db/test/harness.ts
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,16 @@ export async function createTestDatabase(): Promise<TestDatabase> {
});

const pools: Pool[] = [];
const closed: Promise<void>[] = [];

const open = (user: string, password: string): Pool => {
const pool = poolFor(databaseName, user, password);
pool.on('connect', (client) => {
closed.push(new Promise((resolve) => client.once('end', resolve)));
});
pools.push(pool);
return pool;
};

return {
databaseName,
Expand All @@ -86,22 +96,21 @@ export async function createTestDatabase(): Promise<TestDatabase> {
runtimeUser,
runtimePassword,
migratorPool(): Pool {
const pool = poolFor(databaseName, migratorUser, migratorPassword);
pools.push(pool);
return pool;
return open(migratorUser, migratorPassword);
},
runtimePool(): Pool {
const pool = poolFor(databaseName, runtimeUser, runtimePassword);
pools.push(pool);
return pool;
return open(runtimeUser, runtimePassword);
},
adminPool(): Pool {
const pool = poolFor(databaseName, SUPERUSER_USER, SUPERUSER_PASSWORD);
pools.push(pool);
return pool;
return open(SUPERUSER_USER, SUPERUSER_PASSWORD);
},
async teardown(): Promise<void> {
// pool.end() resolves once it has asked its clients to close, not
// once they have. A backend that has not yet read its client's
// Terminate when the terminate below reaches it sends the client
// a FATAL error, which the ended pool emits with no listener.
await Promise.all(pools.map((pool) => pool.end()));
await Promise.all(closed);

const admin = new Client({
host: SUPERUSER_HOST,
Expand Down
6 changes: 3 additions & 3 deletions docs/development.md
Original file line number Diff line number Diff line change
Expand Up @@ -211,17 +211,17 @@ The `apis`, `ai` and `dcp` repositories run their own dev services. `apis` and `

## API client package

The Search, Datasets and Graph types come from `@developmentfinance/client`, vendored as `vendor/developmentfinance-client-0.1.0.tgz`. The tarball is the `typescript/` package of the sdk repository, built and packed. To refresh it, pack that package into `vendor/` and re-lock, so `pnpm-lock.yaml` carries the new tarball's integrity:
The Search, Datasets and Graph types come from `@developmentfinance/client`, vendored as `vendor/developmentfinance-client-0.1.0.tgz`. The tarball is the `typescript/` package of the sdk repository, built and packed. To refresh it, pack that package into `vendor/` and re-lock:

```sh
cd <sdk checkout>/typescript
pnpm install && pnpm run build
pnpm pack --pack-destination <website checkout>/vendor
cd <website checkout>
pnpm install
just relock-client
```

A new package version packs under a new file name. Point the `@developmentfinance/client` entry in `package.json` at it before re-locking. A rebuilt tarball keeps its file name, so a local re-vendor needs the client's directory under `node_modules/.pnpm` deleted before `pnpm install --frozen-lockfile` picks it up. Commit the tarball and the lockfile together.
A new package version packs under a new file name. Point the `@developmentfinance/client` entry in `package.json` at it, delete the old tarball, then re-lock. `just relock-client` deletes the client's stale directory under `node_modules/.pnpm` (pnpm keeps an installed `file:` package of the same name and version, so a rebuilt tarball is otherwise ignored), has pnpm re-resolve the client's lockfile entries from the tarball with `pnpm install --lockfile-only --fix-lockfile`, runs `pnpm install --frozen-lockfile`, and imports the client to prove it loads. The re-lock needs the registry. It changes the client's entries and those of any dependency the new tarball adds. pnpm 10.5.2 also fills in deprecation notices and writes `eslint-plugin-import`'s peer suffix in one of two equivalent forms, so a re-lock can touch those few lines too. Commit the tarball and the lockfile together.

The tarball carries the statuses of the codes the proxy raises itself and not their descriptions. `src/lib/proxy/proxy-codes.json` holds the full file, copied from the spec-kit repository's `python/client-contract/src/dfo_client_contract/proxy-codes.json` and formatted with Prettier. Copy it again when the client contract changes it.

Expand Down
10 changes: 10 additions & 0 deletions justfile
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,16 @@ env:
echo "ok Created {{ENV_FILE}} from {{ENV_SAMPLE}}."
fi

# pnpm reuses an installed `file:` package whose name and version are
# unchanged, so the stale install goes first.
#
# Re-lock the vendored API client tarball
relock-client:
rm -rf node_modules/.pnpm/@developmentfinance+client@*
pnpm install --lockfile-only --fix-lockfile
pnpm install --frozen-lockfile
node --input-type=module -e "await import('@developmentfinance/client')"

# ── Development ───────────────────────────────────────

# LAN access (phone/tablet testing) needs DEV_ORIGINS set in {{ENV_FILE}}.
Expand Down
Loading
Loading