Skip to content

Commit 618ec0d

Browse files
mpstatonclaude
andcommitted
fix(strategy-curator), feat(workspace, resolver, content-ingest): Corpora Curator tracks its workspace, thesis vocabulary lands with a domain.retype migration
Two operator-reported bugs fixed live, then the rest of build-order step 5 (thesis vocabulary) landed the same session: the curator now tracks whichever workspace is actually active, defaults to the right domain type per client without being told, and a real migration moved the one pre-existing domain (consumer-immunology) from strategy to thesis. Workspace-awareness bug: each federation remote loads its own separate @augment-it/workspace singleton (no `shared` block in the shell's rsbuild config). The shared package already broadcasts a cross-remote WORKSPACE_CHANGED_EVENT on every switch, but curation.svelte.ts's own clientSlug mirror was a one-time snapshot taken at bootstrap, never re-synced — switching workspaces from the shell's header updated the package's active_client_id correctly but left the curator silently scoped to whatever it saw first. Fixed with one shared applyWorkspaceChange path, used whether the switch happens in the curator or anywhere else in the shell. Domain type, now genuinely operator-defined AND per-workspace: the hardcoded DOMAIN_TYPE = 'strategy' constant is gone, replaced by a reactive domainType field editable via a new Type field on the "New corpus" form. WorkspaceSummary gained default_domain_type (read from each client's DEFAULT_DOMAIN_TYPE .env — clients/humain-vc/.env now has DEFAULT_DOMAIN_TYPE=thesis), resolved on bootstrap and on every workspace switch, so a fresh humain-vc session starts on "thesis" without typing it. Creating a domain with a different type switches the active type and does a full reload so nothing silently vanishes into an unseen type. domain.retype: a new capability that moves a domain between types — DB (domains.type + every source_usages.domain_type for the slug) and filesystem (<old-type-plural>/<slug>/ -> <new-type-plural>/<slug>/, index.md's type: line patched, every source file's domains: list entry patched) — for every client_slug on the domain's row at once, since a domain's identity is one row regardless of how many clients reference it. Idempotent: re-running after a partial failure picks up where it left off. Ran it once via a new RETYPE=1 mode on scripts/prove-didi-auth.mjs: consumer-immunology is now thesis:consumer-immunology, verified directly against the DB and the on-disk frontmatter. Caught and fixed a real bug along the way — the first draft's directory-existence check reused content-ingest's file-only exists() helper (readFile-based, throws EISDIR on a directory, silently read as "not found"), which would have made every retype fail even when the source directory was right there; fixed with a proper stat().isDirectory() check. UI rename + a real back button: "Strategy Curator" -> "Corpora Curator" throughout the display copy (package/folder/remote id unchanged — a display rename, not a code-identity one). Added a prominent "‹ All corpora" button in the header itself, visible whenever a corpus is open, replacing a link that was too easy to miss tucked into the source list's filter row. Files changed: - apps/strategy-curator/src/curation.svelte.ts, App.svelte, StrategyPicker.svelte, SourceList.svelte, app.css - services/workspace/src/workspaces.ts, capabilities.ts — default_domain_type, domain.retype wiring - packages/workspace/src/types.ts, index.ts — WorkspaceSummary + WORKSPACE_CHANGED_EVENT export - services/record-surrealdb-resolver/src/domains.ts — retypeDomain + domain.retype.requested handler - services/content-ingest/src/corpus.ts, handlers.ts — retypeDomainFiles + corpus.domain.retype.requested handler - scripts/prove-didi-auth.mjs — RETYPE=1 mode - context-v/plans/Build-Order-Humain-VC-Unlock-Flow.md, context-v/specs/Strategy-Curator-Entry-Point-for-Augment-It.md - changelog/2026-07-06_05, changelog/2026-07-07_01 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
1 parent fcfe894 commit 618ec0d

17 files changed

Lines changed: 605 additions & 66 deletions

apps/strategy-curator/src/App.svelte

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -14,20 +14,28 @@
1414

1515
<div class="sc-app">
1616
<header class="sc-header">
17-
<span class="sc-brand">Strategy Curator</span>
17+
<span class="sc-brand">Corpora Curator</span>
1818
{#if curation.workspaces.length}
1919
<select
2020
class="sc-ws"
2121
value={curation.clientSlug ?? ''}
2222
onchange={(e) => curation.switchWorkspace(e.currentTarget.value)}
23-
title="Active workspace — the client whose corpus strategies are written into"
23+
title="Active workspace — the client whose corpus is written into"
2424
>
2525
{#each curation.workspaces as w (w.client_id)}<option value={w.client_id}>{w.client_id}</option>{/each}
2626
</select>
27+
{:else if curation.connection !== 'open'}
28+
<span class="sc-pill" title="Connecting to workspace-service">connecting…</span>
2729
{:else}
2830
<span class="sc-pill" title="Active workspace">{curation.clientSlug ?? '— no workspace —'}</span>
2931
{/if}
32+
<span class="sc-pill" title="Active domain type">{curation.domainType}</span>
3033
{#if curation.active}
34+
<button
35+
class="sc-back"
36+
onclick={() => (curation.activeSlug = null)}
37+
title="Back to the corpora list / create form"
38+
>‹ All corpora</button>
3139
<span class="sc-strategy">{curation.active.title}</span>
3240
<span class="sc-pill">{curation.sources.length} sources</span>
3341
{/if}

apps/strategy-curator/src/SourceList.svelte

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
</script>
1212

1313
<div class="sc-list-head">
14-
<button class="sc-link" onclick={() => (curation.activeSlug = null)}>‹ strategies</button>
14+
<button class="sc-link" onclick={() => (curation.activeSlug = null)}>‹ corpora</button>
1515
<input class="sc-filter" placeholder="filter sources… (coverage check)" bind:value={curation.listFilter} />
1616
</div>
1717

apps/strategy-curator/src/StrategyPicker.svelte

Lines changed: 36 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,24 @@
11
<script lang="ts">
22
import { curation, slugify, toDashed } from './curation.svelte';
33
4+
// Mirrors content-ingest's DOMAIN_FOLDERS (services/content-ingest/src/
5+
// corpus.ts) so the "writes to" preview below matches the actual folder
6+
// the backend will create. Irregular plurals only; fallback +s.
7+
const DOMAIN_FOLDERS: Record<string, string> = {
8+
strategy: 'strategies',
9+
topic: 'topics',
10+
thesis: 'theses',
11+
category: 'categories',
12+
'market-segment': 'market-segments',
13+
};
14+
function domainFolder(t: string): string {
15+
return DOMAIN_FOLDERS[t] ?? `${t}s`;
16+
}
17+
418
let title = $state('');
519
let slug = $state('');
620
let slugEdited = $state(false);
21+
let type = $state(curation.domainType);
722
let tagInput = $state('');
823
let pendingTags = $state<string[]>([]);
924
let tagSuggest = $derived(curation.suggestTags(tagInput));
@@ -24,22 +39,26 @@
2439
function removeTag(t: string): void {
2540
pendingTags = pendingTags.filter((x) => x !== t);
2641
}
27-
function create(): void {
28-
void curation.createStrategy({ title, slug, tags: pendingTags });
42+
async function create(): Promise<void> {
43+
await curation.createStrategy({ title, slug, tags: pendingTags, type });
44+
// Read domainType back AFTER the await — createStrategy only updates it
45+
// (via setDomainType) once the server confirms a type change, so
46+
// resetting eagerly here would just re-read the pre-create value.
2947
title = '';
3048
slug = '';
3149
slugEdited = false;
50+
type = curation.domainType;
3251
pendingTags = [];
3352
tagInput = '';
3453
}
3554
</script>
3655

3756
<section class="sc-card">
38-
<h2>Strategies</h2>
39-
<p class="sc-muted">Pick a strategy to gather sources for, or create a new one.</p>
57+
<h2>Corpora</h2>
58+
<p class="sc-muted">Pick a corpus to gather sources for, or create a new one.</p>
4059

4160
{#if curation.strategies.length === 0}
42-
<p class="sc-muted sc-mini">No strategies yet.</p>
61+
<p class="sc-muted sc-mini">No corpora yet.</p>
4362
{:else}
4463
<ul class="sc-strat-list">
4564
{#each curation.strategies as s (s.slug)}
@@ -55,16 +74,21 @@
5574
</section>
5675

5776
<section class="sc-card">
58-
<h2>New strategy</h2>
77+
<h2>New corpus</h2>
5978

6079
<div class="sc-field">
6180
<span class="sc-label">Title</span>
62-
<input value={title} oninput={(e) => onTitle(e.currentTarget.value)} placeholder="full strategy name…" />
81+
<input value={title} oninput={(e) => onTitle(e.currentTarget.value)} placeholder="full corpus name…" />
82+
</div>
83+
84+
<div class="sc-field">
85+
<span class="sc-label">Type <span class="sc-muted sc-mini">— any value; 'strategy' and 'thesis' are the two in use today</span></span>
86+
<input class="sc-mono" bind:value={type} placeholder="strategy" />
6387
</div>
6488

6589
<div class="sc-field">
6690
<span class="sc-label">Slug <span class="sc-muted sc-mini">— auto from title, editable, lowercase-kebab</span></span>
67-
<input class="sc-mono" value={slug} oninput={(e) => onSlug(e.currentTarget.value)} placeholder="strategy-slug" />
91+
<input class="sc-mono" value={slug} oninput={(e) => onSlug(e.currentTarget.value)} placeholder="corpus-slug" />
6892
</div>
6993

7094
<div class="sc-field">
@@ -88,12 +112,12 @@
88112
</div>
89113
</div>
90114

91-
<button class="sc-primary" onclick={create} disabled={!title.trim() || !slug.trim()}>
92-
+ Create strategy → folder + index.md
115+
<button class="sc-primary" onclick={create} disabled={!title.trim() || !slug.trim() || !type.trim()}>
116+
+ Create corpus → folder + index.md
93117
</button>
94-
{#if slug.trim()}
118+
{#if slug.trim() && type.trim()}
95119
<p class="sc-muted sc-mini">
96-
Writes <code class="sc-mono">strategies/{slug}/index.md</code>
120+
Writes <code class="sc-mono">{domainFolder(type.trim())}/{slug}/index.md</code>
97121
</p>
98122
{/if}
99123
</section>

apps/strategy-curator/src/app.css

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,16 @@
3535
background: var(--color-surface-raised);
3636
}
3737
.sc-app .sc-ws { width: auto; padding: 0.15rem 0.4rem; font-size: 12px; }
38+
.sc-app .sc-back {
39+
font-size: 12px;
40+
padding: 0.2rem 0.6rem;
41+
border-radius: 4px;
42+
border: 1px solid var(--color-border);
43+
background: var(--color-surface-raised);
44+
color: var(--color-text);
45+
cursor: pointer;
46+
}
47+
.sc-app .sc-back:hover { border-color: var(--color-accent); color: var(--color-accent); }
3848
.sc-app .sc-conn { font-size: 11px; padding: 1px 7px; border-radius: 3px; background: var(--color-border); }
3949
.sc-app .sc-conn.status-open { background: var(--color-ok-bg); color: var(--color-ok-text); }
4050
.sc-app .sc-conn.status-error,

0 commit comments

Comments
 (0)