@pellux/goodvibes-contracts 0.38.0 → 1.0.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.
@@ -0,0 +1,102 @@
1
+ /**
2
+ * core-verbs.ts — the canonical operator-method verb vocabulary (E8, W6-C3).
3
+ *
4
+ * WHY THIS EXISTS: OPERATOR_METHOD_IDS (generated/operator-method-ids.ts) is a
5
+ * flat list of dotted ids (`<namespace...>.<verb>`). Before this file, nothing
6
+ * enumerated or constrained the verb vocabulary — every method-catalog author
7
+ * picked whatever word felt right, which is how the wave-6 audit found three
8
+ * worst-class collisions on the word "schedule", a redundant lifecycle pair
9
+ * (`enable`/`disable` duplicated by `pause`/`resume`), and an update-verb split
10
+ * (`patch` vs `update`). This module is the forcing function that keeps that
11
+ * from recurring: CORE_VERBS is the closed vocabulary for generic lifecycle
12
+ * operations, BANNED_VERBS are verbs that were retired and must never
13
+ * reappear, and EXEMPT_VERB_CATEGORIES documents the (large, expected) set of
14
+ * domain-specific verbs that aren't generic CRUD/lifecycle words — things like
15
+ * `voice.stt`, `homeassistant.homeGraph.askHomeGraph`, or `telemetry.otlp.logs`
16
+ * are real, single-purpose operations, not a coherence bug.
17
+ *
18
+ * NAMESPACE RULE: a resource family name is the plural noun the family
19
+ * manages (`tasks`, `sessions`, `schedules`, `watchers`); verbs attach
20
+ * directly to it (`tasks.list`, `tasks.get`). The ONE exception is a
21
+ * documented, reusable pattern — not ad hoc: when a family already has both a
22
+ * per-item action surface AND a collection surface, and giving both the same
23
+ * plural name would be ambiguous about which one an action targets, the
24
+ * per-item family takes the SINGULAR form and the collection keeps the
25
+ * PLURAL. `knowledge.schedule.get/save/delete/enable` (singular — acts on one
26
+ * schedule) alongside `knowledge.schedules.list` (plural — the collection) is
27
+ * the canonical example. Do not introduce this split defensively "for
28
+ * symmetry" on a family that only ever has one shape — `automation.schedules.*`
29
+ * has no singular sibling because nothing needs one yet.
30
+ *
31
+ * CONFORMANCE: see test/core-verbs-conformance.test.ts, which lints every id
32
+ * in OPERATOR_METHOD_IDS against this file: each verb tail must be in
33
+ * CORE_VERBS, in one of EXEMPT_VERB_CATEGORIES, or the test fails and names
34
+ * the offending id — a new ad hoc verb cannot land silently. BANNED_VERBS are
35
+ * asserted absent outright, so a retired verb can never come back under the
36
+ * same tail.
37
+ */
38
+ /**
39
+ * The canonical generic-lifecycle verb vocabulary. Every operator method
40
+ * whose action is a generic CRUD/lifecycle operation (not a bespoke,
41
+ * domain-specific action) must use one of these exact words as its id's final
42
+ * dotted segment.
43
+ */
44
+ export declare const CORE_VERBS: readonly ["list", "get", "search", "snapshot", "status", "create", "update", "delete", "upsert", "set", "enable", "disable", "close", "reopen", "cancel", "run", "retry", "invoke", "stream", "register"];
45
+ export type CoreVerb = typeof CORE_VERBS[number];
46
+ /**
47
+ * Verbs that were retired by a Wave-6 core-verb ruling and must never
48
+ * reappear as a method id's final dotted segment. A verb lands here (instead
49
+ * of just being deleted from history) so the conformance test keeps banning
50
+ * it even if someone re-adds it later without knowing why it was removed.
51
+ *
52
+ * - `patch` — retired in favor of `update` (automation.jobs.patch ->
53
+ * automation.jobs.update, routes.bindings.patch -> routes.bindings.update,
54
+ * watchers.patch -> watchers.update, all W6-C3). `update` is the one
55
+ * canonical partial-mutation verb; `patch` mirrored the HTTP-verb name
56
+ * (PATCH) instead of the operator-method vocabulary in these three places —
57
+ * the HTTP method on the descriptor is unaffected, only the id's verb tail
58
+ * changed.
59
+ * - `pause` / `resume` — retired as a byte-identical redundant lifecycle pair
60
+ * with `enable`/`disable` (automation.jobs.pause/resume -> deleted, W6-C3;
61
+ * same `{id, enabled}` output shape, same semantics). A caller-facing
62
+ * "pause"/"resume" user verb should map onto `disable`/`enable` at the wire.
63
+ */
64
+ export declare const BANNED_VERBS: readonly ["patch", "pause", "resume"];
65
+ export type BannedVerb = typeof BANNED_VERBS[number];
66
+ /**
67
+ * Domain-specific verbs that are NOT part of the generic lifecycle vocabulary
68
+ * but are legitimate, real operations — not a coherence bug. Grouped by
69
+ * category with a one-line reason each, rather than one entry per id, because
70
+ * the catalog has ~300 methods across a dozen unrelated domains (calendar,
71
+ * channels, home automation, knowledge, media, telemetry, voice, ...) and
72
+ * requiring an individual justification per verb would make the exemption
73
+ * list an unmaintainable copy of the catalog itself. The categorization is
74
+ * still a real constraint: a verb tail that matches none of CORE_VERBS and
75
+ * none of these categories' listed verbs fails the conformance test — a
76
+ * genuinely new ad hoc verb has to either fit an existing category, join
77
+ * CORE_VERBS with a decision record, or get its own category here.
78
+ */
79
+ export declare const EXEMPT_VERB_CATEGORIES: Readonly<Record<string, readonly string[]>>;
80
+ /** Flattened set of every exempt (non-core, non-banned) verb, for fast lookup. */
81
+ export declare const EXEMPT_VERBS: ReadonlySet<string>;
82
+ /**
83
+ * Classify a single operator-method id's verb tail (its final dotted
84
+ * segment) against the vocabulary.
85
+ */
86
+ export type VerbClassification = {
87
+ readonly kind: 'core';
88
+ readonly verb: CoreVerb;
89
+ } | {
90
+ readonly kind: 'exempt';
91
+ readonly verb: string;
92
+ readonly category: string;
93
+ } | {
94
+ readonly kind: 'banned';
95
+ readonly verb: BannedVerb;
96
+ } | {
97
+ readonly kind: 'unclassified';
98
+ readonly verb: string;
99
+ };
100
+ export declare function verbTailOf(methodId: string): string;
101
+ export declare function classifyVerb(methodId: string): VerbClassification;
102
+ //# sourceMappingURL=core-verbs.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"core-verbs.d.ts","sourceRoot":"","sources":["../src/core-verbs.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAoCG;AAEH;;;;;GAKG;AACH,eAAO,MAAM,UAAU,2MAyBb,CAAC;AAEX,MAAM,MAAM,QAAQ,GAAG,OAAO,UAAU,CAAC,MAAM,CAAC,CAAC;AAEjD;;;;;;;;;;;;;;;;;GAiBG;AACH,eAAO,MAAM,YAAY,uCAAwC,CAAC;AAElE,MAAM,MAAM,UAAU,GAAG,OAAO,YAAY,CAAC,MAAM,CAAC,CAAC;AAErD;;;;;;;;;;;;GAYG;AACH,eAAO,MAAM,sBAAsB,EAAE,QAAQ,CAAC,MAAM,CAAC,MAAM,EAAE,SAAS,MAAM,EAAE,CAAC,CAoErE,CAAC;AAEX,kFAAkF;AAClF,eAAO,MAAM,YAAY,EAAE,WAAW,CAAC,MAAM,CAE5C,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,kBAAkB,GAC1B;IAAE,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAA;CAAE,GAClD;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,QAAQ,EAAE,MAAM,CAAA;CAAE,GAC7E;IAAE,QAAQ,CAAC,IAAI,EAAE,QAAQ,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,UAAU,CAAA;CAAE,GACtD;IAAE,QAAQ,CAAC,IAAI,EAAE,cAAc,CAAC;IAAC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAA;CAAE,CAAC;AAE7D,wBAAgB,UAAU,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGnD;AAED,wBAAgB,YAAY,CAAC,QAAQ,EAAE,MAAM,GAAG,kBAAkB,CAcjE"}
@@ -0,0 +1,191 @@
1
+ /**
2
+ * core-verbs.ts — the canonical operator-method verb vocabulary (E8, W6-C3).
3
+ *
4
+ * WHY THIS EXISTS: OPERATOR_METHOD_IDS (generated/operator-method-ids.ts) is a
5
+ * flat list of dotted ids (`<namespace...>.<verb>`). Before this file, nothing
6
+ * enumerated or constrained the verb vocabulary — every method-catalog author
7
+ * picked whatever word felt right, which is how the wave-6 audit found three
8
+ * worst-class collisions on the word "schedule", a redundant lifecycle pair
9
+ * (`enable`/`disable` duplicated by `pause`/`resume`), and an update-verb split
10
+ * (`patch` vs `update`). This module is the forcing function that keeps that
11
+ * from recurring: CORE_VERBS is the closed vocabulary for generic lifecycle
12
+ * operations, BANNED_VERBS are verbs that were retired and must never
13
+ * reappear, and EXEMPT_VERB_CATEGORIES documents the (large, expected) set of
14
+ * domain-specific verbs that aren't generic CRUD/lifecycle words — things like
15
+ * `voice.stt`, `homeassistant.homeGraph.askHomeGraph`, or `telemetry.otlp.logs`
16
+ * are real, single-purpose operations, not a coherence bug.
17
+ *
18
+ * NAMESPACE RULE: a resource family name is the plural noun the family
19
+ * manages (`tasks`, `sessions`, `schedules`, `watchers`); verbs attach
20
+ * directly to it (`tasks.list`, `tasks.get`). The ONE exception is a
21
+ * documented, reusable pattern — not ad hoc: when a family already has both a
22
+ * per-item action surface AND a collection surface, and giving both the same
23
+ * plural name would be ambiguous about which one an action targets, the
24
+ * per-item family takes the SINGULAR form and the collection keeps the
25
+ * PLURAL. `knowledge.schedule.get/save/delete/enable` (singular — acts on one
26
+ * schedule) alongside `knowledge.schedules.list` (plural — the collection) is
27
+ * the canonical example. Do not introduce this split defensively "for
28
+ * symmetry" on a family that only ever has one shape — `automation.schedules.*`
29
+ * has no singular sibling because nothing needs one yet.
30
+ *
31
+ * CONFORMANCE: see test/core-verbs-conformance.test.ts, which lints every id
32
+ * in OPERATOR_METHOD_IDS against this file: each verb tail must be in
33
+ * CORE_VERBS, in one of EXEMPT_VERB_CATEGORIES, or the test fails and names
34
+ * the offending id — a new ad hoc verb cannot land silently. BANNED_VERBS are
35
+ * asserted absent outright, so a retired verb can never come back under the
36
+ * same tail.
37
+ */
38
+ /**
39
+ * The canonical generic-lifecycle verb vocabulary. Every operator method
40
+ * whose action is a generic CRUD/lifecycle operation (not a bespoke,
41
+ * domain-specific action) must use one of these exact words as its id's final
42
+ * dotted segment.
43
+ */
44
+ export const CORE_VERBS = [
45
+ // Collection / item reads
46
+ 'list',
47
+ 'get',
48
+ 'search',
49
+ 'snapshot',
50
+ 'status',
51
+ // Writes
52
+ 'create',
53
+ 'update',
54
+ 'delete',
55
+ 'upsert',
56
+ 'set',
57
+ // Lifecycle
58
+ 'enable',
59
+ 'disable',
60
+ 'close',
61
+ 'reopen',
62
+ 'cancel',
63
+ 'run',
64
+ 'retry',
65
+ // Cross-cutting / transport
66
+ 'invoke',
67
+ 'stream',
68
+ 'register',
69
+ ];
70
+ /**
71
+ * Verbs that were retired by a Wave-6 core-verb ruling and must never
72
+ * reappear as a method id's final dotted segment. A verb lands here (instead
73
+ * of just being deleted from history) so the conformance test keeps banning
74
+ * it even if someone re-adds it later without knowing why it was removed.
75
+ *
76
+ * - `patch` — retired in favor of `update` (automation.jobs.patch ->
77
+ * automation.jobs.update, routes.bindings.patch -> routes.bindings.update,
78
+ * watchers.patch -> watchers.update, all W6-C3). `update` is the one
79
+ * canonical partial-mutation verb; `patch` mirrored the HTTP-verb name
80
+ * (PATCH) instead of the operator-method vocabulary in these three places —
81
+ * the HTTP method on the descriptor is unaffected, only the id's verb tail
82
+ * changed.
83
+ * - `pause` / `resume` — retired as a byte-identical redundant lifecycle pair
84
+ * with `enable`/`disable` (automation.jobs.pause/resume -> deleted, W6-C3;
85
+ * same `{id, enabled}` output shape, same semantics). A caller-facing
86
+ * "pause"/"resume" user verb should map onto `disable`/`enable` at the wire.
87
+ */
88
+ export const BANNED_VERBS = ['patch', 'pause', 'resume'];
89
+ /**
90
+ * Domain-specific verbs that are NOT part of the generic lifecycle vocabulary
91
+ * but are legitimate, real operations — not a coherence bug. Grouped by
92
+ * category with a one-line reason each, rather than one entry per id, because
93
+ * the catalog has ~300 methods across a dozen unrelated domains (calendar,
94
+ * channels, home automation, knowledge, media, telemetry, voice, ...) and
95
+ * requiring an individual justification per verb would make the exemption
96
+ * list an unmaintainable copy of the catalog itself. The categorization is
97
+ * still a real constraint: a verb tail that matches none of CORE_VERBS and
98
+ * none of these categories' listed verbs fails the conformance test — a
99
+ * genuinely new ad hoc verb has to either fit an existing category, join
100
+ * CORE_VERBS with a decision record, or get its own category here.
101
+ */
102
+ export const EXEMPT_VERB_CATEGORIES = {
103
+ 'external-api-mirror': [
104
+ // Verbs that mirror an external vendor's own API vocabulary (Home
105
+ // Assistant's homeGraph.* actions, calendar ICS import/export). Renaming
106
+ // these to the core vocabulary would obscure the 1:1 mapping to the
107
+ // vendor operation they wrap.
108
+ 'askHomeGraph', 'browse', 'export', 'generateHomeGraphPacket', 'generateRoomPage',
109
+ 'import', 'ingestHomeGraphArtifact', 'ingestHomeGraphNote', 'ingestHomeGraphUrl',
110
+ 'linkHomeGraphKnowledge', 'listHomeGraphIssues', 'map', 'refreshDevicePassport',
111
+ 'reset', 'reviewHomeGraphFact', 'syncHomeGraph', 'unlinkHomeGraphKnowledge',
112
+ ],
113
+ 'media-and-voice-io': [
114
+ // Single-purpose media/voice operations named for what they produce, not
115
+ // a generic CRUD shape.
116
+ 'analyze', 'generate', 'transform', 'writeback', 'stt', 'tts', 'session',
117
+ ],
118
+ 'maintenance-and-indexing': [
119
+ // Index/derived-state maintenance actions (knowledge and home-graph
120
+ // reindexing, memory vector rebuilds) — a maintenance operation on
121
+ // derived state, not a CRUD action on the record itself.
122
+ 'reindex', 'rebuild',
123
+ ],
124
+ 'transport-and-protocol': [
125
+ // Endpoints whose name is a protocol/transport concept, not a resource
126
+ // verb (auth, OTLP ingestion, contract introspection, GraphQL execution).
127
+ 'login', 'current', 'contract', 'schema', 'execute', 'logs', 'metrics', 'traces', 'web',
128
+ ],
129
+ 'ingest-and-content': [
130
+ // Content-shaped verbs describing what is being brought in or produced,
131
+ // not a generic lifecycle action.
132
+ 'artifact', 'bookmarks', 'browserHistory', 'connector', 'url', 'urls', 'packet',
133
+ 'lint', 'materialize', 'render', 'query', 'ask', 'decide', 'review',
134
+ ],
135
+ 'approval-and-routing': [
136
+ // Domain verbs specific to the approvals/routing/session-target model.
137
+ 'approve', 'claim', 'deny', 'default', 'named', 'assign', 'resolve', 'authorize',
138
+ 'audit', 'edit', 'diff', 'restore', 'revoke', 'rotate', 'disconnect',
139
+ ],
140
+ 'session-and-work-lifecycle': [
141
+ // Session/task-graph-specific state transitions that are not the generic
142
+ // enable/disable/cancel vocabulary (they carry session semantics:
143
+ // steering a live turn, delivering an out-of-band input, etc).
144
+ 'detach', 'followUp', 'deliver', 'steer', 'reorder', 'clearCompleted', 'record',
145
+ 'evaluate', 'send', 'read', 'save',
146
+ ],
147
+ 'reporting-and-diagnostics': [
148
+ // Read-shaped diagnostic/reporting endpoints named for their specific
149
+ // report, not a generic "get"/"list".
150
+ 'doctor', 'stats', 'capacity', 'settings', 'catalog', 'reject', 'review-queue',
151
+ ],
152
+ 'process-control': [
153
+ // OS/service process lifecycle verbs (distinct domain from
154
+ // enable/disable, which toggle a *record's* activation state) plus
155
+ // reload (re-read a live config from disk — a process action, not a
156
+ // record lifecycle transition).
157
+ 'install', 'restart', 'start', 'stop', 'uninstall', 'open', 'reload',
158
+ ],
159
+ 'legacy-verb-aliases': [
160
+ // KNOWN, OUT-OF-SCOPE minor inconsistency (not one of Wave-6's ranked
161
+ // worst-class collisions): mcp.servers.remove means exactly what `delete`
162
+ // means everywhere else in the catalog. Flagged here rather than fixed,
163
+ // per the Wave-6 scope discipline of fixing only the ranked worst-class
164
+ // items (schedule/memory/tasks/session-orphan/sessions-visibility) —
165
+ // renaming every already-consistent-but-differently-worded single
166
+ // outlier is explicitly out of scope for this wave (W6-C3 risk #6). A
167
+ // future pass can fold this into `delete`.
168
+ 'remove',
169
+ ],
170
+ };
171
+ /** Flattened set of every exempt (non-core, non-banned) verb, for fast lookup. */
172
+ export const EXEMPT_VERBS = new Set(Object.values(EXEMPT_VERB_CATEGORIES).flat());
173
+ export function verbTailOf(methodId) {
174
+ const segments = methodId.split('.');
175
+ return segments[segments.length - 1] ?? methodId;
176
+ }
177
+ export function classifyVerb(methodId) {
178
+ const verb = verbTailOf(methodId);
179
+ if (BANNED_VERBS.includes(verb)) {
180
+ return { kind: 'banned', verb: verb };
181
+ }
182
+ if (CORE_VERBS.includes(verb)) {
183
+ return { kind: 'core', verb: verb };
184
+ }
185
+ for (const [category, verbs] of Object.entries(EXEMPT_VERB_CATEGORIES)) {
186
+ if (verbs.includes(verb)) {
187
+ return { kind: 'exempt', verb, category };
188
+ }
189
+ }
190
+ return { kind: 'unclassified', verb };
191
+ }