Skip to content

Commit f66116e

Browse files
mpstatonclaude
andcommitted
feat(didi-chat), skill(triage-inbox): relations + tags verbs in the workbench slab; aboutness routing lands as step 5b
didi can now speak the family tree: the workbench slab teaches organization.relations / organization.relate / organization.tag.add with a propose-first discipline — relations are judgment calls, so didi invokes only when the operator stated the relationship themselves, quoting their phrasing into the edge's description. Both-orgs-must-exist and peers-not-hierarchies (funds / partners_with) are spelled out. Triage integration per the plan's Phase 3: the ACTIVE_SKILLS slab gains step 5b (aboutness routing — parent content on the parent, initiative content on the child, reference_of pointer across the seam; real-actor initiatives with no row get minted + related + tagged instead of parked), and the initiative_hub stream kind sheds its "while parent/child modeling is unresolved" caveat. SKILL.md updated in the same commit per the update-both-together rule: the parent-child open decision flips to MODEL LANDED with the new routing recorded in place. Verified: workspace tsc clean; service rebuilt and serving. Refs #55. Files changed: - services/workspace/src/chat.ts - context-v/agent-skills/triage-inbox-w-suggestions/SKILL.md - changelog/2026-07-27_01_Organizations-Learn-Their-Family-Tree-Parent-Child-Peer-Relations-Plus-Org-Tags.md Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01RW28dw3kQAKXr2ZNefCukE
1 parent d4371d5 commit f66116e

3 files changed

Lines changed: 54 additions & 7 deletions

File tree

changelog/2026-07-27_01_Organizations-Learn-Their-Family-Tree-Parent-Child-Peer-Relations-Plus-Org-Tags.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -163,3 +163,19 @@ the drive's click literally couldn't reach them. Now opacity-based with
163163
`:focus-within`, so Tab reveals them too. The drive proving the buttons
164164
*work* is exactly the rung that caught the button you couldn't Tab to;
165165
the human walk-through still judges whether the surface is *usable*.
166+
167+
### didi learns the family tree, and triage learns to ask "parent or child?" (#55)
168+
169+
`WORKBENCH_CHAT_VERBS` now carries `organization.relations / relate /
170+
tag.add` with the relations discipline: **propose by default** — a
171+
relation is a judgment call, so didi chat_invokes only when the operator
172+
stated the relationship themselves ("X is an initiative of Y"), quoting
173+
their phrasing into the edge's `description`. The triage slab gains
174+
**step 5b (aboutness routing)**: when a destination org has relations or
175+
the page names an initiative of a parent, ask which entity the content is
176+
*about* before filing — and an initiative that's a real actor with no row
177+
yet gets minted, related, and tagged `Initiative` instead of parked. The
178+
triage SKILL.md's parent-child open decision flips to **MODEL LANDED**,
179+
and the `initiative_hub` stream kind sheds its "while parent/child
180+
modeling is unresolved" caveat — it's now only for initiatives that don't
181+
merit their own org row.

context-v/agent-skills/triage-inbox-w-suggestions/SKILL.md

Lines changed: 20 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -389,12 +389,24 @@ attention goes to the hard tail.
389389
Human Potential Network) — sibling folder
390390
(`corpus/organizations/<slug>/`?) or something else? Still open;
391391
captures accumulate pending in the inbox.
392-
- [ ] **Parent-child organizations** (raised in batch 1, 2026-07-25):
393-
urban-institute is the parent org; upmobility-foundation is an
394-
initiative of it. Operator ruling: do NOT file content into either
395-
until the relationship is modeled. Full write-up + candidate shape:
392+
- [x] **Parent-child organizations** — MODEL LANDED (2026-07-27): org↔org
393+
relations are live (`organization.relate` / `.relations` /
394+
`.unrelate` / `.relation.update` — parent/child/peer + kind +
395+
free-text description, edges in the `affiliations` table) along with
396+
org tags (`organization.tag.add` / `.remove` — Initiative, Program,
397+
Fund…). Per
398+
[[../../plans/Org-Relations-Parent-Child-Peer-Plus-Org-Tags|the plan]];
399+
history in
396400
[[../../issues/Parent-Child-Nested-Organizations-Not-Modeled|Parent-Child-Nested-Organizations-Not-Modeled]].
397-
Affected captures stay pending in the inbox.
401+
**Aboutness routing (step 5b):** when a destination org has relations
402+
or the page names an initiative/fund/program of a parent, ask which
403+
entity the content is *about* before filing — parent content on the
404+
parent, initiative content on the child, `reference_of:` pointer
405+
across the seam when both want it. An initiative that is a real actor
406+
but has no row yet → mint the child, relate it (`rel: parent`,
407+
`kind: initiative_of`), tag it `Initiative`, and file there.
408+
The parked upmobility/urban-institute captures are the pilot filing
409+
(issue's worklist section A).
398410
- [x] **Person-destined content** (ruled 2026-07-27): individual funders
399411
keep normal `funders/<slug>/` folders (the funders corpus is the
400412
funder-to-strategy mapping substrate); DB identity is a **persons**
@@ -414,7 +426,9 @@ A condensed operational form of this skill ships in didi's chat as the
414426
**ACTIVE_SKILLS slab** in `services/workspace/src/chat.ts` (the Slab-3 spot
415427
reserved since v0.0.1), teaching the triage verbs
416428
(`organization.corpus.add`, `resolver.search`, `resolver.apply`,
417-
`resolver.update_org`, `organization.streams.add`) and the decision
429+
`resolver.update_org`, `organization.streams.add` — and, since 2026-07-27,
430+
the relations/tags verbs `organization.relations`, `organization.relate`,
431+
`organization.tag.add` with the step-5b aboutness routing) and the decision
418432
sequence above. Honest limit: chat-didi can register, resolve, mint, and
419433
stream — the *disk* half of a filing (canonical merge, pointer files,
420434
binary moves) still runs session-side via the scripts above, until a

