@polycode-projects/the-mechanical-code-talker 0.2.0 → 0.3.0
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.
- package/ROADMAP.md +5 -2
- package/bin/tmct.mjs +253 -12
- package/corpus/README.md +52 -0
- package/corpus/conceptnet/LICENSE-NOTICE +37 -0
- package/corpus/conceptnet/README.md +103 -0
- package/corpus/conceptnet/fetch-slice.mjs +136 -0
- package/corpus/conceptnet/filter-dump.mjs +89 -0
- package/corpus/conceptnet/slice.jsonl +14258 -0
- package/data/phrasebook/software-phrases.txt +231 -0
- package/data/templates/responses.jsonl +55 -0
- package/package.json +12 -3
- package/src/ask-nlp.mjs +14 -0
- package/src/ask-vocab.mjs +13 -1
- package/src/ask.mjs +92 -493
- package/src/chat.mjs +147 -45
- package/src/corpus/conceptnet-map.toml +251 -0
- package/src/corpus/conceptnet.mjs +155 -0
- package/src/corpus/templates.mjs +104 -0
- package/src/grammar/ace.mjs +341 -0
- package/src/grammar/assert.mjs +40 -0
- package/src/grammar/lexicon-core.json +287 -0
- package/src/grammar/lexicon.mjs +202 -0
- package/src/index.mjs +21 -5
- package/src/interpret/fuzzy.mjs +89 -0
- package/src/interpret/merge.mjs +148 -0
- package/src/interpret/normalize.mjs +117 -0
- package/src/interpret/pipeline.mjs +112 -0
- package/src/interpret/strategies/grammar.mjs +137 -0
- package/src/interpret/strategies/keywords.mjs +185 -0
- package/src/interpret/strategies/noise-strip.mjs +114 -0
- package/src/memory/blocks.mjs +201 -0
- package/src/memory/core.mjs +292 -0
- package/src/memory/fold.mjs +105 -0
- package/src/sessions.mjs +125 -3
- package/src/source.mjs +44 -5
- package/src/tui/app.mjs +173 -0
- package/bin/cli.mjs +0 -226
package/src/chat.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// chat.mjs — `tmct chat`: a full interactive client over the tmct code-graph.
|
|
2
2
|
// Any BARE line is a plain-English question dispatched through the mechanical
|
|
3
|
-
// tmct_ask engine (the EXACT path bin/
|
|
3
|
+
// tmct_ask engine (the EXACT path bin/tmct.mjs's `cli tmct_ask` fallback uses),
|
|
4
4
|
// so chat is the same zero-model engine with a readline shell around it, plus:
|
|
5
5
|
//
|
|
6
6
|
// - SLASH-COMMANDS to reach every richer tool dispatchTool (server.mjs) serves —
|
|
@@ -31,6 +31,11 @@
|
|
|
31
31
|
// focus }) so tests exercise it directly; every ask.mjs import is LAZY and
|
|
32
32
|
// failure-tolerated, so concurrent evolution of the engine can never crash a turn
|
|
33
33
|
// (worst case a turn records fewer ids / an honest miss hint, never wrong data).
|
|
34
|
+
//
|
|
35
|
+
// createSession(…) is the SESSION SINK every shell shares: it owns the artifact
|
|
36
|
+
// files, the per-turn writeLog → writeSidecar → upsertGraph sequencing (order is
|
|
37
|
+
// load-bearing — see its docblock), telemetry, and the close. runChat is the
|
|
38
|
+
// readline shell over it; src/tui/app.mjs is the Ink shell over the same sink.
|
|
34
39
|
|
|
35
40
|
import { join } from "node:path";
|
|
36
41
|
import { createWriteStream } from "node:fs";
|
|
@@ -315,7 +320,7 @@ export function gitToplevel(cwd = process.cwd()) {
|
|
|
315
320
|
return null;
|
|
316
321
|
}
|
|
317
322
|
|
|
318
|
-
/** Mirror bin/
|
|
323
|
+
/** Mirror bin/tmct.mjs's configFor: an explicit repo pins the artifact path; no
|
|
319
324
|
* repo falls back to the cwd/env-derived default. */
|
|
320
325
|
function configFor(repoPath) {
|
|
321
326
|
return repoPath ? { graphFile: join(repoPath, DEFAULT_GRAPH_REL) } : loadConfig();
|
|
@@ -475,6 +480,34 @@ async function runCommand(line, { config, source, graph, focus }) {
|
|
|
475
480
|
return mk(answer);
|
|
476
481
|
}
|
|
477
482
|
|
|
483
|
+
/** A declarative ACE-grammar sentence → assert into memory + confirm; null on
|
|
484
|
+
* any grammar miss / residue / import failure so the query engine keeps first
|
|
485
|
+
* refusal on everything else. Lazy imports + catch-all: the grammar layer can
|
|
486
|
+
* never crash a turn (chat.mjs ethos). Writes ONLY under memoryDir/.tmct/memory. */
|
|
487
|
+
async function assertTurn(line, { memoryDir, sessionId, focus }) {
|
|
488
|
+
try {
|
|
489
|
+
const { parseAce } = await import("./grammar/ace.mjs");
|
|
490
|
+
const { loadLexicon } = await import("./grammar/lexicon.mjs");
|
|
491
|
+
const parse = parseAce(line, loadLexicon());
|
|
492
|
+
if (!parse || !parse.triples?.length || parse.residue?.length) return null;
|
|
493
|
+
const { assertSentence } = await import("./grammar/assert.mjs");
|
|
494
|
+
const { normFactTerm } = await import("./memory/core.mjs");
|
|
495
|
+
const ts = new Date().toISOString();
|
|
496
|
+
const res = await assertSentence(memoryDir, line, {
|
|
497
|
+
provenance: { source: "chat", sessionId, ts },
|
|
498
|
+
});
|
|
499
|
+
if (!res || !res.ids?.length) return null;
|
|
500
|
+
const shown = res.triples
|
|
501
|
+
.map((t) => `${normFactTerm(t.subject)} ${t.predicate} ${normFactTerm(t.object)}`)
|
|
502
|
+
.join("; ");
|
|
503
|
+
const n = res.ids.length;
|
|
504
|
+
const answer = `noted — remembered ${n} fact${n === 1 ? "" : "s"}: ${shown}`;
|
|
505
|
+
return plainTurn(line, answer, { command: "assert", focus });
|
|
506
|
+
} catch {
|
|
507
|
+
return null; // grammar unavailable / write failed — fall through to the engine
|
|
508
|
+
}
|
|
509
|
+
}
|
|
510
|
+
|
|
478
511
|
/**
|
|
479
512
|
* One chat turn: input → { answer, logLines, record, focus }. Pure of any
|
|
480
513
|
* TTY/stream concerns so tests exercise it directly. A leading `/` routes to a
|
|
@@ -489,9 +522,9 @@ async function runCommand(line, { config, source, graph, focus }) {
|
|
|
489
522
|
* subject), `answeredIds` the entity ids an ask answer cited; a slash-command turn
|
|
490
523
|
* also carries its `command` name. Both drive the mgx:asksAbout graph append.
|
|
491
524
|
*/
|
|
492
|
-
export async function runTurn(input, { config, source = defaultSource, graph = null, focus = null, last = null } = {}) {
|
|
525
|
+
export async function runTurn(input, { config, source = defaultSource, graph = null, focus = null, last = null, memoryDir = null, sessionId = "" } = {}) {
|
|
493
526
|
const line = String(input ?? "").trim();
|
|
494
|
-
const ctx = { config, source, graph, focus, last };
|
|
527
|
+
const ctx = { config, source, graph, focus, last, memoryDir, sessionId };
|
|
495
528
|
// A DISPATCHED turn (count / slash-command / ask) becomes the new "last answer"
|
|
496
529
|
// that why/say-more re-renders; a conversational turn does not (it preserves it).
|
|
497
530
|
const withLast = (result) => ({ ...result, last: { query: line, answer: result.answer, detail: result.detail ?? null } });
|
|
@@ -502,6 +535,14 @@ export async function runTurn(input, { config, source = defaultSource, graph = n
|
|
|
502
535
|
if (convo) return convo;
|
|
503
536
|
|
|
504
537
|
if (line.startsWith("/")) return withLast(await runCommand(line, ctx));
|
|
538
|
+
// Declarative ACE sentences ("every module is a artifact") ASSERT into tmct's
|
|
539
|
+
// own memory and confirm — they are statements to remember, not graph queries.
|
|
540
|
+
// Gated on memoryDir: only a session shell provides a write target, so a bare
|
|
541
|
+
// runTurn (tests, library callers) stays pure and falls through to the engine.
|
|
542
|
+
if (memoryDir) {
|
|
543
|
+
const asserted = await assertTurn(line, ctx);
|
|
544
|
+
if (asserted) return withLast(asserted);
|
|
545
|
+
}
|
|
505
546
|
// Aggregate/count questions are answered mechanically off the loaded graph header,
|
|
506
547
|
// BEFORE falling through to the ask engine (focus unchanged — a count names no entity).
|
|
507
548
|
const count = answerCount(graph, line);
|
|
@@ -514,16 +555,28 @@ const shortLabel = (l) => { const s = String(l); return s.length > 40 ? "…" +
|
|
|
514
555
|
const promptFor = (focus) => (focus ? `tmct(${shortLabel(focus.label)})> ` : PROMPT);
|
|
515
556
|
|
|
516
557
|
/**
|
|
517
|
-
* The
|
|
518
|
-
*
|
|
519
|
-
*
|
|
520
|
-
*
|
|
521
|
-
*
|
|
558
|
+
* The SESSION SINK — everything a chat shell (readline below, the Ink TUI, any
|
|
559
|
+
* future surface) must share so the on-disk session contract stays identical no
|
|
560
|
+
* matter what draws the screen:
|
|
561
|
+
*
|
|
562
|
+
* - repo/config resolution (git root default, --repo override) + the one-time
|
|
563
|
+
* graph load and banner strings;
|
|
564
|
+
* - the transcript log + structured sidecar file creation and per-turn
|
|
565
|
+
* writeLog → writeSidecar → upsertGraph sequencing. THE ORDER IS LOAD-BEARING:
|
|
566
|
+
* the memory side-write (sessions.mjs) recovers each turn's ANSWER text by
|
|
567
|
+
* re-reading the transcript keyed by turnKey(record.ts, query), so the log
|
|
568
|
+
* line must be flushed before the graph upsert runs, and logLines[0] must be
|
|
569
|
+
* the record's ts (runTurn guarantees that);
|
|
570
|
+
* - opt-in telemetry and the end-of-session close (end lines, final upsert,
|
|
571
|
+
* stream flush).
|
|
572
|
+
*
|
|
573
|
+
* Returns { repo, config, graph, moduleCount, version, sessionId, logFile,
|
|
574
|
+
* sidecarFile, bannerLines, empty, focus, turns, promptFor(), turn(line), close() }.
|
|
575
|
+
* `turn(line)` runs one dispatched turn through runTurn and the full sink
|
|
576
|
+
* sequencing, returning { answer, end, prompt }; `close()` is idempotent.
|
|
522
577
|
*/
|
|
523
|
-
export async function
|
|
578
|
+
export async function createSession({
|
|
524
579
|
repoPath,
|
|
525
|
-
input = process.stdin,
|
|
526
|
-
output = process.stdout,
|
|
527
580
|
source = defaultSource,
|
|
528
581
|
env = process.env,
|
|
529
582
|
cwd = process.cwd(),
|
|
@@ -548,7 +601,7 @@ export async function runChat({
|
|
|
548
601
|
const moduleCount = graph.individuals.filter((i) => (i.class || "") === "Module").length;
|
|
549
602
|
const { version } = JSON.parse(await readFile(new URL("../package.json", import.meta.url), "utf8"));
|
|
550
603
|
|
|
551
|
-
// Opt-in telemetry (default OFF → null → the
|
|
604
|
+
// Opt-in telemetry (default OFF → null → the sink's `tel?.record` is a no-op, and
|
|
552
605
|
// nothing is written). The conversational session log + sidecar above stay the
|
|
553
606
|
// authoritative chat record; this is the machine-readable query telemetry.
|
|
554
607
|
const tel = createTelemetry({ env, config, surface: "chat" });
|
|
@@ -562,7 +615,7 @@ export async function runChat({
|
|
|
562
615
|
const sidecarFile = join(sessionsDir, `session-${sessionId}.jsonl`);
|
|
563
616
|
const stream = createWriteStream(logFile, { flags: "a" });
|
|
564
617
|
const sidecar = createWriteStream(sidecarFile, { flags: "a" });
|
|
565
|
-
// Awaited writes: each chunk is handed to the OS before the
|
|
618
|
+
// Awaited writes: each chunk is handed to the OS before the turn completes, so a
|
|
566
619
|
// killed session keeps everything up to the last completed turn — in both files.
|
|
567
620
|
const flush = (s, text) =>
|
|
568
621
|
new Promise((resolve, reject) => s.write(text, (e) => (e ? reject(e) : resolve())));
|
|
@@ -584,35 +637,36 @@ export async function runChat({
|
|
|
584
637
|
catch { /* best-effort — see above */ }
|
|
585
638
|
};
|
|
586
639
|
|
|
587
|
-
const
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
`
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
const rl = createInterface({ input, output, prompt: PROMPT });
|
|
599
|
-
rl.on("SIGINT", () => rl.close()); // Ctrl+C behaves like /exit (clean close, log flushed)
|
|
600
|
-
let closed = false;
|
|
601
|
-
rl.on("close", () => { closed = true; });
|
|
602
|
-
const prompt = () => { if (!closed) rl.prompt(); }; // input may end while a turn is in flight
|
|
640
|
+
const empty = graph.individuals.length === 0;
|
|
641
|
+
const bannerLines = [
|
|
642
|
+
empty
|
|
643
|
+
// Empty-graph bootstrap: honest-miss messaging, never an error before the prompt.
|
|
644
|
+
? `tmct chat — ${repo} — no graph loaded — starting empty; ` +
|
|
645
|
+
`the conversation is remembered to ${DEFAULT_GRAPH_REL} — log ${logFile}`
|
|
646
|
+
: `tmct chat — ${repo} — ${moduleCount} module(s) — log ${logFile}`,
|
|
647
|
+
"pass --repo <path> to target a different repo",
|
|
648
|
+
"ask a question, or /help for commands (/stats for an overview) — /exit to leave",
|
|
649
|
+
];
|
|
603
650
|
|
|
604
651
|
let turns = 0;
|
|
605
652
|
let focus = null; // the current focus entity ({id,label}) — threaded turn to turn
|
|
606
653
|
let last = null; // the last dispatched answer ({query,answer,detail}) — why/say-more re-renders it
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
654
|
+
let closed = false;
|
|
655
|
+
|
|
656
|
+
return {
|
|
657
|
+
repo, config, graph, moduleCount, version, sessionId, logFile, sidecarFile,
|
|
658
|
+
bannerLines, empty,
|
|
659
|
+
get focus() { return focus; },
|
|
660
|
+
get turns() { return turns; },
|
|
661
|
+
promptFor: () => promptFor(focus),
|
|
662
|
+
|
|
663
|
+
/** One dispatched turn through the FULL sink sequencing (writeLog → writeSidecar
|
|
664
|
+
* → telemetry → upsertGraph, in that exact order). Returns { answer, end, prompt }. */
|
|
665
|
+
async turn(line) {
|
|
666
|
+
const { answer, logLines, record, focus: nextFocus, last: nextLast, end } =
|
|
667
|
+
await runTurn(line, { config, source, graph, focus, last, memoryDir: repo, sessionId });
|
|
613
668
|
focus = nextFocus;
|
|
614
669
|
last = nextLast;
|
|
615
|
-
output.write(answer + "\n");
|
|
616
670
|
await writeLog(logLines.join("\n") + "\n");
|
|
617
671
|
await writeSidecar(record);
|
|
618
672
|
turnRecords.push(record);
|
|
@@ -625,18 +679,66 @@ export async function runChat({
|
|
|
625
679
|
});
|
|
626
680
|
await upsertGraph(record.ts);
|
|
627
681
|
turns += 1;
|
|
628
|
-
|
|
682
|
+
return { answer, end: Boolean(end), prompt: promptFor(focus) };
|
|
683
|
+
},
|
|
684
|
+
|
|
685
|
+
/** End-of-session close: end lines in both artifacts, the final graph upsert
|
|
686
|
+
* (which also triggers the memory fold), stream flush. Idempotent. */
|
|
687
|
+
async close() {
|
|
688
|
+
if (closed) return;
|
|
689
|
+
closed = true;
|
|
690
|
+
const endIso = new Date().toISOString();
|
|
691
|
+
await writeLog(`${endIso}\n> /exit\nsession end ${endIso}\n`);
|
|
692
|
+
await writeSidecar({ type: "end", ts: endIso });
|
|
693
|
+
await upsertGraph(endIso);
|
|
694
|
+
await new Promise((resolve) => stream.end(resolve));
|
|
695
|
+
await new Promise((resolve) => sidecar.end(resolve));
|
|
696
|
+
},
|
|
697
|
+
};
|
|
698
|
+
}
|
|
699
|
+
|
|
700
|
+
/**
|
|
701
|
+
* The interactive readline shell over createSession — the `--plain` surface and
|
|
702
|
+
* the scripted-test surface. Streams are injectable so tests run sessions
|
|
703
|
+
* without a TTY. A repo with NO graph artifact is not an error: the session
|
|
704
|
+
* starts from the empty bootstrap graph (the banner says so honestly) and the
|
|
705
|
+
* first turn's fold-in creates .tmct/graph.json from the conversation itself.
|
|
706
|
+
* Returns { logFile, sidecarFile, turns } once the session ends.
|
|
707
|
+
*/
|
|
708
|
+
export async function runChat({
|
|
709
|
+
repoPath,
|
|
710
|
+
input = process.stdin,
|
|
711
|
+
output = process.stdout,
|
|
712
|
+
source = defaultSource,
|
|
713
|
+
env = process.env,
|
|
714
|
+
cwd = process.cwd(),
|
|
715
|
+
gitRoot = gitToplevel,
|
|
716
|
+
} = {}) {
|
|
717
|
+
const session = await createSession({ repoPath, source, env, cwd, gitRoot });
|
|
718
|
+
|
|
719
|
+
const dim = (s) => (env.NO_COLOR || !output.isTTY ? s : `\x1b[2m${s}\x1b[0m`);
|
|
720
|
+
for (const line of session.bannerLines) output.write(dim(line) + "\n");
|
|
721
|
+
|
|
722
|
+
const rl = createInterface({ input, output, prompt: PROMPT });
|
|
723
|
+
rl.on("SIGINT", () => rl.close()); // Ctrl+C behaves like /exit (clean close, log flushed)
|
|
724
|
+
let closed = false;
|
|
725
|
+
rl.on("close", () => { closed = true; });
|
|
726
|
+
const prompt = () => { if (!closed) rl.prompt(); }; // input may end while a turn is in flight
|
|
727
|
+
|
|
728
|
+
prompt();
|
|
729
|
+
for await (const raw of rl) { // Ctrl+D / closed stdin ends the iteration cleanly
|
|
730
|
+
const line = raw.trim();
|
|
731
|
+
if (line === "/exit") break;
|
|
732
|
+
if (line) {
|
|
733
|
+
const { answer, end, prompt: nextPrompt } = await session.turn(line);
|
|
734
|
+
output.write(answer + "\n");
|
|
735
|
+
rl.setPrompt(nextPrompt);
|
|
629
736
|
if (end) break; // a conversational "bye"/"goodbye" — clean end, same as /exit
|
|
630
737
|
}
|
|
631
738
|
prompt();
|
|
632
739
|
}
|
|
633
740
|
rl.close();
|
|
634
741
|
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
await writeSidecar({ type: "end", ts: endIso });
|
|
638
|
-
await upsertGraph(endIso);
|
|
639
|
-
await new Promise((resolve) => stream.end(resolve));
|
|
640
|
-
await new Promise((resolve) => sidecar.end(resolve));
|
|
641
|
-
return { logFile, sidecarFile, turns };
|
|
742
|
+
await session.close();
|
|
743
|
+
return { logFile: session.logFile, sidecarFile: session.sidecarFile, turns: session.turns };
|
|
642
744
|
}
|
|
@@ -0,0 +1,251 @@
|
|
|
1
|
+
# conceptnet-map.toml — the relation → ACE-OWL-pattern mapping table
|
|
2
|
+
# (ROADMAP Phase 2, ConceptNet corpus slice).
|
|
3
|
+
#
|
|
4
|
+
# One row per ConceptNet relation (the canonical closed set of 34 —
|
|
5
|
+
# docs/references/schemas/conceptnet-relations.md is the reference list this
|
|
6
|
+
# table is drift-checked against; test/corpus-conceptnet.test.mjs fails if the
|
|
7
|
+
# committed slice contains a relation with no row here).
|
|
8
|
+
#
|
|
9
|
+
# Fields:
|
|
10
|
+
# rel the ConceptNet relation URI ("/r/IsA")
|
|
11
|
+
# surface the canonical surface template, "{start}"/"{end}" slotted —
|
|
12
|
+
# the "A dog is a kind of animal" sentence shape this relation reads as
|
|
13
|
+
# ace which ACE-OWL pattern it maps to (docs/references/schemas/
|
|
14
|
+
# ace-owl-fragment.md): subClassOf | type | ObjectProperty |
|
|
15
|
+
# someValuesFrom | disjointWith | property | none
|
|
16
|
+
# predicate the predicate URI src/corpus/conceptnet.mjs emits into memory
|
|
17
|
+
# facts (absent when ace = "none" — no fact is emitted)
|
|
18
|
+
# note why, and what a "none" row is still good for
|
|
19
|
+
#
|
|
20
|
+
# Loader contract (src/corpus/conceptnet.mjs): a relation in the slice that is
|
|
21
|
+
# MISSING here is an error (drift guard); a row with ace = "none" is a
|
|
22
|
+
# deliberate non-emission, silently skipped by toFacts().
|
|
23
|
+
|
|
24
|
+
[[relation]]
|
|
25
|
+
rel = "/r/IsA"
|
|
26
|
+
surface = "a {start} is a kind of {end}"
|
|
27
|
+
ace = "subClassOf"
|
|
28
|
+
predicate = "rdfs:subClassOf"
|
|
29
|
+
note = "hyponym → hypernym; ACE pattern 1 ('every N1 is a N2')"
|
|
30
|
+
|
|
31
|
+
[[relation]]
|
|
32
|
+
rel = "/r/DefinedAs"
|
|
33
|
+
surface = "a {start} is defined as {end}"
|
|
34
|
+
ace = "subClassOf"
|
|
35
|
+
predicate = "rdfs:subClassOf"
|
|
36
|
+
note = "definitional IsA; also ACE pattern 1"
|
|
37
|
+
|
|
38
|
+
[[relation]]
|
|
39
|
+
rel = "/r/PartOf"
|
|
40
|
+
surface = "a {start} is part of a {end}"
|
|
41
|
+
ace = "ObjectProperty"
|
|
42
|
+
predicate = "mgx:partOf"
|
|
43
|
+
note = "meronymy; ACE pattern 3 (N1 VERB N2)"
|
|
44
|
+
|
|
45
|
+
[[relation]]
|
|
46
|
+
rel = "/r/HasA"
|
|
47
|
+
surface = "a {start} has a {end}"
|
|
48
|
+
ace = "ObjectProperty"
|
|
49
|
+
predicate = "mgx:hasA"
|
|
50
|
+
note = "possession/holonymy; plain edge today — ACE pattern 5 cardinality is a later refinement"
|
|
51
|
+
|
|
52
|
+
[[relation]]
|
|
53
|
+
rel = "/r/UsedFor"
|
|
54
|
+
surface = "a {start} is used for {end}"
|
|
55
|
+
ace = "ObjectProperty"
|
|
56
|
+
predicate = "mgx:usedFor"
|
|
57
|
+
note = "typical purpose; ACE pattern 3"
|
|
58
|
+
|
|
59
|
+
[[relation]]
|
|
60
|
+
rel = "/r/CapableOf"
|
|
61
|
+
surface = "a {start} can {end}"
|
|
62
|
+
ace = "ObjectProperty"
|
|
63
|
+
predicate = "mgx:capableOf"
|
|
64
|
+
note = "typical capability; ACE pattern 3"
|
|
65
|
+
|
|
66
|
+
[[relation]]
|
|
67
|
+
rel = "/r/AtLocation"
|
|
68
|
+
surface = "you are likely to find a {start} in a {end}"
|
|
69
|
+
ace = "ObjectProperty"
|
|
70
|
+
predicate = "mgx:atLocation"
|
|
71
|
+
note = "typical location; ACE pattern 3"
|
|
72
|
+
|
|
73
|
+
[[relation]]
|
|
74
|
+
rel = "/r/Causes"
|
|
75
|
+
surface = "a {start} causes {end}"
|
|
76
|
+
ace = "ObjectProperty"
|
|
77
|
+
predicate = "mgx:causes"
|
|
78
|
+
note = "causation; ACE pattern 3"
|
|
79
|
+
|
|
80
|
+
[[relation]]
|
|
81
|
+
rel = "/r/HasSubevent"
|
|
82
|
+
surface = "when you {start}, you {end}"
|
|
83
|
+
ace = "ObjectProperty"
|
|
84
|
+
predicate = "mgx:hasSubevent"
|
|
85
|
+
note = "event decomposition; ACE pattern 3"
|
|
86
|
+
|
|
87
|
+
[[relation]]
|
|
88
|
+
rel = "/r/HasFirstSubevent"
|
|
89
|
+
surface = "the first thing you do when you {start} is {end}"
|
|
90
|
+
ace = "ObjectProperty"
|
|
91
|
+
predicate = "mgx:hasFirstSubevent"
|
|
92
|
+
note = "first step; ACE pattern 3"
|
|
93
|
+
|
|
94
|
+
[[relation]]
|
|
95
|
+
rel = "/r/HasLastSubevent"
|
|
96
|
+
surface = "the last thing you do when you {start} is {end}"
|
|
97
|
+
ace = "ObjectProperty"
|
|
98
|
+
predicate = "mgx:hasLastSubevent"
|
|
99
|
+
note = "last step; ACE pattern 3"
|
|
100
|
+
|
|
101
|
+
[[relation]]
|
|
102
|
+
rel = "/r/HasPrerequisite"
|
|
103
|
+
surface = "in order to {start}, you must {end}"
|
|
104
|
+
ace = "ObjectProperty"
|
|
105
|
+
predicate = "mgx:hasPrerequisite"
|
|
106
|
+
note = "dependency; ACE pattern 3"
|
|
107
|
+
|
|
108
|
+
[[relation]]
|
|
109
|
+
rel = "/r/HasProperty"
|
|
110
|
+
surface = "a {start} is {end}"
|
|
111
|
+
ace = "property"
|
|
112
|
+
predicate = "mgx:hasProperty"
|
|
113
|
+
note = "attribute/adjective; ACE pattern 8 ('N1 is ADJ')"
|
|
114
|
+
|
|
115
|
+
[[relation]]
|
|
116
|
+
rel = "/r/MotivatedByGoal"
|
|
117
|
+
surface = "you would {start} because you want to {end}"
|
|
118
|
+
ace = "ObjectProperty"
|
|
119
|
+
predicate = "mgx:motivatedByGoal"
|
|
120
|
+
note = "motivation; ACE pattern 3"
|
|
121
|
+
|
|
122
|
+
[[relation]]
|
|
123
|
+
rel = "/r/ObstructedBy"
|
|
124
|
+
surface = "{start} can be prevented by {end}"
|
|
125
|
+
ace = "ObjectProperty"
|
|
126
|
+
predicate = "mgx:obstructedBy"
|
|
127
|
+
note = "blocker; ACE pattern 3"
|
|
128
|
+
|
|
129
|
+
[[relation]]
|
|
130
|
+
rel = "/r/Desires"
|
|
131
|
+
surface = "a {start} wants {end}"
|
|
132
|
+
ace = "ObjectProperty"
|
|
133
|
+
predicate = "mgx:desires"
|
|
134
|
+
note = "typical desire; ACE pattern 3"
|
|
135
|
+
|
|
136
|
+
[[relation]]
|
|
137
|
+
rel = "/r/CausesDesire"
|
|
138
|
+
surface = "{start} makes you want to {end}"
|
|
139
|
+
ace = "ObjectProperty"
|
|
140
|
+
predicate = "mgx:causesDesire"
|
|
141
|
+
note = "evoked desire; ACE pattern 3"
|
|
142
|
+
|
|
143
|
+
[[relation]]
|
|
144
|
+
rel = "/r/CreatedBy"
|
|
145
|
+
surface = "a {start} is created by a {end}"
|
|
146
|
+
ace = "ObjectProperty"
|
|
147
|
+
predicate = "mgx:createdBy"
|
|
148
|
+
note = "provenance; ACE pattern 3"
|
|
149
|
+
|
|
150
|
+
[[relation]]
|
|
151
|
+
rel = "/r/MadeOf"
|
|
152
|
+
surface = "a {start} is made of {end}"
|
|
153
|
+
ace = "ObjectProperty"
|
|
154
|
+
predicate = "mgx:madeOf"
|
|
155
|
+
note = "material; ACE pattern 3"
|
|
156
|
+
|
|
157
|
+
[[relation]]
|
|
158
|
+
rel = "/r/ReceivesAction"
|
|
159
|
+
surface = "a {start} can be {end}"
|
|
160
|
+
ace = "ObjectProperty"
|
|
161
|
+
predicate = "mgx:receivesAction"
|
|
162
|
+
note = "typical patient role; ACE pattern 3"
|
|
163
|
+
|
|
164
|
+
[[relation]]
|
|
165
|
+
rel = "/r/LocatedNear"
|
|
166
|
+
surface = "a {start} is typically near a {end}"
|
|
167
|
+
ace = "ObjectProperty"
|
|
168
|
+
predicate = "mgx:locatedNear"
|
|
169
|
+
note = "proximity; ACE pattern 3"
|
|
170
|
+
|
|
171
|
+
[[relation]]
|
|
172
|
+
rel = "/r/MannerOf"
|
|
173
|
+
surface = "{start} is a way to {end}"
|
|
174
|
+
ace = "ObjectProperty"
|
|
175
|
+
predicate = "mgx:mannerOf"
|
|
176
|
+
note = "verb specialization — properly rdfs:subPropertyOf between verbs; stored as a plain edge until the grammar grows verb hierarchies"
|
|
177
|
+
|
|
178
|
+
[[relation]]
|
|
179
|
+
rel = "/r/DistinctFrom"
|
|
180
|
+
surface = "a {start} is not a {end}"
|
|
181
|
+
ace = "disjointWith"
|
|
182
|
+
predicate = "owl:disjointWith"
|
|
183
|
+
note = "mutual exclusion; ACE pattern 6 ('no N1 is a N2')"
|
|
184
|
+
|
|
185
|
+
# --- unmappable relations: no clean OWL-axiom fit; kept for other consumers ---
|
|
186
|
+
|
|
187
|
+
[[relation]]
|
|
188
|
+
rel = "/r/RelatedTo"
|
|
189
|
+
surface = "{start} is related to {end}"
|
|
190
|
+
ace = "none"
|
|
191
|
+
note = "weakest, undirected association — too vague for an axiom; useful later as a fuzzy-match hint, never a fact"
|
|
192
|
+
|
|
193
|
+
[[relation]]
|
|
194
|
+
rel = "/r/Synonym"
|
|
195
|
+
surface = "{start} means the same as {end}"
|
|
196
|
+
ace = "none"
|
|
197
|
+
note = "lexical alias, not an axiom — feeds the grammar lexicon / phrasebook synonym families instead"
|
|
198
|
+
|
|
199
|
+
[[relation]]
|
|
200
|
+
rel = "/r/Antonym"
|
|
201
|
+
surface = "{start} is the opposite of {end}"
|
|
202
|
+
ace = "none"
|
|
203
|
+
note = "lexical opposition; OWL disjointness would over-claim (hot/cold are not disjoint classes) — DistinctFrom carries the real disjointness"
|
|
204
|
+
|
|
205
|
+
[[relation]]
|
|
206
|
+
rel = "/r/FormOf"
|
|
207
|
+
surface = "{start} is a form of the word {end}"
|
|
208
|
+
ace = "none"
|
|
209
|
+
note = "inflection → root; lexicon normalization, not knowledge"
|
|
210
|
+
|
|
211
|
+
[[relation]]
|
|
212
|
+
rel = "/r/DerivedFrom"
|
|
213
|
+
surface = "the word {start} is derived from {end}"
|
|
214
|
+
ace = "none"
|
|
215
|
+
note = "word derivation; lexicon material, not an axiom"
|
|
216
|
+
|
|
217
|
+
[[relation]]
|
|
218
|
+
rel = "/r/SymbolOf"
|
|
219
|
+
surface = "{start} is a symbol of {end}"
|
|
220
|
+
ace = "none"
|
|
221
|
+
note = "symbolism — no OWL fit"
|
|
222
|
+
|
|
223
|
+
[[relation]]
|
|
224
|
+
rel = "/r/SimilarTo"
|
|
225
|
+
surface = "{start} is similar to {end}"
|
|
226
|
+
ace = "none"
|
|
227
|
+
note = "graded similarity — no crisp OWL fit; potential fuzzy-match hint only"
|
|
228
|
+
|
|
229
|
+
[[relation]]
|
|
230
|
+
rel = "/r/HasContext"
|
|
231
|
+
surface = "{start} is used in the context of {end}"
|
|
232
|
+
ace = "none"
|
|
233
|
+
note = "usage-domain tag — the slice FILTER signal (tech-domain selection), not a fact to store"
|
|
234
|
+
|
|
235
|
+
[[relation]]
|
|
236
|
+
rel = "/r/EtymologicallyRelatedTo"
|
|
237
|
+
surface = "{start} shares an origin with {end}"
|
|
238
|
+
ace = "none"
|
|
239
|
+
note = "etymology — filtered out of the slice by policy"
|
|
240
|
+
|
|
241
|
+
[[relation]]
|
|
242
|
+
rel = "/r/EtymologicallyDerivedFrom"
|
|
243
|
+
surface = "the word {start} comes from {end}"
|
|
244
|
+
ace = "none"
|
|
245
|
+
note = "etymology — filtered out of the slice by policy"
|
|
246
|
+
|
|
247
|
+
[[relation]]
|
|
248
|
+
rel = "/r/ExternalURL"
|
|
249
|
+
surface = "{start} is described at {end}"
|
|
250
|
+
ace = "none"
|
|
251
|
+
note = "link out of the graph — filtered out of the slice (end is a URL, not an en concept)"
|