strom-research 1.13.0 → 1.13.1-beta.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -18,6 +18,7 @@ import { readyConnectors } from "../core/connector.js";
18
18
  import { runs, shellArg } from "../cli/format.js";
19
19
  import { foldText } from "../core/text.js";
20
20
  import { subjectPeople } from "../core/records.js";
21
+ import { hypothesisPeople } from "../core/directions.js";
21
22
  export { DEFAULT_BUDGET } from "../core/config.js";
22
23
  import { DEFAULT_BUDGET } from "../core/config.js";
23
24
  /** Rough token estimate: good enough for a soft budget. */
@@ -254,7 +255,7 @@ export function buildBrief(tree, opts) {
254
255
  // the task's own conflicts and hypotheses come first, whatever their state
255
256
  const own = (id) => subjects.has(id);
256
257
  const conflicts = tree.list("conflict").filter((c) => own(c.id) || (c.state === "open" && c.subject.some((s) => ids.has(s))));
257
- const hyps = tree.list("hypothesis").filter((h) => own(h.id) || (h.state === "open" && h.subject.some((s) => ids.has(s))));
258
+ const hyps = tree.list("hypothesis").filter((h) => own(h.id) || (h.state === "open" && hypothesisPeople(tree, h).some((s) => ids.has(s))));
258
259
  const first = (list) => [...list.filter((x) => own(x.id)), ...list.filter((x) => !own(x.id))];
259
260
  const mark = (id, state) => `${own(id) ? "→ " : " "}${id}${state === "open" ? "" : ` [${state}]`}`;
260
261
  if (conflicts.length || hyps.length)
@@ -14,6 +14,25 @@ export function aboutPeople(tree, subject) {
14
14
  const partners = subject.filter((id) => typeOfId(id) === "family").flatMap((id) => tree.get(id)?.partners ?? []);
15
15
  return [...new Set([...subjectPeople(tree, subject), ...partners])];
16
16
  }
17
+ /** Whether a text names a record by its ID (any script around it: never \b, which knows only ASCII letters). */
18
+ export function namesId(text, id) {
19
+ return new RegExp(`(?<![\\p{L}\\p{M}\\p{N}])${id}(?![\\p{L}\\p{M}\\p{N}])`, "u").test(text);
20
+ }
21
+ const PERSON_ID = /(?<![\p{L}\p{M}\p{N}])P\d{4,}(?![\p{L}\p{M}\p{N}])/gu;
22
+ /**
23
+ * The people a hypothesis is about: those of its subject, and the people of the tree its question and its variants'
24
+ * claims name by ID ("the son of P0012 and P0013") — a variant that names a family nothing links to the tree would
25
+ * join it, though the subject names only the person of the tree. What each variant cites for or against it is
26
+ * evidence, not who it is about.
27
+ */
28
+ export function hypothesisPeople(tree, h) {
29
+ const named = [h.question, ...h.variants.map((v) => v.claim)].flatMap((text) => text.match(PERSON_ID) ?? []);
30
+ const people = named.filter((id) => {
31
+ const p = tree.get(id);
32
+ return p && !p.retracted;
33
+ });
34
+ return [...new Set([...aboutPeople(tree, h.subject), ...people])];
35
+ }
17
36
  /** The direction a task belongs to: its own, else the narrowest one its people are in; none for a task about nobody. */
18
37
  export function directionOf(tree, t, all) {
19
38
  if (t.research)
package/dist/core/edge.js CHANGED
@@ -6,7 +6,7 @@
6
6
  // tree ends (_STROM_EDGE, _STROM_ISLAND) and what `strom edge` says.
7
7
  import { now } from "./tree.js";
8
8
  import { Settings } from "./config.js";
9
- import { aboutPeople, directionOf, scopes } from "./directions.js";
9
+ import { aboutPeople, directionOf, hypothesisPeople, namesId, scopes } from "./directions.js";
10
10
  import { birthEstimate, birthRecordProven, birthWindow, frontier, FRONTIER_LEVELS, RECORD_KINDS, recordsetsCovering } from "./frontier.js";
11
11
  import { kinship, offTree } from "./kin.js";
12
12
  import { familiesAsChild, familiesAsPartner, parentsOf, primaryName } from "./people.js";
@@ -81,7 +81,7 @@ function islands(tree, kin, tasks, hypotheses) {
81
81
  const island = {
82
82
  people: [...people],
83
83
  hypotheses: hypotheses
84
- .map((h) => ({ h, about: aboutPeople(tree, h.subject) }))
84
+ .map((h) => ({ h, about: hypothesisPeople(tree, h) }))
85
85
  .filter(({ about }) => about.some((x) => people.has(x)) && about.some((x) => kin.has(x)))
86
86
  .map(({ h, about }) => ({ id: h.id, joins: about.filter((x) => kin.has(x)) })),
87
87
  held: tasks.filter((t) => LIVE.has(t.state) && off(t) && aboutPeople(tree, t.subject).some((x) => people.has(x))).length,
@@ -113,7 +113,7 @@ export function treeEdges(tree) {
113
113
  frontiers.set(r.id, new Map(frontier(tree, r).map((i) => [i.person.id, i])));
114
114
  return frontiers.get(r.id);
115
115
  };
116
- const hypAbout = hypotheses.map((h) => ({ h, about: aboutPeople(tree, h.subject) }));
116
+ const hypAbout = hypotheses.map((h) => ({ h, about: hypothesisPeople(tree, h) }));
117
117
  const edges = new Map();
118
118
  for (const p of tree.list("person")) {
119
119
  if (p.retracted)
@@ -256,7 +256,8 @@ export function treeEdges(tree) {
256
256
  question: h.question,
257
257
  joins,
258
258
  ...(isle ? { island: { people: isle.people.length, held: isle.held } } : {}),
259
- tests: tasks.filter((t) => LIVE.has(t.state) && t.subject.includes(h.id)).map((t) => t.id),
259
+ // a task about it, or one whose words name it ("the baptism … (H0007 B)")
260
+ tests: tasks.filter((t) => LIVE.has(t.state) && (t.subject.includes(h.id) || namesId(t.what, h.id))).map((t) => t.id),
260
261
  };
261
262
  });
262
263
  const edgeConflicts = conflicts
package/dist/core/kin.js CHANGED
@@ -2,7 +2,7 @@
2
2
  // app opens on them), and how near anyone is to the people the research is
3
3
  // for (which entries get their image in the Strom app first).
4
4
  import { Settings } from "./config.js";
5
- import { aboutPeople } from "./directions.js";
5
+ import { aboutPeople, hypothesisPeople } from "./directions.js";
6
6
  import { ancestorGenerations, familiesAsChild, familiesAsPartner } from "./people.js";
7
7
  function researches(tree) {
8
8
  return tree
@@ -125,5 +125,10 @@ export function joiningHypotheses(tree, people) {
125
125
  }
126
126
  return tree
127
127
  .list("hypothesis")
128
- .filter((h) => h.state === "open" && h.subject.some((s) => family.has(s)) && h.subject.some((s) => kin.has(s)));
128
+ .filter((h) => {
129
+ if (h.state !== "open")
130
+ return false;
131
+ const about = hypothesisPeople(tree, h);
132
+ return about.some((s) => family.has(s)) && about.some((s) => kin.has(s));
133
+ });
129
134
  }
@@ -7,6 +7,7 @@
7
7
  // Each kind is at most one task per person at a time, and what a task of that
8
8
  // kind took up is never proposed again.
9
9
  import { dateYears } from "./gdate.js";
10
+ import { hypothesisPeople } from "./directions.js";
10
11
  import { birthEstimate, RECORD_KINDS, recordsetsCovering, researchPeople } from "./frontier.js";
11
12
  import { birthEvent, displayName, familiesAsPartner, lifespan } from "./people.js";
12
13
  import { phrase } from "./phrases.js";
@@ -209,7 +210,7 @@ function openConflicts(tree, p) {
209
210
  const about = (subject) => subject.includes(p.id) || subject.some((s) => families.has(s));
210
211
  return [
211
212
  ...tree.list("conflict").filter((c) => c.state === "open" && about(c.subject)),
212
- ...tree.list("hypothesis").filter((h) => h.state === "open" && about(h.subject)),
213
+ ...tree.list("hypothesis").filter((h) => h.state === "open" && hypothesisPeople(tree, h).includes(p.id)),
213
214
  ].map((x) => x.id);
214
215
  }
215
216
  /** The tasks strom proposes for the people of a person research (strom review). */
package/dist/core/tree.js CHANGED
@@ -27,7 +27,7 @@ import { lastBackup } from "./backup.js";
27
27
  export const TREE_FILE = "strom.json";
28
28
  // between releases the candidate being made (a bridge run from the sources says what it is: the Strom app goes by it);
29
29
  // at a release the release's own (package.json)
30
- export const VERSION = "1.13.0";
30
+ export const VERSION = "1.13.1-beta.1";
31
31
  const GITIGNORE = `# Strom Research — local state and large files are not versioned
32
32
  .strom/
33
33
  /media/
@@ -34,6 +34,7 @@ import { quay } from "../core/evidence.js";
34
34
  import { VERSION } from "../core/tree.js";
35
35
  import { dataUrl, turned } from "../core/excerpt.js";
36
36
  import { mainPerson } from "../core/kin.js";
37
+ import { hypothesisPeople } from "../core/directions.js";
37
38
  import * as git from "../core/git.js";
38
39
  import { treeEdges } from "../core/edge.js";
39
40
  import { readersOf } from "../core/review.js";
@@ -141,7 +142,13 @@ export function exportGedcom(tree, opts = {}) {
141
142
  // What the research knows beyond the facts (the Strom profile, opts.research): read once.
142
143
  const research = {
143
144
  conflicts: opts.research ? tree.list("conflict") : [],
144
- hypotheses: opts.research ? tree.list("hypothesis").filter((h) => h.state === "open") : [],
145
+ // with the people each is about: its subject's, and those its variants name (core/directions.ts hypothesisPeople)
146
+ hypotheses: opts.research
147
+ ? tree
148
+ .list("hypothesis")
149
+ .filter((h) => h.state === "open")
150
+ .map((h) => ({ h, people: hypothesisPeople(tree, h) }))
151
+ : [],
145
152
  // a search is of the people of the task it served
146
153
  searched: opts.research
147
154
  ? tree
@@ -559,7 +566,7 @@ export function exportGedcom(tree, opts = {}) {
559
566
  if (c.state === "resolved" && c.resolution)
560
567
  w.text(2, "DECI", c.resolution);
561
568
  }
562
- for (const h of research.hypotheses.filter((x) => about(x.subject))) {
569
+ for (const { h } of research.hypotheses.filter((x) => x.people.includes(p.id))) {
563
570
  w.line(1, "_STROM_HYPO", h.id);
564
571
  w.text(2, "TITL", h.question);
565
572
  w.text(2, "NOTE", h.variants.map((v) => `${v.label}: ${v.claim}`).join("\n"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "strom-research",
3
- "version": "1.13.0",
3
+ "version": "1.13.1-beta.1",
4
4
  "description": "Genealogical research toolkit for AI coding agents",
5
5
  "type": "module",
6
6
  "bin": {
@@ -45,5 +45,8 @@
45
45
  "devDependencies": {
46
46
  "@types/node": "^22.20.4",
47
47
  "typescript": "^7.0.2"
48
+ },
49
+ "publishConfig": {
50
+ "tag": "beta"
48
51
  }
49
52
  }