services/workspace/src/chat.ts

Lines changed: 18 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -129,6 +129,22 @@ VERB RECOGNITION SHORTCUTS:
129129
- "/crawl-team <org-slug>" → chat_invoke organization.crawl with target "team".
130130
- A bare "/crawl-links" / "/crawl-streams" / "/crawl-team" (no argument), or "this org" / "this organization", targets the focused organization from context when one is present — chat_invoke directly with its org_slug. No focused org and no argument → ask.
131131
- Natural phrasings ("crawl for relevant pulse streams for X", "find X's team members") map to the same targets — chat_invoke when the org is unambiguous, chat_propose otherwise.
132+
133+
ORG RELATIONS + TAGS (per context-v/plans/Org-Relations-Parent-Child-Peer-Plus-Org-Tags.md):
134+
135+
organization.relations — Read one org's family tree: parents / children / peers, each with kind + description.
136+
args: { org_slug: string, client: string }
137+
138+
organization.relate — Connect two EXISTING orgs. rel is relative to org_slug: "parent" = other_slug is the parent. kind is an open vocabulary (initiative_of, fund_of, program_of, agency_of, chapter_of, funds, partners_with); description carries the human context worth keeping.
139+
args: { org_slug: string, other_slug: string, rel: "parent" | "child" | "peer", kind?: string, description?: string, client: string }
140+
141+
organization.tag.add — Tag an org for what it IS (Initiative, Program, Fund, Funder, Think-Tank…). Train-Case by convention.
142+
args: { org_slug: string, tag: string, client: string }
143+
144+
RELATIONS DISCIPLINE:
145+
- Relations are judgment calls — chat_propose by default; chat_invoke only when the operator stated the relationship themselves ("X is an initiative of Y" → relate X→parent Y, kind initiative_of, and quote their phrasing into description).
146+
- Both orgs must already exist — if one is missing, propose creating it first (resolver.search before ever proposing a mint).
147+
- Not every pairing is a hierarchy: funders/grantees and partners are rel "peer" with kind "funds" / "partners_with".
132148
`;
133149

134150
// Slab 3 — active skills. First resident (2026-07-25): the condensed
@@ -154,7 +170,7 @@ resolver.apply — Mint a new organization row (action "create"). Long-form full
154170
resolver.update_org — Enrich names right after ANY one-string create: complete_name (full formal name), conventional_name (what humans call it), aliases[] (greedy: acronyms, smushed forms, former names). Also renames slugs (old slug auto-preserved as alias).
155171
args: { org_slug: string, new_slug?: string, complete_name?: string, conventional_name?: string, aliases?: string[], client: string }
156172
157-
organization.streams.add — Register a rolling page as a pulse stream on an org. Kinds: "topic_stream" (topic/issues hub), "blog_index" (blog/news index), "initiative_hub" (a named initiative's hub page — also the lightweight answer for initiative-shaped pages while parent/child org modeling is unresolved).
173+
organization.streams.add — Register a rolling page as a pulse stream on an org. Kinds: "topic_stream" (topic/issues hub), "blog_index" (blog/news index), "initiative_hub" (a named initiative's hub page — still right when the initiative does NOT merit its own org row; when it does, mint the child and relate it instead).
158174
args: { org_slug: string, url: string, kind: string, name: string, client: string }
159175
160176
TRIAGE DECISION SEQUENCE (per item):
@@ -163,6 +179,7 @@ TRIAGE DECISION SEQUENCE (per item):
163179
3. Rolling index page (topic hub / blog index / initiative hub) on a tracked org? → organization.streams.add, not corpus content. A one-time capture of a page that keeps pulsing is worthless.
164180
4. Fetch-blocked capture (403/CAPTCHA/paywall)? → gated, not discarded: the URL is still wanted. Discard is only for genuinely worthless content (404 bodies, nav-only pages, consent boilerplate).
165181
5. Destination: org-attributable content → the org (search first, mint via resolver.apply if truly absent, enrich names immediately); topical content → an existing domain via source.add (resolve the slug against "Existing corpora" — never fabricate); tool homepages / content marketing → the tools topic; a profile page on an identity-link site (Candid, Cause IQ, Charity Navigator, GrantForward…) → the org it profiles.
182+
5b. Parent or child? When the destination org has relations (organization.relations) or the page names an initiative/fund/program of a parent, ask which entity the content is ABOUT before filing: parent-org content files on the parent, initiative content on the child. Both plausibly claim it → file by aboutness and tell the operator a reference_of pointer belongs across the seam (disk side, triage session). Initiative with no org row yet → propose minting the child + organization.relate (rel "parent", kind initiative_of) instead of filing onto the parent's streams when the initiative is a real actor.
166183
6. Org role buckets on disk (funders / gov-entities / think-tanks / associations-networks / academic-institutions / data-services) and the disk half of a filing (canonical file moves, reference_of pointer files, binary siblings) are handled by operator-side sessions, not chat — register the DB side here and tell the operator the file placement runs in the triage session.
167184
168185
TRIAGE DISCIPLINE:

0 commit comments

Comments
 (0)