@vibgrate/cli 2026.613.1 → 2026.615.2

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/dist/cli.js CHANGED
@@ -6,7 +6,7 @@ import {
6
6
  pathExists,
7
7
  readJsonFile,
8
8
  writeTextFile
9
- } from "./chunk-UNIBRNYG.js";
9
+ } from "./chunk-HJ4D6B3B.js";
10
10
  import {
11
11
  computeRepoFingerprint,
12
12
  detectVcs,
@@ -16,10 +16,10 @@ import {
16
16
  resolveRepositoryName,
17
17
  runScan,
18
18
  writeDefaultConfig
19
- } from "./chunk-RPI2K62L.js";
19
+ } from "./chunk-BOGC5GYS.js";
20
20
  import {
21
21
  require_semver
22
- } from "./chunk-74ZJFYEM.js";
22
+ } from "./chunk-5IXVOEZN.js";
23
23
  import {
24
24
  parseExcludePatterns,
25
25
  pathExists as pathExists2
@@ -49,7 +49,7 @@ var initCommand = new Command("init").description("Initialize vibgrate in a proj
49
49
  console.log(chalk.green("\u2714") + ` Created ${chalk.bold("vibgrate.config.ts")}`);
50
50
  }
51
51
  if (opts.baseline) {
52
- const { runBaseline } = await import("./baseline-LTB3IFRV.js");
52
+ const { runBaseline } = await import("./baseline-WUHK4LQY.js");
53
53
  await runBaseline(rootDir);
54
54
  }
55
55
  console.log("");
@@ -1667,7 +1667,7 @@ var updateCommand = new Command6("update").description("Update vibgrate to the l
1667
1667
  console.error(chalk7.red("Could not reach the npm registry. Check your network connection."));
1668
1668
  process.exit(1);
1669
1669
  }
1670
- const semver2 = await import("./semver-JBJZTHUX.js");
1670
+ const semver2 = await import("./semver-2FJFIYVN.js");
1671
1671
  if (!semver2.gt(latest, VERSION)) {
1672
1672
  console.log(chalk7.green("\u2714") + ` You are on the latest version (${VERSION}).`);
1673
1673
  return;
@@ -1885,6 +1885,859 @@ import { existsSync } from "fs";
1885
1885
  import { spawn, spawnSync } from "child_process";
1886
1886
  import { Command as Command8 } from "commander";
1887
1887
  import chalk9 from "chalk";
1888
+
1889
+ // src/behavioural/derive.ts
1890
+ import { createHash as createHash2 } from "crypto";
1891
+
1892
+ // src/behavioural/hxl.ts
1893
+ import { createHash } from "crypto";
1894
+ var HxlTable = class {
1895
+ nodes = [];
1896
+ byKey = /* @__PURE__ */ new Map();
1897
+ intern(node) {
1898
+ const key = canonicalKey(node);
1899
+ const existing = this.byKey.get(key);
1900
+ if (existing !== void 0) return existing;
1901
+ const idx = this.nodes.length;
1902
+ this.nodes.push(node);
1903
+ this.byKey.set(key, idx);
1904
+ return idx;
1905
+ }
1906
+ /** Fraction of nodes that are faithfully represented (not Opaque). */
1907
+ fidelity() {
1908
+ if (this.nodes.length === 0) return 1;
1909
+ const opaque = this.nodes.filter((n) => n.k === "Opaque").length;
1910
+ return 1 - opaque / this.nodes.length;
1911
+ }
1912
+ };
1913
+ function canonicalKey(n) {
1914
+ switch (n.k) {
1915
+ case "Lit":
1916
+ return `L:${n.type}:${JSON.stringify(n.value)}`;
1917
+ case "Ref":
1918
+ return `R:${n.name}`;
1919
+ case "Member":
1920
+ return `M:${n.obj}.${n.name}`;
1921
+ case "Call":
1922
+ return `C:${n.callee}(${n.args.join(",")})`;
1923
+ case "Unary":
1924
+ return `U:${n.op}:${n.x}`;
1925
+ case "Binary":
1926
+ return `B:${n.op}:${n.l}:${n.r}`;
1927
+ case "Opaque":
1928
+ return `O:${n.raw}`;
1929
+ }
1930
+ }
1931
+ var BIN_OPS = {
1932
+ "==": "eq",
1933
+ "===": "eq",
1934
+ "!=": "ne",
1935
+ "!==": "ne",
1936
+ "<": "lt",
1937
+ "<=": "le",
1938
+ ">": "gt",
1939
+ ">=": "ge",
1940
+ "&&": "and",
1941
+ "||": "or",
1942
+ "+": "add",
1943
+ "-": "sub",
1944
+ "*": "mul",
1945
+ "/": "div",
1946
+ "%": "mod"
1947
+ };
1948
+ function tokenize(src) {
1949
+ const toks = [];
1950
+ let i = 0;
1951
+ const ops3 = ["===", "!=="];
1952
+ const ops2 = ["==", "!=", "<=", ">=", "&&", "||"];
1953
+ while (i < src.length) {
1954
+ const c = src[i];
1955
+ if (c === " " || c === " " || c === "\n" || c === "\r") {
1956
+ i++;
1957
+ continue;
1958
+ }
1959
+ if (src.startsWith("=>", i)) return null;
1960
+ const three = src.slice(i, i + 3);
1961
+ if (ops3.includes(three)) {
1962
+ toks.push({ t: "op", v: three });
1963
+ i += 3;
1964
+ continue;
1965
+ }
1966
+ const two = src.slice(i, i + 2);
1967
+ if (ops2.includes(two)) {
1968
+ toks.push({ t: "op", v: two });
1969
+ i += 2;
1970
+ continue;
1971
+ }
1972
+ if ("<>+-*/%".includes(c)) {
1973
+ toks.push({ t: "op", v: c });
1974
+ i++;
1975
+ continue;
1976
+ }
1977
+ if (c === "!") {
1978
+ toks.push({ t: "not", v: "!" });
1979
+ i++;
1980
+ continue;
1981
+ }
1982
+ if (c === "(" || c === ")" || c === "." || c === ",") {
1983
+ toks.push({ t: c, v: c });
1984
+ i++;
1985
+ continue;
1986
+ }
1987
+ if (c === '"' || c === "'") {
1988
+ let j = i + 1;
1989
+ let s = "";
1990
+ while (j < src.length && src[j] !== c) {
1991
+ if (src[j] === "\\") {
1992
+ s += src[j + 1] ?? "";
1993
+ j += 2;
1994
+ } else {
1995
+ s += src[j];
1996
+ j++;
1997
+ }
1998
+ }
1999
+ if (j >= src.length) return null;
2000
+ toks.push({ t: "str", v: s });
2001
+ i = j + 1;
2002
+ continue;
2003
+ }
2004
+ if (/[0-9]/.test(c)) {
2005
+ let j = i;
2006
+ while (j < src.length && /[0-9.]/.test(src[j])) j++;
2007
+ toks.push({ t: "num", v: src.slice(i, j) });
2008
+ i = j;
2009
+ continue;
2010
+ }
2011
+ if (/[A-Za-z_$]/.test(c)) {
2012
+ let j = i;
2013
+ while (j < src.length && /[A-Za-z0-9_$]/.test(src[j])) j++;
2014
+ toks.push({ t: "ident", v: src.slice(i, j) });
2015
+ i = j;
2016
+ continue;
2017
+ }
2018
+ return null;
2019
+ }
2020
+ return toks;
2021
+ }
2022
+ function parseGuard(src, table = new HxlTable()) {
2023
+ const toks = tokenize(src);
2024
+ if (!toks || toks.length === 0) return null;
2025
+ let p = 0;
2026
+ const peek = () => toks[p];
2027
+ const eat = () => toks[p++];
2028
+ function parseOr() {
2029
+ return parseBinLevel(["||"], parseAnd);
2030
+ }
2031
+ function parseAnd() {
2032
+ return parseBinLevel(["&&"], parseCmp);
2033
+ }
2034
+ function parseCmp() {
2035
+ const l = parseAdd();
2036
+ if (l === null) return null;
2037
+ const tk = peek();
2038
+ if (tk && tk.t === "op" && ["==", "===", "!=", "!==", "<", "<=", ">", ">="].includes(tk.v)) {
2039
+ eat();
2040
+ const r = parseAdd();
2041
+ if (r === null) return null;
2042
+ return table.intern({ k: "Binary", op: BIN_OPS[tk.v], l, r });
2043
+ }
2044
+ return l;
2045
+ }
2046
+ function parseAdd() {
2047
+ return parseBinLevel(["+", "-"], parseMul);
2048
+ }
2049
+ function parseMul() {
2050
+ return parseBinLevel(["*", "/", "%"], parseUnary);
2051
+ }
2052
+ function parseBinLevel(ops, next) {
2053
+ let l = next();
2054
+ if (l === null) return null;
2055
+ while (peek() && peek().t === "op" && ops.includes(peek().v)) {
2056
+ const op = eat().v;
2057
+ const r = next();
2058
+ if (r === null) return null;
2059
+ l = table.intern({ k: "Binary", op: BIN_OPS[op], l, r });
2060
+ }
2061
+ return l;
2062
+ }
2063
+ function parseUnary() {
2064
+ if (peek() && peek().t === "not") {
2065
+ eat();
2066
+ const x = parseUnary();
2067
+ if (x === null) return null;
2068
+ return table.intern({ k: "Unary", op: "not", x });
2069
+ }
2070
+ return parsePostfix();
2071
+ }
2072
+ function parsePostfix() {
2073
+ let node = parsePrimary();
2074
+ if (node === null) return null;
2075
+ for (; ; ) {
2076
+ const tk = peek();
2077
+ if (tk && tk.t === ".") {
2078
+ eat();
2079
+ const id = eat();
2080
+ if (!id || id.t !== "ident") return null;
2081
+ node = table.intern({ k: "Member", obj: node, name: id.v });
2082
+ continue;
2083
+ }
2084
+ if (tk && tk.t === "(") {
2085
+ eat();
2086
+ const args = [];
2087
+ if (peek() && peek().t !== ")") {
2088
+ for (; ; ) {
2089
+ const a = parseOr();
2090
+ if (a === null) return null;
2091
+ args.push(a);
2092
+ if (peek() && peek().t === ",") {
2093
+ eat();
2094
+ continue;
2095
+ }
2096
+ break;
2097
+ }
2098
+ }
2099
+ if (!peek() || eat().t !== ")") return null;
2100
+ node = table.intern({ k: "Call", callee: node, args });
2101
+ continue;
2102
+ }
2103
+ break;
2104
+ }
2105
+ return node;
2106
+ }
2107
+ function parsePrimary() {
2108
+ const tk = peek();
2109
+ if (!tk) return null;
2110
+ if (tk.t === "num") {
2111
+ eat();
2112
+ return table.intern({ k: "Lit", value: Number(tk.v), type: tk.v.includes(".") ? "float" : "int" });
2113
+ }
2114
+ if (tk.t === "str") {
2115
+ eat();
2116
+ return table.intern({ k: "Lit", value: tk.v, type: "string" });
2117
+ }
2118
+ if (tk.t === "ident") {
2119
+ if (tk.v === "true" || tk.v === "false") {
2120
+ eat();
2121
+ return table.intern({ k: "Lit", value: tk.v === "true", type: "bool" });
2122
+ }
2123
+ if (tk.v === "null") {
2124
+ eat();
2125
+ return table.intern({ k: "Lit", value: null, type: "null" });
2126
+ }
2127
+ eat();
2128
+ return table.intern({ k: "Ref", name: tk.v });
2129
+ }
2130
+ if (tk.t === "(") {
2131
+ eat();
2132
+ const e = parseOr();
2133
+ if (e === null) return null;
2134
+ if (!peek() || eat().t !== ")") return null;
2135
+ return e;
2136
+ }
2137
+ return null;
2138
+ }
2139
+ const root = parseOr();
2140
+ if (root === null || p !== toks.length) return null;
2141
+ return { table, root };
2142
+ }
2143
+ function lowerGuard(src) {
2144
+ const parsed = parseGuard(src.trim());
2145
+ if (parsed) return parsed;
2146
+ const table = new HxlTable();
2147
+ const root = table.intern({ k: "Opaque", raw: src.trim() });
2148
+ return { table, root };
2149
+ }
2150
+ function renderNode(table, idx) {
2151
+ const n = table.nodes[idx];
2152
+ switch (n.k) {
2153
+ case "Lit":
2154
+ return n.type === "string" ? JSON.stringify(n.value) : String(n.value);
2155
+ case "Ref":
2156
+ return n.name;
2157
+ case "Member":
2158
+ return `${renderNode(table, n.obj)}.${n.name}`;
2159
+ case "Call":
2160
+ return `${renderNode(table, n.callee)}(${n.args.map((a) => renderNode(table, a)).join(", ")})`;
2161
+ case "Unary":
2162
+ return n.op === "not" ? `not ${renderNode(table, n.x)}` : `-${renderNode(table, n.x)}`;
2163
+ case "Binary":
2164
+ return `${renderNode(table, n.l)} ${n.op} ${renderNode(table, n.r)}`;
2165
+ case "Opaque":
2166
+ return `\xAB${n.raw}\xBB`;
2167
+ }
2168
+ }
2169
+
2170
+ // src/behavioural/derive.ts
2171
+ var BEHAVIOUR_BEARING_TYPES = /* @__PURE__ */ new Set([
2172
+ "RouteDeclared",
2173
+ "DataAccessOperation",
2174
+ "EventEmitted",
2175
+ "EventConsumed",
2176
+ "ExternalServiceCall",
2177
+ "CicsTransactionDeclared"
2178
+ ]);
2179
+ function detokenize(value, refs) {
2180
+ if (typeof value === "string") {
2181
+ if (!value.startsWith("@@")) return value;
2182
+ for (const [id, full] of refs) {
2183
+ if (value === id) return full;
2184
+ if (value.startsWith(id + "/")) return full + value.slice(id.length);
2185
+ }
2186
+ return value;
2187
+ }
2188
+ if (Array.isArray(value)) return value.map((v) => detokenize(v, refs));
2189
+ if (value && typeof value === "object") {
2190
+ const out = {};
2191
+ for (const [k, v] of Object.entries(value)) out[k] = detokenize(v, refs);
2192
+ return out;
2193
+ }
2194
+ return value;
2195
+ }
2196
+ function decompressFacts(lines) {
2197
+ const models = /* @__PURE__ */ new Map();
2198
+ const refs = /* @__PURE__ */ new Map();
2199
+ const parsed = [];
2200
+ for (const line of lines) {
2201
+ const trimmed = line.trim();
2202
+ if (!trimmed) continue;
2203
+ let obj;
2204
+ try {
2205
+ obj = JSON.parse(trimmed);
2206
+ } catch {
2207
+ continue;
2208
+ }
2209
+ if (obj.factType === "Models") {
2210
+ const list = obj.payload?.models ?? [];
2211
+ for (const m of list) models.set(m.id, m);
2212
+ continue;
2213
+ }
2214
+ if (obj.factType === "References") {
2215
+ const list = obj.payload?.references ?? [];
2216
+ for (const r of list) refs.set(r.id, r.value);
2217
+ continue;
2218
+ }
2219
+ parsed.push(obj);
2220
+ }
2221
+ const out = [];
2222
+ for (const obj of parsed) {
2223
+ const payload = detokenize(obj.payload, refs);
2224
+ if (typeof obj.m === "string") {
2225
+ const model = models.get(obj.m);
2226
+ if (!model) continue;
2227
+ out.push({
2228
+ factId: String(obj.factId),
2229
+ factType: model.factType,
2230
+ language: model.language,
2231
+ scanner: model.scanner,
2232
+ scannerVersion: model.scannerVersion,
2233
+ emittedAt: "",
2234
+ payload
2235
+ });
2236
+ } else if (typeof obj.factType === "string" && typeof obj.factId === "string") {
2237
+ out.push({
2238
+ factId: obj.factId,
2239
+ factType: obj.factType,
2240
+ language: String(obj.language ?? ""),
2241
+ scanner: String(obj.scanner ?? ""),
2242
+ scannerVersion: String(obj.scannerVersion ?? ""),
2243
+ emittedAt: String(obj.emittedAt ?? ""),
2244
+ payload
2245
+ });
2246
+ }
2247
+ }
2248
+ return out;
2249
+ }
2250
+ var str = (v) => typeof v === "string" && v ? v : void 0;
2251
+ var pick = (p, ...keys) => {
2252
+ for (const k of keys) {
2253
+ const s = str(p[k]);
2254
+ if (s !== void 0) return s;
2255
+ }
2256
+ return void 0;
2257
+ };
2258
+ function buildIndex(facts) {
2259
+ const idx = {
2260
+ byType: /* @__PURE__ */ new Map(),
2261
+ entityById: /* @__PURE__ */ new Map(),
2262
+ fieldsByEntity: /* @__PURE__ */ new Map(),
2263
+ paramsByRoute: /* @__PURE__ */ new Map(),
2264
+ daoByHandler: /* @__PURE__ */ new Map(),
2265
+ entityIdByName: /* @__PURE__ */ new Map()
2266
+ };
2267
+ for (const f of facts) {
2268
+ (idx.byType.get(f.factType) ?? idx.byType.set(f.factType, []).get(f.factType)).push(f);
2269
+ const p = f.payload;
2270
+ switch (f.factType) {
2271
+ case "EntityDeclared": {
2272
+ const id = pick(p, "entityId");
2273
+ const name = pick(p, "name", "shortName");
2274
+ if (id) idx.entityById.set(id, f);
2275
+ if (id && name) idx.entityIdByName.set(name.toLowerCase(), id);
2276
+ break;
2277
+ }
2278
+ case "FieldDeclared": {
2279
+ const id = pick(p, "entityId");
2280
+ if (id) (idx.fieldsByEntity.get(id) ?? idx.fieldsByEntity.set(id, []).get(id)).push(f);
2281
+ break;
2282
+ }
2283
+ case "ParameterBinding": {
2284
+ const rid = pick(p, "routeId");
2285
+ if (rid) (idx.paramsByRoute.get(rid) ?? idx.paramsByRoute.set(rid, []).get(rid)).push(f);
2286
+ break;
2287
+ }
2288
+ case "DataAccessOperation": {
2289
+ const caller = pick(p, "callerSymbol");
2290
+ if (caller) idx.daoByHandler.set(caller, f);
2291
+ break;
2292
+ }
2293
+ }
2294
+ }
2295
+ return idx;
2296
+ }
2297
+ function canonicalize(value) {
2298
+ if (value === null || typeof value !== "object") return JSON.stringify(value);
2299
+ if (Array.isArray(value)) return "[" + value.map(canonicalize).join(",") + "]";
2300
+ const obj = value;
2301
+ const keys = Object.keys(obj).sort();
2302
+ return "{" + keys.map((k) => JSON.stringify(k) + ":" + canonicalize(obj[k])).join(",") + "}";
2303
+ }
2304
+ function sha16(seed) {
2305
+ return createHash2("sha256").update(seed).digest("hex").slice(0, 16);
2306
+ }
2307
+ function factId(factType, seed) {
2308
+ return `hcs:${factType}:${sha16(seed)}`;
2309
+ }
2310
+ function makeAssertion(kind, subjectFactIds, structuredPredicate, predicate, strength, label, surfaceRefs, expr) {
2311
+ const subjects = [...new Set(subjectFactIds)].sort();
2312
+ const assertionId = `ba:${kind}:${sha16(canonicalize({ kind, subjectFactIds: subjects, predicate: structuredPredicate }))}`;
2313
+ const payload = { assertionId, kind, subjectFactIds: subjects, predicate, derivedBy: "static", corpusRef: null, strength, label };
2314
+ if (expr) payload.expr = expr;
2315
+ return { payload, surfaceRefs };
2316
+ }
2317
+ function statusForException(ex) {
2318
+ const e = ex.toLowerCase();
2319
+ if (e.includes("notfound")) return 404;
2320
+ if (e.includes("unauthor") || e.includes("forbidden")) return 403;
2321
+ if (e.includes("unauthenticated")) return 401;
2322
+ if (e.includes("validation") || e.includes("argument") || e.includes("badrequest")) return 400;
2323
+ if (e.includes("conflict")) return 409;
2324
+ return 500;
2325
+ }
2326
+ var STATIC_CAP = 0.85;
2327
+ var INTEGRATION_STRENGTH = 0.55;
2328
+ var EVENT_STRENGTH = 0.5;
2329
+ function contractStrength(opts) {
2330
+ let s = 0.35;
2331
+ if (opts.hasParams) s += 0.1;
2332
+ if (opts.hasResponseEntity) s += 0.2;
2333
+ if (opts.hasErrorBranches) s += 0.15;
2334
+ return Math.min(STATIC_CAP, s);
2335
+ }
2336
+ function invariantStrength(fidelity) {
2337
+ return Math.min(STATIC_CAP, 0.5 + 0.3 * fidelity);
2338
+ }
2339
+ function synthesizeApiContracts(idx) {
2340
+ const routes = idx.byType.get("RouteDeclared") ?? [];
2341
+ const out = [];
2342
+ for (const route of routes) {
2343
+ const p = route.payload;
2344
+ const method = pick(p, "method", "httpMethod") ?? "ANY";
2345
+ const template = pick(p, "template", "routePath") ?? "";
2346
+ const routeId = pick(p, "routeId") ?? `${method}:${template}`;
2347
+ const handler = pick(p, "handlerSymbol", "handlerName");
2348
+ const subjects = [route.factId];
2349
+ const surfaceRefs = [route.factId];
2350
+ const params = (idx.paramsByRoute.get(routeId) ?? []).map((pb) => {
2351
+ subjects.push(pb.factId);
2352
+ return {
2353
+ name: pick(pb.payload, "paramName", "parameterName") ?? "",
2354
+ source: pick(pb.payload, "source", "bindingSource") ?? "unknown",
2355
+ type: pick(pb.payload, "typeName", "parameterType") ?? "unknown",
2356
+ required: pb.payload.required === true || pb.payload.isRequired === true
2357
+ };
2358
+ });
2359
+ let entityName;
2360
+ let fields = [];
2361
+ const errors = [];
2362
+ const dao = handler ? idx.daoByHandler.get(handler) : void 0;
2363
+ if (dao) {
2364
+ subjects.push(dao.factId);
2365
+ surfaceRefs.push(dao.factId);
2366
+ entityName = pick(dao.payload, "targetEntity");
2367
+ const steps = Array.isArray(dao.payload.steps) ? dao.payload.steps : [];
2368
+ for (const stepEntry of steps) {
2369
+ const kind = pick(stepEntry, "stepKind", "kind");
2370
+ const throwsOn = pick(stepEntry, "throwsOnFail");
2371
+ if (kind === "guard" && throwsOn) errors.push({ status: statusForException(throwsOn), when: throwsOn });
2372
+ }
2373
+ }
2374
+ if (entityName) {
2375
+ const eid = idx.entityIdByName.get(entityName.toLowerCase());
2376
+ const entity = eid ? idx.entityById.get(eid) : void 0;
2377
+ if (entity) subjects.push(entity.factId);
2378
+ if (eid) {
2379
+ fields = (idx.fieldsByEntity.get(eid) ?? []).map((fd) => ({
2380
+ name: pick(fd.payload, "fieldName") ?? "",
2381
+ type: pick(fd.payload, "typeName", "hcsType") ?? "unknown"
2382
+ }));
2383
+ }
2384
+ }
2385
+ const structured = { method, template, params, response: { status: 200, entity: entityName ?? null, fields }, errors };
2386
+ const predicate = renderContract(method, template, params, entityName, fields, errors);
2387
+ const label = `${method} ${template} contract`;
2388
+ const strength = contractStrength({ hasParams: params.length > 0, hasResponseEntity: !!entityName, hasErrorBranches: errors.length > 0 });
2389
+ out.push(makeAssertion("contract", subjects, structured, predicate, strength, label, surfaceRefs));
2390
+ }
2391
+ return out;
2392
+ }
2393
+ function renderContract(method, template, params, entity, fields, errors) {
2394
+ const paramStr = params.map((p) => `${p.name}:${p.type}`).join(", ");
2395
+ const fieldStr = fields.map((f) => `${f.name}:${f.type}`).join(", ");
2396
+ const ok = entity ? `200 ${entity}{ ${fieldStr} }` : "200";
2397
+ const errStr = errors.map((e) => `${e.status} when ${e.when}`).join(" | ");
2398
+ return `${method} ${template}(${paramStr}) -> ${ok}${errStr ? " | " + errStr : ""}`;
2399
+ }
2400
+ function synthesizeIntegrationContracts(idx) {
2401
+ const calls = idx.byType.get("ExternalServiceCall") ?? [];
2402
+ const out = [];
2403
+ for (const call of calls) {
2404
+ const p = call.payload;
2405
+ const target = pick(p, "targetUrl", "serviceRef", "urlOrPattern") ?? "unknown";
2406
+ const method = pick(p, "httpMethod", "method") ?? "ANY";
2407
+ const client = pick(p, "clientKind", "protocol") ?? "unknown";
2408
+ const structured = { type: "integration", method, target, client };
2409
+ const predicate = `calls ${method} ${target} via ${client}`;
2410
+ out.push(makeAssertion("contract", [call.factId], structured, predicate, INTEGRATION_STRENGTH, `outbound ${method} ${target}`, [call.factId]));
2411
+ }
2412
+ return out;
2413
+ }
2414
+ function synthesizeEventContracts(idx) {
2415
+ const out = [];
2416
+ for (const ev of idx.byType.get("EventEmitted") ?? []) {
2417
+ const p = ev.payload;
2418
+ const name = pick(p, "eventName") ?? "unknown";
2419
+ const kind = pick(p, "emitterKind") ?? "event";
2420
+ const payloadType = pick(p, "payloadType") ?? null;
2421
+ const structured = { type: "event", direction: "emit", name, kind, payloadType };
2422
+ const predicate = `emits ${name}${payloadType ? ` (${payloadType})` : ""} via ${kind}`;
2423
+ out.push(makeAssertion("contract", [ev.factId], structured, predicate, EVENT_STRENGTH, `emits ${name}`, [ev.factId]));
2424
+ }
2425
+ for (const ev of idx.byType.get("EventConsumed") ?? []) {
2426
+ const p = ev.payload;
2427
+ const name = pick(p, "eventName") ?? "unknown";
2428
+ const kind = pick(p, "consumerKind") ?? "handler";
2429
+ const payloadType = pick(p, "payloadType") ?? null;
2430
+ const structured = { type: "event", direction: "consume", name, kind, payloadType };
2431
+ const predicate = `consumes ${name}${payloadType ? ` (${payloadType})` : ""} via ${kind}`;
2432
+ out.push(makeAssertion("contract", [ev.factId], structured, predicate, EVENT_STRENGTH, `consumes ${name}`, [ev.factId]));
2433
+ }
2434
+ return out;
2435
+ }
2436
+ function validationRuleToExpr(field, fieldType, rule, arg) {
2437
+ const t = new HxlTable();
2438
+ const ref = t.intern({ k: "Ref", name: field });
2439
+ const len = () => t.intern({ k: "Member", obj: ref, name: "length" });
2440
+ const num = (s) => {
2441
+ const n = Number(s);
2442
+ return t.intern({ k: "Lit", value: Number.isFinite(n) ? n : 0, type: Number.isInteger(n) ? "int" : "float" });
2443
+ };
2444
+ const zero = () => t.intern({ k: "Lit", value: 0, type: "int" });
2445
+ const isString = fieldType === "string";
2446
+ const bin = (op, l, r) => t.intern({ k: "Binary", op, l, r });
2447
+ const format = (name) => t.intern({ k: "Call", callee: t.intern({ k: "Member", obj: ref, name: "matches" }), args: [t.intern({ k: "Lit", value: name, type: "string" })] });
2448
+ let root;
2449
+ switch (rule) {
2450
+ case "min":
2451
+ root = bin("ge", isString ? len() : ref, num(arg));
2452
+ break;
2453
+ case "max":
2454
+ root = bin("le", isString ? len() : ref, num(arg));
2455
+ break;
2456
+ case "gt":
2457
+ root = bin("gt", ref, num(arg));
2458
+ break;
2459
+ case "gte":
2460
+ root = bin("ge", ref, num(arg));
2461
+ break;
2462
+ case "lt":
2463
+ root = bin("lt", ref, num(arg));
2464
+ break;
2465
+ case "lte":
2466
+ root = bin("le", ref, num(arg));
2467
+ break;
2468
+ case "positive":
2469
+ root = bin("gt", ref, zero());
2470
+ break;
2471
+ case "nonnegative":
2472
+ root = bin("ge", ref, zero());
2473
+ break;
2474
+ case "length":
2475
+ root = bin("eq", len(), num(arg));
2476
+ break;
2477
+ case "nonempty":
2478
+ root = bin("gt", len(), zero());
2479
+ break;
2480
+ case "email":
2481
+ case "url":
2482
+ case "uuid":
2483
+ case "datetime":
2484
+ root = format(rule);
2485
+ break;
2486
+ case "regex":
2487
+ root = format(arg ?? "regex");
2488
+ break;
2489
+ default:
2490
+ return null;
2491
+ }
2492
+ return { table: t, root };
2493
+ }
2494
+ function schemaCandidates(typeName) {
2495
+ const base = typeName.replace(/<.*>/, "").replace(/\[\]$/, "").trim();
2496
+ const stripped = base.replace(/(Dto|Input|Request|Payload|Body)$/i, "");
2497
+ return [base, stripped, `${stripped}Schema`, `${base}Schema`];
2498
+ }
2499
+ function buildSchemaRouteLinks(idx) {
2500
+ const links = /* @__PURE__ */ new Map();
2501
+ const schemaNames = /* @__PURE__ */ new Set();
2502
+ for (const vr of idx.byType.get("ValidationRuleObserved") ?? []) {
2503
+ const s = pick(vr.payload, "schemaName");
2504
+ if (s) schemaNames.add(s);
2505
+ }
2506
+ if (schemaNames.size === 0) return links;
2507
+ for (const route of idx.byType.get("RouteDeclared") ?? []) {
2508
+ const rid = pick(route.payload, "routeId") ?? "";
2509
+ const body = (idx.paramsByRoute.get(rid) ?? []).find((p) => pick(p.payload, "source", "bindingSource") === "body");
2510
+ if (!body) continue;
2511
+ const ptype = pick(body.payload, "typeName", "parameterType");
2512
+ if (!ptype || ptype.startsWith("Record") || ptype === "any" || ptype === "unknown") continue;
2513
+ const cands = schemaCandidates(ptype);
2514
+ for (const sn of schemaNames) {
2515
+ const snBase = sn.replace(/Schema\d*$/, "").replace(/\d+$/, "");
2516
+ if (cands.includes(sn) || cands.includes(snBase) || cands.includes(`${snBase}Schema`)) {
2517
+ links.set(sn, route.factId);
2518
+ break;
2519
+ }
2520
+ }
2521
+ }
2522
+ return links;
2523
+ }
2524
+ function synthesizeValidationInvariants(idx, schemaRoute) {
2525
+ const out = [];
2526
+ for (const vr of idx.byType.get("ValidationRuleObserved") ?? []) {
2527
+ const p = vr.payload;
2528
+ const field = pick(p, "fieldName") ?? "field";
2529
+ const fieldType = pick(p, "fieldType") ?? "unknown";
2530
+ const rule = pick(p, "rule") ?? "";
2531
+ const arg = pick(p, "arg") ?? null;
2532
+ const schema = pick(p, "schemaName") ?? "";
2533
+ const built = validationRuleToExpr(`${schema}.${field}`, fieldType, rule, arg);
2534
+ if (!built) continue;
2535
+ const rendered = renderNode(built.table, built.root);
2536
+ const structured = { type: "validation", hxl: built.table.nodes, root: built.root };
2537
+ const routeRef = schemaRoute.get(schema);
2538
+ out.push(makeAssertion("invariant", [vr.factId], structured, rendered, invariantStrength(built.table.fidelity()), `rule ${rendered}`, routeRef ? [routeRef] : [], { nodes: built.table.nodes, root: built.root }));
2539
+ }
2540
+ return out;
2541
+ }
2542
+ function buildRouteIntervals(idx) {
2543
+ const map = /* @__PURE__ */ new Map();
2544
+ for (const route of idx.byType.get("RouteDeclared") ?? []) {
2545
+ const file = pick(route.payload, "filePath");
2546
+ const start = route.payload.handlerStartLine;
2547
+ const end = route.payload.handlerEndLine;
2548
+ if (!file || typeof start !== "number" || typeof end !== "number") continue;
2549
+ (map.get(file) ?? map.set(file, []).get(file)).push({ start, end, factId: route.factId });
2550
+ }
2551
+ return map;
2552
+ }
2553
+ function routeAt(intervals, file, line) {
2554
+ if (!file || typeof line !== "number") return void 0;
2555
+ for (const iv of intervals.get(file) ?? []) if (line >= iv.start && line <= iv.end) return iv.factId;
2556
+ return void 0;
2557
+ }
2558
+ function synthesizeMethodLogicInvariants(idx) {
2559
+ const out = [];
2560
+ const surfaceByContainer = /* @__PURE__ */ new Map();
2561
+ for (const dao of idx.byType.get("DataAccessOperation") ?? []) {
2562
+ const c = pick(dao.payload, "callerSymbol");
2563
+ if (c) surfaceByContainer.set(c, dao.factId);
2564
+ }
2565
+ for (const route of idx.byType.get("RouteDeclared") ?? []) {
2566
+ const h = pick(route.payload, "handlerSymbol", "handlerName");
2567
+ if (h) surfaceByContainer.set(h, route.factId);
2568
+ }
2569
+ const callsFrom = /* @__PURE__ */ new Map();
2570
+ for (const co of idx.byType.get("CallObserved") ?? []) {
2571
+ const caller = pick(co.payload, "callerId", "callerSymbol");
2572
+ const callee = pick(co.payload, "calleeId", "calleeSymbol");
2573
+ if (!caller || !callee) continue;
2574
+ const short = callee.split(".").pop();
2575
+ (callsFrom.get(caller) ?? callsFrom.set(caller, /* @__PURE__ */ new Set()).get(caller)).add(short);
2576
+ }
2577
+ const routeIntervals = buildRouteIntervals(idx);
2578
+ for (const m of idx.byType.get("MethodLogicObserved") ?? []) {
2579
+ const expr = pick(m.payload, "expression");
2580
+ if (!expr) continue;
2581
+ const container = pick(m.payload, "containerSymbol") ?? "<m>";
2582
+ const kind = pick(m.payload, "kind") ?? "guard";
2583
+ const { table, root } = lowerGuard(expr);
2584
+ if (table.fidelity() === 0) continue;
2585
+ const rendered = renderNode(table, root);
2586
+ let surfaceRef = surfaceByContainer.get(container);
2587
+ if (!surfaceRef) {
2588
+ const shortName = container.split(".").pop();
2589
+ for (const [cont, fid] of surfaceByContainer) {
2590
+ if (callsFrom.get(cont)?.has(shortName)) {
2591
+ surfaceRef = fid;
2592
+ break;
2593
+ }
2594
+ }
2595
+ }
2596
+ if (!surfaceRef) surfaceRef = routeAt(routeIntervals, pick(m.payload, "filePath"), m.payload.line);
2597
+ const structured = { type: "methodlogic", kind, hxl: table.nodes, root };
2598
+ out.push(makeAssertion("invariant", [m.factId], structured, rendered, invariantStrength(table.fidelity()), `${kind} ${rendered}`, surfaceRef ? [surfaceRef] : [], { nodes: table.nodes, root }));
2599
+ }
2600
+ return out;
2601
+ }
2602
+ function synthesizeEntityConstraints(idx) {
2603
+ const out = [];
2604
+ for (const fd of idx.byType.get("FieldDeclared") ?? []) {
2605
+ const field = pick(fd.payload, "fieldName");
2606
+ if (!field) continue;
2607
+ const base = `${pick(fd.payload, "entityId") ?? ""}.${field}`;
2608
+ if (fd.payload.isNullable === false) {
2609
+ const t = new HxlTable();
2610
+ const root = t.intern({ k: "Binary", op: "ne", l: t.intern({ k: "Ref", name: base }), r: t.intern({ k: "Lit", value: null, type: "null" }) });
2611
+ out.push(makeAssertion("invariant", [fd.factId], { type: "entity-constraint", hxl: t.nodes, root }, renderNode(t, root), invariantStrength(1), `nonnull ${base}`, [], { nodes: t.nodes, root }));
2612
+ }
2613
+ if (typeof fd.payload.maxLength === "number") {
2614
+ const t = new HxlTable();
2615
+ const ref = t.intern({ k: "Ref", name: base });
2616
+ const root = t.intern({ k: "Binary", op: "le", l: t.intern({ k: "Member", obj: ref, name: "length" }), r: t.intern({ k: "Lit", value: fd.payload.maxLength, type: "int" }) });
2617
+ out.push(makeAssertion("invariant", [fd.factId], { type: "entity-constraint", hxl: t.nodes, root }, renderNode(t, root), invariantStrength(1), `maxlen ${base}`, [], { nodes: t.nodes, root }));
2618
+ }
2619
+ }
2620
+ return out;
2621
+ }
2622
+ function synthesizeDataOpContracts(idx) {
2623
+ const out = [];
2624
+ for (const dao of idx.byType.get("DataAccessOperation") ?? []) {
2625
+ const p = dao.payload;
2626
+ const kind = pick(p, "semanticKind") ?? "unknown";
2627
+ const entity = pick(p, "targetEntity");
2628
+ const returnType = pick(p, "returnType");
2629
+ const steps = Array.isArray(p.steps) ? p.steps : [];
2630
+ const collection = p.returnsCollection === true;
2631
+ let strength = 0.4;
2632
+ if (entity) strength += 0.15;
2633
+ if (steps.length > 0) strength += 0.1;
2634
+ if (returnType) strength += 0.1;
2635
+ strength = Math.min(STATIC_CAP, strength);
2636
+ const structured = { type: "dataop", kind, entity: entity ?? null, returnType: returnType ?? null, collection };
2637
+ const predicate = `${kind} ${entity ?? "?"}${collection ? "[]" : ""}`;
2638
+ out.push(makeAssertion("contract", [dao.factId], structured, predicate, strength, `data ${predicate}`, [dao.factId]));
2639
+ }
2640
+ return out;
2641
+ }
2642
+ function synthesizeInvariants(idx) {
2643
+ const out = [];
2644
+ for (const dao of idx.byType.get("DataAccessOperation") ?? []) {
2645
+ const steps = Array.isArray(dao.payload.steps) ? dao.payload.steps : [];
2646
+ for (const step of steps) {
2647
+ const expr = pick(step, "predicateExpression");
2648
+ if (!expr) continue;
2649
+ const { table, root } = lowerGuard(expr);
2650
+ const fidelity = table.fidelity();
2651
+ if (fidelity === 0) continue;
2652
+ const rendered = renderNode(table, root);
2653
+ const structured = { type: "invariant", hxl: table.nodes, root };
2654
+ out.push(makeAssertion("invariant", [dao.factId], structured, rendered, invariantStrength(fidelity), `invariant ${rendered}`, [dao.factId], { nodes: table.nodes, root }));
2655
+ }
2656
+ }
2657
+ return out;
2658
+ }
2659
+ function deriveBehaviouralFacts(facts, opts) {
2660
+ const emittedAt = opts.emittedAt ?? (/* @__PURE__ */ new Date()).toISOString();
2661
+ const idx = buildIndex(facts);
2662
+ const derived = [
2663
+ ...synthesizeApiContracts(idx),
2664
+ ...synthesizeIntegrationContracts(idx),
2665
+ ...synthesizeEventContracts(idx),
2666
+ ...synthesizeDataOpContracts(idx),
2667
+ ...synthesizeInvariants(idx),
2668
+ ...synthesizeValidationInvariants(idx, buildSchemaRouteLinks(idx)),
2669
+ ...synthesizeMethodLogicInvariants(idx),
2670
+ ...synthesizeEntityConstraints(idx)
2671
+ ];
2672
+ const seenAssertion = /* @__PURE__ */ new Set();
2673
+ const unique = [];
2674
+ for (const d of derived) {
2675
+ if (seenAssertion.has(d.payload.assertionId)) continue;
2676
+ seenAssertion.add(d.payload.assertionId);
2677
+ unique.push(d);
2678
+ }
2679
+ unique.sort((a, b) => a.payload.assertionId.localeCompare(b.payload.assertionId));
2680
+ const envelopes = [];
2681
+ const assertionFactIds = [];
2682
+ const byKind = {};
2683
+ const assertedSurface = /* @__PURE__ */ new Map();
2684
+ for (const d of unique) {
2685
+ const fid = factId("BehaviouralAssertion", d.payload.assertionId);
2686
+ assertionFactIds.push(fid);
2687
+ byKind[d.payload.kind] = (byKind[d.payload.kind] ?? 0) + 1;
2688
+ for (const ref of d.surfaceRefs) {
2689
+ assertedSurface.set(ref, Math.max(assertedSurface.get(ref) ?? 0, d.payload.strength));
2690
+ }
2691
+ envelopes.push({
2692
+ factId: fid,
2693
+ factType: "BehaviouralAssertion",
2694
+ language: "multi",
2695
+ scanner: "hcs-behavioural",
2696
+ scannerVersion: "0.3.0",
2697
+ emittedAt,
2698
+ payload: d.payload
2699
+ });
2700
+ }
2701
+ const surfaceFactIds = [...new Set(facts.filter((f) => BEHAVIOUR_BEARING_TYPES.has(f.factType)).map((f) => f.factId))];
2702
+ const surfaceFacts = surfaceFactIds.length;
2703
+ const assertedFacts = surfaceFactIds.filter((id) => assertedSurface.has(id)).length;
2704
+ const strengthSum = surfaceFactIds.reduce((acc, id) => acc + (assertedSurface.get(id) ?? 0), 0);
2705
+ const behaviouralConfidence = surfaceFacts === 0 ? 0 : Math.min(100, Math.round(strengthSum / surfaceFacts * 100));
2706
+ const sortedAssertionIds = [...new Set(assertionFactIds)].sort();
2707
+ const specId = `spec:${sha16(canonicalize({ scanId: opts.scanId, assertionIds: sortedAssertionIds }))}`;
2708
+ const specFactId = factId("BehaviouralSpec", specId);
2709
+ const invariants = unique.filter((d) => d.payload.kind === "invariant");
2710
+ const logicFacts = invariants.length;
2711
+ let nodes = 0;
2712
+ let opaque = 0;
2713
+ for (const inv of invariants) {
2714
+ const t = inv.payload.expr?.nodes ?? [];
2715
+ nodes += t.length;
2716
+ opaque += t.filter((n) => n.k === "Opaque").length;
2717
+ }
2718
+ const logicCoverage = nodes === 0 ? 0 : Math.round((1 - opaque / nodes) * 100);
2719
+ envelopes.push({
2720
+ factId: specFactId,
2721
+ factType: "BehaviouralSpec",
2722
+ language: "multi",
2723
+ scanner: "hcs-behavioural",
2724
+ scannerVersion: "0.3.0",
2725
+ emittedAt,
2726
+ payload: {
2727
+ specId,
2728
+ scanId: opts.scanId,
2729
+ applicationId: opts.applicationId,
2730
+ assertionIds: sortedAssertionIds,
2731
+ coverage: { surfaceFacts, assertedFacts, byKind },
2732
+ behaviouralConfidence,
2733
+ logicFacts,
2734
+ logicCoverage
2735
+ }
2736
+ });
2737
+ return { envelopes, summary: { assertions: unique.length, surfaceFacts, assertedFacts, behaviouralConfidence } };
2738
+ }
2739
+
2740
+ // src/commands/extract.ts
1888
2741
  var EXIT_SUCCESS = 0;
1889
2742
  var EXIT_SCHEMA_FAILURE = 1;
1890
2743
  var EXIT_PARSE_FAILURE = 2;
@@ -2553,7 +3406,7 @@ var ProgressTracker = class {
2553
3406
  }
2554
3407
  }
2555
3408
  };
2556
- var extractCommand = new Command8("extract").description("Analyze source code and emit validated HCS facts (NDJSON)").argument("[path]", "Path to source directory", ".").option("-o, --out <file>", "Write NDJSON to file (default: stdout)").option("--language <langs>", "Comma-separated languages to analyze (default: auto-detect)").option("--include-tests", "Include test files in analysis").option("--push", "Stream validated facts to dashboard API").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--concurrency <n>", "Number of parallel file workers").option("--timeout-mins <mins>", "Total analysis timeout in minutes", "60").option("--feedback <file>", "Load NDJSON diff artifact for refinement").option("--verbose", "Print worker stderr and summary statistics").action(async (targetPath, opts) => {
3409
+ var extractCommand = new Command8("extract").description("Analyze source code and emit validated HCS facts (NDJSON)").argument("[path]", "Path to source directory", ".").option("-o, --out <file>", "Write NDJSON to file (default: stdout)").option("--language <langs>", "Comma-separated languages to analyze (default: auto-detect)").option("--include-tests", "Include test files in analysis").option("--push", "Stream validated facts to dashboard API").option("--dsn <dsn>", "DSN token for push (or use VIBGRATE_DSN env)").option("--concurrency <n>", "Number of parallel file workers").option("--timeout-mins <mins>", "Total analysis timeout in minutes", "60").option("--feedback <file>", "Load NDJSON diff artifact for refinement").option("--derive", "Derive behavioural spec facts (BehaviouralAssertion/BehaviouralSpec) from extracted facts").option("--scan-id <id>", 'Scan id for the derived BehaviouralSpec (default: "local")').option("--application-id <id>", "Application id for the derived BehaviouralSpec (default: directory name)").option("--verbose", "Print worker stderr and summary statistics").action(async (targetPath, opts) => {
2557
3410
  const rootDir = path9.resolve(targetPath);
2558
3411
  if (!await pathExists(rootDir)) {
2559
3412
  process.stderr.write(chalk9.red(`Path does not exist: ${rootDir}
@@ -2678,6 +3531,7 @@ var extractCommand = new Command8("extract").description("Analyze source code an
2678
3531
  const allPreamble = [];
2679
3532
  const allFacts = [];
2680
3533
  const allErrors = [];
3534
+ const derivedInputs = [];
2681
3535
  let hasSchemaFailure = false;
2682
3536
  let hasParseFailure = false;
2683
3537
  let hasTimeout = false;
@@ -2703,6 +3557,12 @@ var extractCommand = new Command8("extract").description("Analyze source code an
2703
3557
  }
2704
3558
  const { preamble, facts } = splitHcsOutput(result.facts);
2705
3559
  allPreamble.push(...preamble);
3560
+ if (opts.derive) {
3561
+ try {
3562
+ derivedInputs.push(...decompressFacts([...preamble, ...facts]));
3563
+ } catch {
3564
+ }
3565
+ }
2706
3566
  let langFactCount = 0;
2707
3567
  for (const line of [...preamble, ...facts]) {
2708
3568
  const validation = validateFactLine(line);
@@ -2734,6 +3594,24 @@ var extractCommand = new Command8("extract").description("Analyze source code an
2734
3594
  await Promise.all(workerPromises);
2735
3595
  const elapsed = ((Date.now() - startTime) / 1e3).toFixed(1);
2736
3596
  progress.finish(elapsed);
3597
+ if (opts.derive) {
3598
+ const scanId = opts.scanId || "local";
3599
+ const applicationId = opts.applicationId || path9.basename(rootDir);
3600
+ const { envelopes, summary } = deriveBehaviouralFacts(derivedInputs, { scanId, applicationId });
3601
+ for (const env of envelopes) {
3602
+ const line = JSON.stringify(env);
3603
+ if (validateFactLine(line).valid) {
3604
+ allFacts.push(line);
3605
+ } else {
3606
+ hasSchemaFailure = true;
3607
+ allErrors.push("[behavioural] derived fact failed schema validation");
3608
+ }
3609
+ }
3610
+ process.stderr.write(chalk9.dim(
3611
+ `Derived ${summary.assertions} behavioural assertion(s) \xB7 confidence ${summary.behaviouralConfidence} (${summary.assertedFacts}/${summary.surfaceFacts} surface facts)
3612
+ `
3613
+ ));
3614
+ }
2737
3615
  allFacts.sort();
2738
3616
  const allLines = [...allPreamble, ...allFacts];
2739
3617
  const ndjsonOutput = allLines.join("\n") + (allLines.length > 0 ? "\n" : "");