arkaik 0.1.1 → 0.2.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/LICENSE +21 -0
- package/dist/assets/bootstrap-skill/references/fragments.md +203 -0
- package/dist/assets/bootstrap-skill/references/waves.md +120 -0
- package/dist/assets/bootstrap-skill/skill.md +190 -0
- package/dist/assets/kritik/library.json +5854 -0
- package/dist/assets/skill/references/schema.md +86 -4
- package/dist/assets/skill/scripts/validate-bundle.js +4 -4
- package/dist/assets/skill/skill.md +84 -6
- package/dist/index.js +4853 -367
- package/dist/io.js +1167 -102
- package/package.json +3 -2
- package/src/io.ts +18 -1
package/dist/io.js
CHANGED
|
@@ -6,33 +6,6 @@ var __export = (target, all) => {
|
|
|
6
6
|
|
|
7
7
|
// src/lib/bundle-io.ts
|
|
8
8
|
import { existsSync, readFileSync } from "node:fs";
|
|
9
|
-
function readBundle(filePath) {
|
|
10
|
-
if (!existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
11
|
-
let parsed;
|
|
12
|
-
try {
|
|
13
|
-
parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
14
|
-
} catch (e) {
|
|
15
|
-
throw new Error(`Cannot parse JSON \u2014 ${e.message}`);
|
|
16
|
-
}
|
|
17
|
-
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
18
|
-
throw new Error("Bundle must be a JSON object.");
|
|
19
|
-
}
|
|
20
|
-
return parsed;
|
|
21
|
-
}
|
|
22
|
-
function nodesByIdOf(bundle) {
|
|
23
|
-
const nodes = Array.isArray(bundle.nodes) ? bundle.nodes : [];
|
|
24
|
-
const map2 = /* @__PURE__ */ new Map();
|
|
25
|
-
for (const n of nodes) {
|
|
26
|
-
if (n !== null && typeof n === "object" && typeof n.id === "string") {
|
|
27
|
-
map2.set(n.id, n);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
return map2;
|
|
31
|
-
}
|
|
32
|
-
|
|
33
|
-
// src/lib/journal-io.ts
|
|
34
|
-
import { appendFileSync, existsSync as existsSync2, mkdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
35
|
-
import { dirname, join } from "node:path";
|
|
36
9
|
|
|
37
10
|
// ../../node_modules/zod/v4/classic/external.js
|
|
38
11
|
var external_exports = {};
|
|
@@ -540,7 +513,7 @@ __export(core_exports2, {
|
|
|
540
513
|
parse: () => parse,
|
|
541
514
|
parseAsync: () => parseAsync,
|
|
542
515
|
prettifyError: () => prettifyError,
|
|
543
|
-
process: () =>
|
|
516
|
+
process: () => process2,
|
|
544
517
|
regexes: () => regexes_exports,
|
|
545
518
|
registry: () => registry,
|
|
546
519
|
safeDecode: () => safeDecode,
|
|
@@ -11465,7 +11438,7 @@ function initializeContext(params) {
|
|
|
11465
11438
|
external: params?.external ?? void 0
|
|
11466
11439
|
};
|
|
11467
11440
|
}
|
|
11468
|
-
function
|
|
11441
|
+
function process2(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
11469
11442
|
var _a3;
|
|
11470
11443
|
const def = schema._zod.def;
|
|
11471
11444
|
const seen = ctx.seen.get(schema);
|
|
@@ -11502,7 +11475,7 @@ function process(schema, ctx, _params = { path: [], schemaPath: [] }) {
|
|
|
11502
11475
|
if (parent) {
|
|
11503
11476
|
if (!result.ref)
|
|
11504
11477
|
result.ref = parent;
|
|
11505
|
-
|
|
11478
|
+
process2(parent, ctx, params);
|
|
11506
11479
|
ctx.seen.get(parent).isParent = true;
|
|
11507
11480
|
}
|
|
11508
11481
|
}
|
|
@@ -11790,14 +11763,14 @@ function isTransforming(_schema, _ctx) {
|
|
|
11790
11763
|
}
|
|
11791
11764
|
var createToJSONSchemaMethod = (schema, processors = {}) => (params) => {
|
|
11792
11765
|
const ctx = initializeContext({ ...params, processors });
|
|
11793
|
-
|
|
11766
|
+
process2(schema, ctx);
|
|
11794
11767
|
extractDefs(ctx, schema);
|
|
11795
11768
|
return finalize(ctx, schema);
|
|
11796
11769
|
};
|
|
11797
11770
|
var createStandardJSONSchemaMethod = (schema, io, processors = {}) => (params) => {
|
|
11798
11771
|
const { libraryOptions, target } = params ?? {};
|
|
11799
11772
|
const ctx = initializeContext({ ...libraryOptions ?? {}, target, io, processors });
|
|
11800
|
-
|
|
11773
|
+
process2(schema, ctx);
|
|
11801
11774
|
extractDefs(ctx, schema);
|
|
11802
11775
|
return finalize(ctx, schema);
|
|
11803
11776
|
};
|
|
@@ -12043,7 +12016,7 @@ var arrayProcessor = (schema, ctx, _json, params) => {
|
|
|
12043
12016
|
if (typeof maximum === "number")
|
|
12044
12017
|
json2.maxItems = maximum;
|
|
12045
12018
|
json2.type = "array";
|
|
12046
|
-
json2.items =
|
|
12019
|
+
json2.items = process2(def.element, ctx, {
|
|
12047
12020
|
...params,
|
|
12048
12021
|
path: [...params.path, "items"]
|
|
12049
12022
|
});
|
|
@@ -12055,7 +12028,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12055
12028
|
json2.properties = {};
|
|
12056
12029
|
const shape = def.shape;
|
|
12057
12030
|
for (const key in shape) {
|
|
12058
|
-
json2.properties[key] =
|
|
12031
|
+
json2.properties[key] = process2(shape[key], ctx, {
|
|
12059
12032
|
...params,
|
|
12060
12033
|
path: [...params.path, "properties", key]
|
|
12061
12034
|
});
|
|
@@ -12078,7 +12051,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12078
12051
|
if (ctx.io === "output")
|
|
12079
12052
|
json2.additionalProperties = false;
|
|
12080
12053
|
} else if (def.catchall) {
|
|
12081
|
-
json2.additionalProperties =
|
|
12054
|
+
json2.additionalProperties = process2(def.catchall, ctx, {
|
|
12082
12055
|
...params,
|
|
12083
12056
|
path: [...params.path, "additionalProperties"]
|
|
12084
12057
|
});
|
|
@@ -12087,7 +12060,7 @@ var objectProcessor = (schema, ctx, _json, params) => {
|
|
|
12087
12060
|
var unionProcessor = (schema, ctx, json2, params) => {
|
|
12088
12061
|
const def = schema._zod.def;
|
|
12089
12062
|
const isExclusive = def.inclusive === false;
|
|
12090
|
-
const options = def.options.map((x, i) =>
|
|
12063
|
+
const options = def.options.map((x, i) => process2(x, ctx, {
|
|
12091
12064
|
...params,
|
|
12092
12065
|
path: [...params.path, isExclusive ? "oneOf" : "anyOf", i]
|
|
12093
12066
|
}));
|
|
@@ -12099,11 +12072,11 @@ var unionProcessor = (schema, ctx, json2, params) => {
|
|
|
12099
12072
|
};
|
|
12100
12073
|
var intersectionProcessor = (schema, ctx, json2, params) => {
|
|
12101
12074
|
const def = schema._zod.def;
|
|
12102
|
-
const a =
|
|
12075
|
+
const a = process2(def.left, ctx, {
|
|
12103
12076
|
...params,
|
|
12104
12077
|
path: [...params.path, "allOf", 0]
|
|
12105
12078
|
});
|
|
12106
|
-
const b =
|
|
12079
|
+
const b = process2(def.right, ctx, {
|
|
12107
12080
|
...params,
|
|
12108
12081
|
path: [...params.path, "allOf", 1]
|
|
12109
12082
|
});
|
|
@@ -12120,11 +12093,11 @@ var tupleProcessor = (schema, ctx, _json, params) => {
|
|
|
12120
12093
|
json2.type = "array";
|
|
12121
12094
|
const prefixPath = ctx.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
12122
12095
|
const restPath = ctx.target === "draft-2020-12" ? "items" : ctx.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
12123
|
-
const prefixItems = def.items.map((x, i) =>
|
|
12096
|
+
const prefixItems = def.items.map((x, i) => process2(x, ctx, {
|
|
12124
12097
|
...params,
|
|
12125
12098
|
path: [...params.path, prefixPath, i]
|
|
12126
12099
|
}));
|
|
12127
|
-
const rest = def.rest ?
|
|
12100
|
+
const rest = def.rest ? process2(def.rest, ctx, {
|
|
12128
12101
|
...params,
|
|
12129
12102
|
path: [...params.path, restPath, ...ctx.target === "openapi-3.0" ? [def.items.length] : []]
|
|
12130
12103
|
}) : null;
|
|
@@ -12164,7 +12137,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12164
12137
|
const keyBag = keyType._zod.bag;
|
|
12165
12138
|
const patterns = keyBag?.patterns;
|
|
12166
12139
|
if (def.mode === "loose" && patterns && patterns.size > 0) {
|
|
12167
|
-
const valueSchema =
|
|
12140
|
+
const valueSchema = process2(def.valueType, ctx, {
|
|
12168
12141
|
...params,
|
|
12169
12142
|
path: [...params.path, "patternProperties", "*"]
|
|
12170
12143
|
});
|
|
@@ -12174,12 +12147,12 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12174
12147
|
}
|
|
12175
12148
|
} else {
|
|
12176
12149
|
if (ctx.target === "draft-07" || ctx.target === "draft-2020-12") {
|
|
12177
|
-
json2.propertyNames =
|
|
12150
|
+
json2.propertyNames = process2(def.keyType, ctx, {
|
|
12178
12151
|
...params,
|
|
12179
12152
|
path: [...params.path, "propertyNames"]
|
|
12180
12153
|
});
|
|
12181
12154
|
}
|
|
12182
|
-
json2.additionalProperties =
|
|
12155
|
+
json2.additionalProperties = process2(def.valueType, ctx, {
|
|
12183
12156
|
...params,
|
|
12184
12157
|
path: [...params.path, "additionalProperties"]
|
|
12185
12158
|
});
|
|
@@ -12194,7 +12167,7 @@ var recordProcessor = (schema, ctx, _json, params) => {
|
|
|
12194
12167
|
};
|
|
12195
12168
|
var nullableProcessor = (schema, ctx, json2, params) => {
|
|
12196
12169
|
const def = schema._zod.def;
|
|
12197
|
-
const inner =
|
|
12170
|
+
const inner = process2(def.innerType, ctx, params);
|
|
12198
12171
|
const seen = ctx.seen.get(schema);
|
|
12199
12172
|
if (ctx.target === "openapi-3.0") {
|
|
12200
12173
|
seen.ref = def.innerType;
|
|
@@ -12205,20 +12178,20 @@ var nullableProcessor = (schema, ctx, json2, params) => {
|
|
|
12205
12178
|
};
|
|
12206
12179
|
var nonoptionalProcessor = (schema, ctx, _json, params) => {
|
|
12207
12180
|
const def = schema._zod.def;
|
|
12208
|
-
|
|
12181
|
+
process2(def.innerType, ctx, params);
|
|
12209
12182
|
const seen = ctx.seen.get(schema);
|
|
12210
12183
|
seen.ref = def.innerType;
|
|
12211
12184
|
};
|
|
12212
12185
|
var defaultProcessor = (schema, ctx, json2, params) => {
|
|
12213
12186
|
const def = schema._zod.def;
|
|
12214
|
-
|
|
12187
|
+
process2(def.innerType, ctx, params);
|
|
12215
12188
|
const seen = ctx.seen.get(schema);
|
|
12216
12189
|
seen.ref = def.innerType;
|
|
12217
12190
|
json2.default = JSON.parse(JSON.stringify(def.defaultValue));
|
|
12218
12191
|
};
|
|
12219
12192
|
var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
12220
12193
|
const def = schema._zod.def;
|
|
12221
|
-
|
|
12194
|
+
process2(def.innerType, ctx, params);
|
|
12222
12195
|
const seen = ctx.seen.get(schema);
|
|
12223
12196
|
seen.ref = def.innerType;
|
|
12224
12197
|
if (ctx.io === "input")
|
|
@@ -12226,7 +12199,7 @@ var prefaultProcessor = (schema, ctx, json2, params) => {
|
|
|
12226
12199
|
};
|
|
12227
12200
|
var catchProcessor = (schema, ctx, json2, params) => {
|
|
12228
12201
|
const def = schema._zod.def;
|
|
12229
|
-
|
|
12202
|
+
process2(def.innerType, ctx, params);
|
|
12230
12203
|
const seen = ctx.seen.get(schema);
|
|
12231
12204
|
seen.ref = def.innerType;
|
|
12232
12205
|
let catchValue;
|
|
@@ -12241,32 +12214,32 @@ var pipeProcessor = (schema, ctx, _json, params) => {
|
|
|
12241
12214
|
const def = schema._zod.def;
|
|
12242
12215
|
const inIsTransform = def.in._zod.traits.has("$ZodTransform");
|
|
12243
12216
|
const innerType = ctx.io === "input" ? inIsTransform ? def.out : def.in : def.out;
|
|
12244
|
-
|
|
12217
|
+
process2(innerType, ctx, params);
|
|
12245
12218
|
const seen = ctx.seen.get(schema);
|
|
12246
12219
|
seen.ref = innerType;
|
|
12247
12220
|
};
|
|
12248
12221
|
var readonlyProcessor = (schema, ctx, json2, params) => {
|
|
12249
12222
|
const def = schema._zod.def;
|
|
12250
|
-
|
|
12223
|
+
process2(def.innerType, ctx, params);
|
|
12251
12224
|
const seen = ctx.seen.get(schema);
|
|
12252
12225
|
seen.ref = def.innerType;
|
|
12253
12226
|
json2.readOnly = true;
|
|
12254
12227
|
};
|
|
12255
12228
|
var promiseProcessor = (schema, ctx, _json, params) => {
|
|
12256
12229
|
const def = schema._zod.def;
|
|
12257
|
-
|
|
12230
|
+
process2(def.innerType, ctx, params);
|
|
12258
12231
|
const seen = ctx.seen.get(schema);
|
|
12259
12232
|
seen.ref = def.innerType;
|
|
12260
12233
|
};
|
|
12261
12234
|
var optionalProcessor = (schema, ctx, _json, params) => {
|
|
12262
12235
|
const def = schema._zod.def;
|
|
12263
|
-
|
|
12236
|
+
process2(def.innerType, ctx, params);
|
|
12264
12237
|
const seen = ctx.seen.get(schema);
|
|
12265
12238
|
seen.ref = def.innerType;
|
|
12266
12239
|
};
|
|
12267
12240
|
var lazyProcessor = (schema, ctx, _json, params) => {
|
|
12268
12241
|
const innerType = schema._zod.innerType;
|
|
12269
|
-
|
|
12242
|
+
process2(innerType, ctx, params);
|
|
12270
12243
|
const seen = ctx.seen.get(schema);
|
|
12271
12244
|
seen.ref = innerType;
|
|
12272
12245
|
};
|
|
@@ -12318,7 +12291,7 @@ function toJSONSchema(input, params) {
|
|
|
12318
12291
|
const defs = {};
|
|
12319
12292
|
for (const entry of registry2._idmap.entries()) {
|
|
12320
12293
|
const [_, schema] = entry;
|
|
12321
|
-
|
|
12294
|
+
process2(schema, ctx2);
|
|
12322
12295
|
}
|
|
12323
12296
|
const schemas = {};
|
|
12324
12297
|
const external = {
|
|
@@ -12341,7 +12314,7 @@ function toJSONSchema(input, params) {
|
|
|
12341
12314
|
return { schemas };
|
|
12342
12315
|
}
|
|
12343
12316
|
const ctx = initializeContext({ ...params, processors: allProcessors });
|
|
12344
|
-
|
|
12317
|
+
process2(input, ctx);
|
|
12345
12318
|
extractDefs(ctx, input);
|
|
12346
12319
|
return finalize(ctx, input);
|
|
12347
12320
|
}
|
|
@@ -12399,7 +12372,7 @@ var JSONSchemaGenerator = class {
|
|
|
12399
12372
|
* This must be called before emit().
|
|
12400
12373
|
*/
|
|
12401
12374
|
process(schema, _params = { path: [], schemaPath: [] }) {
|
|
12402
|
-
return
|
|
12375
|
+
return process2(schema, this.ctx, _params);
|
|
12403
12376
|
}
|
|
12404
12377
|
/**
|
|
12405
12378
|
* Emit the final JSON Schema after processing.
|
|
@@ -14549,19 +14522,58 @@ function date4(params) {
|
|
|
14549
14522
|
config(en_default());
|
|
14550
14523
|
|
|
14551
14524
|
// ../schema/src/ids.ts
|
|
14552
|
-
var SPECIES_IDS = ["flow", "view", "data-model", "api-endpoint", "acceptance"];
|
|
14525
|
+
var SPECIES_IDS = ["flow", "view", "data-model", "api-endpoint", "acceptance", "decision"];
|
|
14553
14526
|
var STATUS_IDS = [
|
|
14554
14527
|
"idea",
|
|
14528
|
+
"discovery",
|
|
14555
14529
|
"backlog",
|
|
14556
|
-
"prioritized",
|
|
14557
14530
|
"development",
|
|
14558
14531
|
"releasing",
|
|
14559
14532
|
"live",
|
|
14560
|
-
"archived"
|
|
14561
|
-
"blocked"
|
|
14533
|
+
"archived"
|
|
14562
14534
|
];
|
|
14563
14535
|
var PLATFORM_IDS = ["web", "ios", "android"];
|
|
14564
|
-
var EDGE_TYPE_IDS = ["composes", "calls", "displays", "queries", "covers"];
|
|
14536
|
+
var EDGE_TYPE_IDS = ["composes", "calls", "displays", "queries", "covers", "supersedes", "generates", "impacts"];
|
|
14537
|
+
var VALID_EDGE_SEMANTICS = {
|
|
14538
|
+
composes: [
|
|
14539
|
+
["flow", "view"],
|
|
14540
|
+
["flow", "flow"],
|
|
14541
|
+
["view", "flow"],
|
|
14542
|
+
["view", "view"]
|
|
14543
|
+
],
|
|
14544
|
+
// `calls` names the initiator, which is why it runs in both directions
|
|
14545
|
+
// between a view and an endpoint: view → api is the outbound/write
|
|
14546
|
+
// affordance, api → view the inbound/read one a server opens itself (a
|
|
14547
|
+
// webhook, an SSE channel, a push). Both project onto the View card
|
|
14548
|
+
// (docs/graph-model.md § Edge Types). Consumers that walk `calls` must
|
|
14549
|
+
// therefore not assume it points down into the system layer — see
|
|
14550
|
+
// `buildProductUsageIndex` in ./products.ts, which restricts hops by
|
|
14551
|
+
// target species for exactly this reason.
|
|
14552
|
+
calls: [
|
|
14553
|
+
["view", "api-endpoint"],
|
|
14554
|
+
["flow", "api-endpoint"],
|
|
14555
|
+
["api-endpoint", "api-endpoint"],
|
|
14556
|
+
["api-endpoint", "view"]
|
|
14557
|
+
],
|
|
14558
|
+
displays: [["view", "data-model"]],
|
|
14559
|
+
queries: [["api-endpoint", "data-model"]],
|
|
14560
|
+
covers: [
|
|
14561
|
+
["acceptance", "view"],
|
|
14562
|
+
["acceptance", "flow"]
|
|
14563
|
+
],
|
|
14564
|
+
// Decision edges (cycle 2). `generates` and `impacts` are deliberately
|
|
14565
|
+
// disjoint: an acceptance is *generated* by a decision, never merely
|
|
14566
|
+
// impacted, so `impacts` does not admit an acceptance target
|
|
14567
|
+
// (docs/superpowers/specs/2026-08-03-decisions-species-design.md §3).
|
|
14568
|
+
supersedes: [["decision", "decision"]],
|
|
14569
|
+
generates: [["decision", "acceptance"]],
|
|
14570
|
+
impacts: [
|
|
14571
|
+
["decision", "flow"],
|
|
14572
|
+
["decision", "view"],
|
|
14573
|
+
["decision", "data-model"],
|
|
14574
|
+
["decision", "api-endpoint"]
|
|
14575
|
+
]
|
|
14576
|
+
};
|
|
14565
14577
|
var VALUE_TIER_IDS = ["functional", "emotional", "life-changing", "social-impact"];
|
|
14566
14578
|
var VALUE_IDS = [
|
|
14567
14579
|
// functional (14)
|
|
@@ -14600,6 +14612,106 @@ var VALUE_IDS = [
|
|
|
14600
14612
|
"self-transcendence"
|
|
14601
14613
|
];
|
|
14602
14614
|
|
|
14615
|
+
// ../schema/src/legacy-status.ts
|
|
14616
|
+
var LEGACY_STATUS_IDS = ["prioritized", "blocked"];
|
|
14617
|
+
var LEGACY_STATUS_ALIASES = {
|
|
14618
|
+
prioritized: "backlog",
|
|
14619
|
+
blocked: "development"
|
|
14620
|
+
};
|
|
14621
|
+
var STATUS_VOCABULARY_VERSION = 3;
|
|
14622
|
+
var BLOCKED_BY_MIGRATION_NOTE = "migrated from legacy blocked status";
|
|
14623
|
+
function normalizeStatus(value) {
|
|
14624
|
+
if (STATUS_IDS.includes(value)) return value;
|
|
14625
|
+
return LEGACY_STATUS_ALIASES[value];
|
|
14626
|
+
}
|
|
14627
|
+
var LEGACY_ALIAS_REMAP = { ...LEGACY_STATUS_ALIASES };
|
|
14628
|
+
var LEGACY_VOCABULARY_REMAP = {
|
|
14629
|
+
backlog: "idea",
|
|
14630
|
+
...LEGACY_STATUS_ALIASES
|
|
14631
|
+
};
|
|
14632
|
+
function migrateNode(node, remap) {
|
|
14633
|
+
let changed = false;
|
|
14634
|
+
let wasBlocked = node.status === "blocked";
|
|
14635
|
+
const mappedStatus = remap[node.status];
|
|
14636
|
+
const status = mappedStatus ?? node.status;
|
|
14637
|
+
if (mappedStatus !== void 0) changed = true;
|
|
14638
|
+
let metadata = node.metadata;
|
|
14639
|
+
const platformStatuses = node.metadata?.platformStatuses;
|
|
14640
|
+
if (platformStatuses !== void 0) {
|
|
14641
|
+
let anyMapped = false;
|
|
14642
|
+
const next = {};
|
|
14643
|
+
for (const [platform, value] of Object.entries(platformStatuses)) {
|
|
14644
|
+
if (value === "blocked") wasBlocked = true;
|
|
14645
|
+
const mapped = remap[value];
|
|
14646
|
+
next[platform] = mapped ?? value;
|
|
14647
|
+
if (mapped !== void 0) anyMapped = true;
|
|
14648
|
+
}
|
|
14649
|
+
if (anyMapped) {
|
|
14650
|
+
metadata = { ...metadata, platformStatuses: next };
|
|
14651
|
+
changed = true;
|
|
14652
|
+
}
|
|
14653
|
+
}
|
|
14654
|
+
const refs = node.metadata?.refs;
|
|
14655
|
+
if (Array.isArray(refs)) {
|
|
14656
|
+
let anyMapped = false;
|
|
14657
|
+
const nextRefs = refs.map((ref) => {
|
|
14658
|
+
if (ref.status_mapped === void 0) return ref;
|
|
14659
|
+
const mapped = remap[ref.status_mapped];
|
|
14660
|
+
if (mapped === void 0) return ref;
|
|
14661
|
+
anyMapped = true;
|
|
14662
|
+
return { ...ref, status_mapped: mapped };
|
|
14663
|
+
});
|
|
14664
|
+
if (anyMapped) {
|
|
14665
|
+
metadata = { ...metadata, refs: nextRefs };
|
|
14666
|
+
changed = true;
|
|
14667
|
+
}
|
|
14668
|
+
}
|
|
14669
|
+
if (wasBlocked && !metadata?.blocked_by) {
|
|
14670
|
+
metadata = { ...metadata, blocked_by: BLOCKED_BY_MIGRATION_NOTE };
|
|
14671
|
+
changed = true;
|
|
14672
|
+
}
|
|
14673
|
+
if (!changed) return node;
|
|
14674
|
+
return { ...node, status, ...metadata !== void 0 ? { metadata } : {} };
|
|
14675
|
+
}
|
|
14676
|
+
function migrateStatusVocabulary(bundle) {
|
|
14677
|
+
const current = typeof bundle.schema_version === "number" && bundle.schema_version >= STATUS_VOCABULARY_VERSION;
|
|
14678
|
+
const remap = current ? LEGACY_ALIAS_REMAP : LEGACY_VOCABULARY_REMAP;
|
|
14679
|
+
const nodes = bundle.nodes.map((node) => migrateNode(node, remap));
|
|
14680
|
+
if (current) {
|
|
14681
|
+
const untouched = nodes.every((node, i) => node === bundle.nodes[i]);
|
|
14682
|
+
return untouched ? bundle : { ...bundle, nodes };
|
|
14683
|
+
}
|
|
14684
|
+
return { ...bundle, schema_version: STATUS_VOCABULARY_VERSION, nodes };
|
|
14685
|
+
}
|
|
14686
|
+
|
|
14687
|
+
// ../schema/src/decision.ts
|
|
14688
|
+
var DECISION_STATUS_IDS = [
|
|
14689
|
+
"proposed",
|
|
14690
|
+
"approved",
|
|
14691
|
+
"enacted",
|
|
14692
|
+
"rejected",
|
|
14693
|
+
"deprecated",
|
|
14694
|
+
"superseded"
|
|
14695
|
+
];
|
|
14696
|
+
function lifecycleStatusForDecision(decisionStatus) {
|
|
14697
|
+
switch (decisionStatus) {
|
|
14698
|
+
case "proposed":
|
|
14699
|
+
return "discovery";
|
|
14700
|
+
case "approved":
|
|
14701
|
+
return "backlog";
|
|
14702
|
+
case "enacted":
|
|
14703
|
+
return "live";
|
|
14704
|
+
case "rejected":
|
|
14705
|
+
case "deprecated":
|
|
14706
|
+
case "superseded":
|
|
14707
|
+
return "archived";
|
|
14708
|
+
}
|
|
14709
|
+
}
|
|
14710
|
+
function decisionStatusOf(node) {
|
|
14711
|
+
const raw = node.metadata?.decision_status;
|
|
14712
|
+
return DECISION_STATUS_IDS.includes(raw) ? raw : "proposed";
|
|
14713
|
+
}
|
|
14714
|
+
|
|
14603
14715
|
// ../schema/src/enums.ts
|
|
14604
14716
|
var SpeciesSchema = external_exports.enum(SPECIES_IDS).meta({
|
|
14605
14717
|
id: "Species",
|
|
@@ -14609,6 +14721,10 @@ var StatusSchema = external_exports.enum(STATUS_IDS).meta({
|
|
|
14609
14721
|
id: "Status",
|
|
14610
14722
|
description: "Lifecycle status of a node."
|
|
14611
14723
|
});
|
|
14724
|
+
var AnyStatusSchema = external_exports.enum([...STATUS_IDS, ...LEGACY_STATUS_IDS]).meta({
|
|
14725
|
+
id: "AnyStatus",
|
|
14726
|
+
description: "Lifecycle status as stored: the current vocabulary, or a legacy id (prioritized, blocked) accepted from pre-v3 bundles and migrated on load."
|
|
14727
|
+
});
|
|
14612
14728
|
var PlatformSchema = external_exports.enum(PLATFORM_IDS).meta({
|
|
14613
14729
|
id: "Platform",
|
|
14614
14730
|
description: "Target platform."
|
|
@@ -14625,6 +14741,10 @@ var ValueSchema = external_exports.enum(VALUE_IDS).meta({
|
|
|
14625
14741
|
id: "Value",
|
|
14626
14742
|
description: "A Bain B2C Elements-of-Value element served by an acceptance (spec \xA73.2)."
|
|
14627
14743
|
});
|
|
14744
|
+
var DecisionStatusSchema = external_exports.enum(DECISION_STATUS_IDS).meta({
|
|
14745
|
+
id: "DecisionStatus",
|
|
14746
|
+
description: "Decision nodes only: proposed \u2192 approved (agreed, not yet reality) \u2192 enacted (in effect); terminal: rejected, deprecated, superseded. Not a lifecycle status \u2014 the node's status field is kept in sync (proposed\u2192discovery, approved\u2192backlog, enacted\u2192live, terminals\u2192archived)."
|
|
14747
|
+
});
|
|
14628
14748
|
|
|
14629
14749
|
// ../schema/src/id-gen.ts
|
|
14630
14750
|
var SPECIES_PREFIXES = {
|
|
@@ -14632,7 +14752,8 @@ var SPECIES_PREFIXES = {
|
|
|
14632
14752
|
view: "V-",
|
|
14633
14753
|
"data-model": "DM-",
|
|
14634
14754
|
"api-endpoint": "API-",
|
|
14635
|
-
acceptance: "AC-"
|
|
14755
|
+
acceptance: "AC-",
|
|
14756
|
+
decision: "DEC-"
|
|
14636
14757
|
};
|
|
14637
14758
|
|
|
14638
14759
|
// ../schema/src/playlist.ts
|
|
@@ -14730,6 +14851,7 @@ function parseJournalLines(text) {
|
|
|
14730
14851
|
var NODE_REF_FIELDS = {
|
|
14731
14852
|
"node.updated": ["node_id"],
|
|
14732
14853
|
"node.status_changed": ["node_id"],
|
|
14854
|
+
"decision.status_changed": ["node_id"],
|
|
14733
14855
|
"node.deleted": ["node_id"],
|
|
14734
14856
|
"edge.added": ["source_id", "target_id"],
|
|
14735
14857
|
"ref.added": ["node_id"],
|
|
@@ -14738,6 +14860,12 @@ var NODE_REF_FIELDS = {
|
|
|
14738
14860
|
"idea.proposed": ["node_id"],
|
|
14739
14861
|
"request.filed": ["node_id"]
|
|
14740
14862
|
};
|
|
14863
|
+
function statusesAgree(last, snapshot) {
|
|
14864
|
+
if (last === snapshot) return true;
|
|
14865
|
+
if (typeof snapshot !== "string") return false;
|
|
14866
|
+
if (last === "backlog" && snapshot === "idea") return true;
|
|
14867
|
+
return (normalizeStatus(last) ?? last) === (normalizeStatus(snapshot) ?? snapshot);
|
|
14868
|
+
}
|
|
14741
14869
|
function crossCheckJournal(bundle) {
|
|
14742
14870
|
const findings = [];
|
|
14743
14871
|
const journalRaw = bundle.journal;
|
|
@@ -14756,9 +14884,15 @@ function crossCheckJournal(bundle) {
|
|
|
14756
14884
|
const nodesRaw = Array.isArray(bundle.nodes) ? bundle.nodes : [];
|
|
14757
14885
|
const edgesRaw = Array.isArray(bundle.edges) ? bundle.edges : [];
|
|
14758
14886
|
const snapshotNodeStatus = /* @__PURE__ */ new Map();
|
|
14887
|
+
const snapshotDecisionStatus = /* @__PURE__ */ new Map();
|
|
14759
14888
|
for (const n of nodesRaw) {
|
|
14760
14889
|
const id = str(n?.id);
|
|
14761
|
-
if (id !== void 0)
|
|
14890
|
+
if (id !== void 0) {
|
|
14891
|
+
snapshotNodeStatus.set(id, n.status);
|
|
14892
|
+
const metadata = n.metadata;
|
|
14893
|
+
const decisionStatus = metadata && typeof metadata === "object" && !Array.isArray(metadata) ? metadata.decision_status : void 0;
|
|
14894
|
+
snapshotDecisionStatus.set(id, decisionStatus ?? "proposed");
|
|
14895
|
+
}
|
|
14762
14896
|
}
|
|
14763
14897
|
const snapshotEdgeIds = /* @__PURE__ */ new Set();
|
|
14764
14898
|
for (const e of edgesRaw) {
|
|
@@ -14795,6 +14929,7 @@ function crossCheckJournal(bundle) {
|
|
|
14795
14929
|
});
|
|
14796
14930
|
const everNodes = new Set(snapshotNodeStatus.keys());
|
|
14797
14931
|
const everEdges = new Set(snapshotEdgeIds);
|
|
14932
|
+
const baselined = /* @__PURE__ */ new Set();
|
|
14798
14933
|
for (const { ev } of valid) {
|
|
14799
14934
|
if (ev.type === "node.created") {
|
|
14800
14935
|
const nid = str(ev.node_id);
|
|
@@ -14802,11 +14937,20 @@ function crossCheckJournal(bundle) {
|
|
|
14802
14937
|
} else if (ev.type === "edge.added") {
|
|
14803
14938
|
const eid = str(ev.edge_id);
|
|
14804
14939
|
if (eid) everEdges.add(eid);
|
|
14940
|
+
} else if (ev.type === "journal.baseline" && Array.isArray(ev.node_ids)) {
|
|
14941
|
+
for (const raw of ev.node_ids) {
|
|
14942
|
+
const nid = str(raw);
|
|
14943
|
+
if (nid) {
|
|
14944
|
+
baselined.add(nid);
|
|
14945
|
+
everNodes.add(nid);
|
|
14946
|
+
}
|
|
14947
|
+
}
|
|
14805
14948
|
}
|
|
14806
14949
|
}
|
|
14807
14950
|
const ordered = orderEvents(valid.map((v) => v.ev));
|
|
14808
14951
|
const created = /* @__PURE__ */ new Set();
|
|
14809
14952
|
const lastProjectStatus = /* @__PURE__ */ new Map();
|
|
14953
|
+
const lastDecisionStatus = /* @__PURE__ */ new Map();
|
|
14810
14954
|
for (const ev of ordered) {
|
|
14811
14955
|
if (ev.type === "node.created") {
|
|
14812
14956
|
const nid = str(ev.node_id);
|
|
@@ -14817,6 +14961,12 @@ function crossCheckJournal(bundle) {
|
|
|
14817
14961
|
const to = str(ev.to);
|
|
14818
14962
|
if (to !== void 0) lastProjectStatus.set(nid, to);
|
|
14819
14963
|
}
|
|
14964
|
+
} else if (ev.type === "decision.status_changed") {
|
|
14965
|
+
const nid = str(ev.node_id);
|
|
14966
|
+
if (nid) {
|
|
14967
|
+
const to = str(ev.to);
|
|
14968
|
+
if (to !== void 0) lastDecisionStatus.set(nid, to);
|
|
14969
|
+
}
|
|
14820
14970
|
}
|
|
14821
14971
|
}
|
|
14822
14972
|
for (const { ev, index } of valid) {
|
|
@@ -14834,6 +14984,19 @@ function crossCheckJournal(bundle) {
|
|
|
14834
14984
|
}
|
|
14835
14985
|
}
|
|
14836
14986
|
}
|
|
14987
|
+
if (ev.type === "deliverable.shipped" && Array.isArray(ev.node_ids)) {
|
|
14988
|
+
ev.node_ids.forEach((raw, i) => {
|
|
14989
|
+
const ref = str(raw);
|
|
14990
|
+
if (ref !== void 0 && !everNodes.has(ref)) {
|
|
14991
|
+
findings.push({
|
|
14992
|
+
path: `journal[${index}].node_ids[${i}]`,
|
|
14993
|
+
rule: "journal-dangling-node-ref",
|
|
14994
|
+
message: `journal[${index}] (${ev.type}): references node "${ref}" that never existed in the snapshot or journal.`,
|
|
14995
|
+
severity: "error"
|
|
14996
|
+
});
|
|
14997
|
+
}
|
|
14998
|
+
});
|
|
14999
|
+
}
|
|
14837
15000
|
if (ev.type === "edge.removed") {
|
|
14838
15001
|
const ref = str(ev.edge_id);
|
|
14839
15002
|
if (ref !== void 0 && !everEdges.has(ref)) {
|
|
@@ -14847,7 +15010,7 @@ function crossCheckJournal(bundle) {
|
|
|
14847
15010
|
}
|
|
14848
15011
|
}
|
|
14849
15012
|
for (const [nodeId, status] of snapshotNodeStatus) {
|
|
14850
|
-
if (!created.has(nodeId)) {
|
|
15013
|
+
if (!created.has(nodeId) && !baselined.has(nodeId)) {
|
|
14851
15014
|
findings.push({
|
|
14852
15015
|
path: "journal",
|
|
14853
15016
|
rule: "journal-missing-node-created",
|
|
@@ -14856,7 +15019,7 @@ function crossCheckJournal(bundle) {
|
|
|
14856
15019
|
});
|
|
14857
15020
|
}
|
|
14858
15021
|
const last = lastProjectStatus.get(nodeId);
|
|
14859
|
-
if (last !== void 0 && last
|
|
15022
|
+
if (last !== void 0 && !statusesAgree(last, status)) {
|
|
14860
15023
|
findings.push({
|
|
14861
15024
|
path: "journal",
|
|
14862
15025
|
rule: "journal-status-mismatch",
|
|
@@ -14865,8 +15028,40 @@ function crossCheckJournal(bundle) {
|
|
|
14865
15028
|
});
|
|
14866
15029
|
}
|
|
14867
15030
|
}
|
|
15031
|
+
for (const [nodeId, last] of lastDecisionStatus) {
|
|
15032
|
+
if (!snapshotDecisionStatus.has(nodeId)) continue;
|
|
15033
|
+
const current = snapshotDecisionStatus.get(nodeId) ?? "proposed";
|
|
15034
|
+
if (last !== current) {
|
|
15035
|
+
findings.push({
|
|
15036
|
+
path: "journal",
|
|
15037
|
+
rule: "journal-decision-status-mismatch",
|
|
15038
|
+
message: `Node "${nodeId}": journal's last decision.status_changed.to "${last}" disagrees with snapshot decision_status "${String(current)}".`,
|
|
15039
|
+
severity: "error"
|
|
15040
|
+
});
|
|
15041
|
+
}
|
|
15042
|
+
}
|
|
14868
15043
|
return findings;
|
|
14869
15044
|
}
|
|
15045
|
+
function missingProvenanceNodeIds(snapshotNodeIds, events) {
|
|
15046
|
+
const covered = /* @__PURE__ */ new Set();
|
|
15047
|
+
for (const ev of events) {
|
|
15048
|
+
if (ev.type === "node.created") {
|
|
15049
|
+
if (typeof ev.node_id === "string") covered.add(ev.node_id);
|
|
15050
|
+
} else if (ev.type === "journal.baseline" && Array.isArray(ev.node_ids)) {
|
|
15051
|
+
for (const id of ev.node_ids) {
|
|
15052
|
+
if (typeof id === "string") covered.add(id);
|
|
15053
|
+
}
|
|
15054
|
+
}
|
|
15055
|
+
}
|
|
15056
|
+
const missing = [];
|
|
15057
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15058
|
+
for (const id of snapshotNodeIds) {
|
|
15059
|
+
if (covered.has(id) || seen.has(id)) continue;
|
|
15060
|
+
seen.add(id);
|
|
15061
|
+
missing.push(id);
|
|
15062
|
+
}
|
|
15063
|
+
return missing;
|
|
15064
|
+
}
|
|
14870
15065
|
|
|
14871
15066
|
// ../schema/src/journal-events.ts
|
|
14872
15067
|
var envelope = {
|
|
@@ -14895,10 +15090,19 @@ var NodeStatusChangedEventSchema = external_exports.object({
|
|
|
14895
15090
|
...envelope,
|
|
14896
15091
|
type: external_exports.literal("node.status_changed"),
|
|
14897
15092
|
node_id: external_exports.string(),
|
|
14898
|
-
|
|
14899
|
-
|
|
15093
|
+
// History is never rewritten (docs/spec/journal.md): pre-v3 events keep
|
|
15094
|
+
// their legacy status ids, so strict per-type validation must accept them.
|
|
15095
|
+
from: AnyStatusSchema,
|
|
15096
|
+
to: AnyStatusSchema,
|
|
14900
15097
|
platform: PlatformSchema.optional()
|
|
14901
15098
|
}).catchall(external_exports.unknown());
|
|
15099
|
+
var DecisionStatusChangedEventSchema = external_exports.object({
|
|
15100
|
+
...envelope,
|
|
15101
|
+
type: external_exports.literal("decision.status_changed"),
|
|
15102
|
+
node_id: external_exports.string(),
|
|
15103
|
+
from: DecisionStatusSchema,
|
|
15104
|
+
to: DecisionStatusSchema
|
|
15105
|
+
}).catchall(external_exports.unknown());
|
|
14902
15106
|
var NodeDeletedEventSchema = external_exports.object({ ...envelope, type: external_exports.literal("node.deleted"), node_id: external_exports.string() }).catchall(external_exports.unknown());
|
|
14903
15107
|
var EdgeAddedEventSchema = external_exports.object({
|
|
14904
15108
|
...envelope,
|
|
@@ -14916,6 +15120,21 @@ var ReleaseTaggedEventSchema = external_exports.object({
|
|
|
14916
15120
|
notes: external_exports.string().optional(),
|
|
14917
15121
|
platform: PlatformSchema.optional()
|
|
14918
15122
|
}).catchall(external_exports.unknown());
|
|
15123
|
+
var DeliverableShippedEventSchema = external_exports.object({
|
|
15124
|
+
...envelope,
|
|
15125
|
+
type: external_exports.literal("deliverable.shipped"),
|
|
15126
|
+
deliverable_id: external_exports.string(),
|
|
15127
|
+
title: external_exports.string(),
|
|
15128
|
+
summary: external_exports.string().optional(),
|
|
15129
|
+
url: external_exports.string().optional(),
|
|
15130
|
+
node_ids: external_exports.array(external_exports.string()).optional(),
|
|
15131
|
+
platform: PlatformSchema.optional(),
|
|
15132
|
+
lab_note: external_exports.object({
|
|
15133
|
+
en: external_exports.object({ title: external_exports.string(), summary: external_exports.string() }).catchall(external_exports.unknown()),
|
|
15134
|
+
fr: external_exports.object({ title: external_exports.string().optional(), summary: external_exports.string().optional() }).catchall(external_exports.unknown()).optional(),
|
|
15135
|
+
suggested: external_exports.record(external_exports.string(), external_exports.unknown()).optional()
|
|
15136
|
+
}).catchall(external_exports.unknown()).optional().meta({ description: "The Lab Note a merged PR carried (slice 3); en.title/en.summary required when present." })
|
|
15137
|
+
}).catchall(external_exports.unknown());
|
|
14919
15138
|
var IdeaProposedEventSchema = external_exports.object({
|
|
14920
15139
|
...envelope,
|
|
14921
15140
|
type: external_exports.literal("idea.proposed"),
|
|
@@ -14949,25 +15168,197 @@ var RefStatusChangedEventSchema = external_exports.object({
|
|
|
14949
15168
|
to: external_exports.string(),
|
|
14950
15169
|
synced_at: external_exports.string()
|
|
14951
15170
|
}).catchall(external_exports.unknown());
|
|
15171
|
+
var JournalBaselineEventSchema = external_exports.object({ ...envelope, type: external_exports.literal("journal.baseline"), node_ids: external_exports.array(external_exports.string()) }).catchall(external_exports.unknown());
|
|
15172
|
+
var QualityAuditCompletedEventSchema = external_exports.object({
|
|
15173
|
+
...envelope,
|
|
15174
|
+
type: external_exports.literal("quality.audit.completed"),
|
|
15175
|
+
audit_id: external_exports.string(),
|
|
15176
|
+
framework_version: external_exports.string(),
|
|
15177
|
+
commit: external_exports.string().optional(),
|
|
15178
|
+
scores: external_exports.record(external_exports.string(), external_exports.record(external_exports.string(), external_exports.number())).optional(),
|
|
15179
|
+
counts: external_exports.object({
|
|
15180
|
+
critical: external_exports.number().optional(),
|
|
15181
|
+
high: external_exports.number().optional(),
|
|
15182
|
+
medium: external_exports.number().optional(),
|
|
15183
|
+
low: external_exports.number().optional(),
|
|
15184
|
+
info: external_exports.number().optional()
|
|
15185
|
+
}).catchall(external_exports.unknown()).optional()
|
|
15186
|
+
}).catchall(external_exports.unknown());
|
|
15187
|
+
var QualityFindingOpenedEventSchema = external_exports.object({
|
|
15188
|
+
...envelope,
|
|
15189
|
+
type: external_exports.literal("quality.finding.opened"),
|
|
15190
|
+
finding_id: external_exports.string(),
|
|
15191
|
+
criterion_id: external_exports.string(),
|
|
15192
|
+
surface: external_exports.string(),
|
|
15193
|
+
severity: external_exports.string(),
|
|
15194
|
+
priority: external_exports.string(),
|
|
15195
|
+
title: external_exports.string(),
|
|
15196
|
+
node_ids: external_exports.array(external_exports.string()).optional(),
|
|
15197
|
+
issue_url: external_exports.string().optional()
|
|
15198
|
+
}).catchall(external_exports.unknown());
|
|
15199
|
+
var QualityFindingResolvedEventSchema = external_exports.object({
|
|
15200
|
+
...envelope,
|
|
15201
|
+
type: external_exports.literal("quality.finding.resolved"),
|
|
15202
|
+
finding_id: external_exports.string(),
|
|
15203
|
+
resolved_by: external_exports.string().optional(),
|
|
15204
|
+
node_ids: external_exports.array(external_exports.string()).optional()
|
|
15205
|
+
}).catchall(external_exports.unknown());
|
|
15206
|
+
var QualitySignalTrippedEventSchema = external_exports.object({
|
|
15207
|
+
...envelope,
|
|
15208
|
+
type: external_exports.literal("quality.signal.tripped"),
|
|
15209
|
+
criterion_id: external_exports.string(),
|
|
15210
|
+
surface: external_exports.string(),
|
|
15211
|
+
signal: external_exports.string(),
|
|
15212
|
+
detail: external_exports.string().optional()
|
|
15213
|
+
}).catchall(external_exports.unknown());
|
|
15214
|
+
var JOURNAL_EVENT_SCHEMAS = {
|
|
15215
|
+
"node.created": NodeCreatedEventSchema,
|
|
15216
|
+
"node.updated": NodeUpdatedEventSchema,
|
|
15217
|
+
"node.status_changed": NodeStatusChangedEventSchema,
|
|
15218
|
+
"decision.status_changed": DecisionStatusChangedEventSchema,
|
|
15219
|
+
"node.deleted": NodeDeletedEventSchema,
|
|
15220
|
+
"edge.added": EdgeAddedEventSchema,
|
|
15221
|
+
"edge.removed": EdgeRemovedEventSchema,
|
|
15222
|
+
"release.tagged": ReleaseTaggedEventSchema,
|
|
15223
|
+
"deliverable.shipped": DeliverableShippedEventSchema,
|
|
15224
|
+
"idea.proposed": IdeaProposedEventSchema,
|
|
15225
|
+
"request.filed": RequestFiledEventSchema,
|
|
15226
|
+
"ref.added": RefAddedEventSchema,
|
|
15227
|
+
"ref.removed": RefRemovedEventSchema,
|
|
15228
|
+
"ref.status_changed": RefStatusChangedEventSchema,
|
|
15229
|
+
"journal.baseline": JournalBaselineEventSchema,
|
|
15230
|
+
"quality.audit.completed": QualityAuditCompletedEventSchema,
|
|
15231
|
+
"quality.finding.opened": QualityFindingOpenedEventSchema,
|
|
15232
|
+
"quality.finding.resolved": QualityFindingResolvedEventSchema,
|
|
15233
|
+
"quality.signal.tripped": QualitySignalTrippedEventSchema
|
|
15234
|
+
};
|
|
14952
15235
|
var KnownJournalEventSchema = external_exports.union([
|
|
14953
15236
|
NodeCreatedEventSchema,
|
|
14954
15237
|
NodeUpdatedEventSchema,
|
|
14955
15238
|
NodeStatusChangedEventSchema,
|
|
15239
|
+
DecisionStatusChangedEventSchema,
|
|
14956
15240
|
NodeDeletedEventSchema,
|
|
14957
15241
|
EdgeAddedEventSchema,
|
|
14958
15242
|
EdgeRemovedEventSchema,
|
|
14959
15243
|
ReleaseTaggedEventSchema,
|
|
15244
|
+
DeliverableShippedEventSchema,
|
|
14960
15245
|
IdeaProposedEventSchema,
|
|
14961
15246
|
RequestFiledEventSchema,
|
|
14962
15247
|
RefAddedEventSchema,
|
|
14963
15248
|
RefRemovedEventSchema,
|
|
14964
|
-
RefStatusChangedEventSchema
|
|
15249
|
+
RefStatusChangedEventSchema,
|
|
15250
|
+
JournalBaselineEventSchema,
|
|
15251
|
+
QualityAuditCompletedEventSchema,
|
|
15252
|
+
QualityFindingOpenedEventSchema,
|
|
15253
|
+
QualityFindingResolvedEventSchema,
|
|
15254
|
+
QualitySignalTrippedEventSchema
|
|
14965
15255
|
]);
|
|
14966
15256
|
|
|
15257
|
+
// ../schema/src/quality-schemas.ts
|
|
15258
|
+
var GradeSchema = external_exports.enum(["A", "B", "C", "D", "E"]);
|
|
15259
|
+
var KritikScalesSchema = external_exports.object({
|
|
15260
|
+
grades: external_exports.partialRecord(GradeSchema, external_exports.number()).optional(),
|
|
15261
|
+
severity_buckets: external_exports.partialRecord(external_exports.enum(["critical", "high", "medium", "low", "info"]), external_exports.tuple([external_exports.number(), external_exports.number()])).optional(),
|
|
15262
|
+
caps: external_exports.object({ critical_open: GradeSchema.optional(), high_open: GradeSchema.optional() }).catchall(external_exports.unknown()).optional()
|
|
15263
|
+
}).catchall(external_exports.unknown()).meta({
|
|
15264
|
+
id: "KritikScales",
|
|
15265
|
+
description: "Overridable maturity/severity/grade scales carried by a criteria pack. Absent entries fall back to the framework defaults (packages/kritik-library/SPEC.md \xA7 4)."
|
|
15266
|
+
});
|
|
15267
|
+
var KritikReferenceSchema = external_exports.object({ title: external_exports.string(), anchor: external_exports.string().optional(), url: external_exports.string().optional() }).catchall(external_exports.unknown()).meta({ id: "KritikReference", description: "A stable external anchor behind a criterion (OWASP control, GDPR article, WCAG SC\u2026)." });
|
|
15268
|
+
var KritikIssueTemplateSchema = external_exports.object({
|
|
15269
|
+
title_template: external_exports.string().optional(),
|
|
15270
|
+
labels: external_exports.array(external_exports.string()).optional(),
|
|
15271
|
+
body_skeleton: external_exports.string().optional()
|
|
15272
|
+
}).catchall(external_exports.unknown()).meta({ id: "KritikIssueTemplate", description: "Per-criterion issue skeleton \u2014 what `arkaik kritik issue` prefills." });
|
|
15273
|
+
var KritikDomainSchema = external_exports.object({ code: external_exports.string(), name: external_exports.string(), description: external_exports.string().optional() }).catchall(external_exports.unknown()).meta({ id: "KritikDomain", description: "A top-level criteria category owning a roll-up row in the matrix." });
|
|
15274
|
+
var KritikCriterionSchema = external_exports.object({
|
|
15275
|
+
id: external_exports.string().meta({ description: "Stable identifier, `<DOMAIN>-NN`. Never redefined \u2014 retired via superseded_by." }),
|
|
15276
|
+
domain: external_exports.string(),
|
|
15277
|
+
subcategory: external_exports.string().optional(),
|
|
15278
|
+
name: external_exports.string().optional(),
|
|
15279
|
+
question: external_exports.string().optional(),
|
|
15280
|
+
definition: external_exports.string().optional(),
|
|
15281
|
+
rationale: external_exports.string().optional(),
|
|
15282
|
+
applies_to: external_exports.array(external_exports.string()).optional().meta({ description: "Surface ids where the criterion is meaningful; absence means N/A." }),
|
|
15283
|
+
level_anchors: external_exports.record(external_exports.string(), external_exports.string()).optional().meta({ description: "Observable descriptions per maturity level, keyed l0\u2026l4." }),
|
|
15284
|
+
default_impact: external_exports.number().optional(),
|
|
15285
|
+
weight: external_exports.number().optional().meta({ description: "Weight in the domain roll-up (1\u20133; 3 = load-bearing). Absent means 1." }),
|
|
15286
|
+
references: external_exports.array(KritikReferenceSchema).optional(),
|
|
15287
|
+
checklist: external_exports.array(external_exports.string()).optional(),
|
|
15288
|
+
signals: external_exports.array(external_exports.string()).optional().meta({ description: "Mechanically checkable monitoring hooks run between audits." }),
|
|
15289
|
+
remediation: external_exports.string().optional(),
|
|
15290
|
+
issue: KritikIssueTemplateSchema.optional(),
|
|
15291
|
+
superseded_by: external_exports.string().optional()
|
|
15292
|
+
}).catchall(external_exports.unknown()).meta({ id: "KritikCriterion", description: "The atomic auditable unit (packages/kritik-library/SPEC.md \xA7 5)." });
|
|
15293
|
+
var KritikLibrarySchema = external_exports.object({
|
|
15294
|
+
version: external_exports.string().meta({ description: "Semver of the pack; matrices are comparable only within a major." }),
|
|
15295
|
+
domains: external_exports.array(KritikDomainSchema),
|
|
15296
|
+
criteria: external_exports.array(KritikCriterionSchema),
|
|
15297
|
+
scales: KritikScalesSchema.optional()
|
|
15298
|
+
}).catchall(external_exports.unknown()).meta({
|
|
15299
|
+
id: "KritikLibrary",
|
|
15300
|
+
description: "A versioned criteria pack. Embedded on export; repos may instead pin it as a sidecar file."
|
|
15301
|
+
});
|
|
15302
|
+
var SurfaceDefSchema = external_exports.object({
|
|
15303
|
+
id: external_exports.string(),
|
|
15304
|
+
title: external_exports.string(),
|
|
15305
|
+
platform: PlatformSchema.optional().meta({
|
|
15306
|
+
description: "Optional bridge to PLATFORM_IDS where a surface is also a view-shipping platform. Surfaces are not platforms (RFC \xA7 2)."
|
|
15307
|
+
}),
|
|
15308
|
+
path: external_exports.string().optional()
|
|
15309
|
+
}).catchall(external_exports.unknown()).meta({ id: "SurfaceDef", description: "One independently assessable body of code \u2014 an audit target, chosen at install." });
|
|
15310
|
+
var QualityProfileSchema = external_exports.object({
|
|
15311
|
+
surfaces: external_exports.array(SurfaceDefSchema),
|
|
15312
|
+
domain_weights: external_exports.record(external_exports.string(), external_exports.number()).optional().meta({
|
|
15313
|
+
description: "Per-domain weight in the surface roll-up, keyed by domain code. A missing weight is 1."
|
|
15314
|
+
})
|
|
15315
|
+
}).catchall(external_exports.unknown()).meta({ id: "QualityProfile", description: "The project's parameterization of the pack (RFC \xA7 6)." });
|
|
15316
|
+
var QualityAssessmentSchema = external_exports.object({
|
|
15317
|
+
criterion_id: external_exports.string(),
|
|
15318
|
+
surface: external_exports.string(),
|
|
15319
|
+
level: external_exports.union([external_exports.literal(0), external_exports.literal(1), external_exports.literal(2), external_exports.literal(3), external_exports.literal(4)]).meta({
|
|
15320
|
+
description: "Maturity 0\u20134. N/A is the absence of the row, never a level."
|
|
15321
|
+
}),
|
|
15322
|
+
evidence: external_exports.string().meta({ description: "file:line / config citations. A score without evidence is an opinion, not an assessment." }),
|
|
15323
|
+
audit_id: external_exports.string(),
|
|
15324
|
+
commit: external_exports.string().optional(),
|
|
15325
|
+
ts: external_exports.string()
|
|
15326
|
+
}).catchall(external_exports.unknown()).meta({ id: "QualityAssessment", description: "One (criterion \xD7 surface) score \u2014 latest wins; history lives in the journal." });
|
|
15327
|
+
var QualityFindingSchema = external_exports.object({
|
|
15328
|
+
id: external_exports.string(),
|
|
15329
|
+
criterion_id: external_exports.string(),
|
|
15330
|
+
surface: external_exports.string(),
|
|
15331
|
+
title: external_exports.string(),
|
|
15332
|
+
detail: external_exports.string(),
|
|
15333
|
+
evidence: external_exports.string(),
|
|
15334
|
+
impact: external_exports.number().meta({ description: "Worst plausible consequence, 1\u20135." }),
|
|
15335
|
+
likelihood: external_exports.number().meta({ description: "Probability it materializes, 1\u20135." }),
|
|
15336
|
+
cost: external_exports.enum(["S", "M", "L", "XL"]),
|
|
15337
|
+
status: external_exports.enum(["open", "resolved", "refuted", "accepted-risk"]),
|
|
15338
|
+
remediation: external_exports.string().optional(),
|
|
15339
|
+
node_ids: external_exports.array(external_exports.string()).optional().meta({ description: "Graph nodes this finding is about \u2014 the tie deliverable.shipped already uses." }),
|
|
15340
|
+
issue_url: external_exports.string().optional(),
|
|
15341
|
+
verification: external_exports.object({ verdict: external_exports.enum(["CONFIRMED", "REFUTED", "DOWNGRADED"]), note: external_exports.string().optional() }).catchall(external_exports.unknown()).optional().meta({ description: "Result of the adversarial refutation pass. Refuted findings are disclosed, not deleted." })
|
|
15342
|
+
}).catchall(external_exports.unknown()).meta({
|
|
15343
|
+
id: "QualityFinding",
|
|
15344
|
+
description: "One concrete defect. Severity and priority are deliberately absent \u2014 they are derived from impact \xD7 likelihood and cost (deriveQualityMatrix), so a finding can never carry a severity its own numbers disagree with."
|
|
15345
|
+
});
|
|
15346
|
+
var QualitySectionSchema = external_exports.object({
|
|
15347
|
+
framework_version: external_exports.string(),
|
|
15348
|
+
library: KritikLibrarySchema.optional(),
|
|
15349
|
+
profile: QualityProfileSchema,
|
|
15350
|
+
assessments: external_exports.array(QualityAssessmentSchema),
|
|
15351
|
+
findings: external_exports.array(QualityFindingSchema)
|
|
15352
|
+
}).catchall(external_exports.unknown()).meta({
|
|
15353
|
+
id: "QualitySection",
|
|
15354
|
+
description: "The project's Kritik state (docs/rfcs/kritik.md \xA7 4.1) \u2014 profile, assessments, findings. Criteria are library content, not graph nodes; the matrix is a projection, never stored."
|
|
15355
|
+
});
|
|
15356
|
+
|
|
14967
15357
|
// ../schema/src/bundle.ts
|
|
14968
15358
|
var PlatformStatusMapSchema = external_exports.partialRecord(
|
|
14969
15359
|
PlatformSchema,
|
|
14970
|
-
|
|
15360
|
+
// legacy-tolerant: migrateStatusVocabulary normalizes on load
|
|
15361
|
+
AnyStatusSchema
|
|
14971
15362
|
).meta({ id: "PlatformStatusMap", description: "Per-platform status overrides for view nodes." });
|
|
14972
15363
|
var PlatformNotesMapSchema = external_exports.partialRecord(
|
|
14973
15364
|
PlatformSchema,
|
|
@@ -14990,7 +15381,8 @@ var RefSchema = external_exports.object({
|
|
|
14990
15381
|
external_status: external_exports.string().optional().meta({
|
|
14991
15382
|
description: 'Mirrored external state, verbatim (e.g. "open", "merged", "In Progress").'
|
|
14992
15383
|
}),
|
|
14993
|
-
|
|
15384
|
+
// legacy-tolerant: migrateStatusVocabulary normalizes on load
|
|
15385
|
+
status_mapped: AnyStatusSchema.optional().meta({
|
|
14994
15386
|
description: "Optional mapping of external_status into the arkaik lifecycle. Advisory display data \u2014 never mutates node.status."
|
|
14995
15387
|
}),
|
|
14996
15388
|
platform: PlatformSchema.optional().meta({ description: "Optional scoping to one platform variant." }),
|
|
@@ -14998,6 +15390,9 @@ var RefSchema = external_exports.object({
|
|
|
14998
15390
|
}).meta({ id: "Ref", description: "A typed external reference on a node (docs/spec/bundle-format.md \xA7 References)." });
|
|
14999
15391
|
var NodeMetadataSchema = external_exports.object({
|
|
15000
15392
|
stage: external_exports.string().optional(),
|
|
15393
|
+
blocked_by: external_exports.string().optional().meta({
|
|
15394
|
+
description: "Non-empty = blocked at the current status. A node id (rendered as a link) or free text naming the dependency."
|
|
15395
|
+
}),
|
|
15001
15396
|
playlist: FlowPlaylistSchema.optional(),
|
|
15002
15397
|
platformNotes: PlatformNotesMapSchema.optional(),
|
|
15003
15398
|
platformStatuses: PlatformStatusMapSchema.optional(),
|
|
@@ -15008,6 +15403,21 @@ var NodeMetadataSchema = external_exports.object({
|
|
|
15008
15403
|
}),
|
|
15009
15404
|
values: external_exports.array(ValueSchema).optional().meta({
|
|
15010
15405
|
description: "Acceptance nodes only: 1..n Bain value elements served (the Why)."
|
|
15406
|
+
}),
|
|
15407
|
+
product: external_exports.string().optional().meta({
|
|
15408
|
+
description: "Product membership (docs/spec/bundle-format.md \xA7 Products); flow, view, and acceptance only."
|
|
15409
|
+
}),
|
|
15410
|
+
decision_status: DecisionStatusSchema.optional().meta({
|
|
15411
|
+
description: "Decision nodes only: proposed | approved | enacted | rejected | deprecated | superseded. The node's lifecycle status is kept in sync (spec \xA72)."
|
|
15412
|
+
}),
|
|
15413
|
+
context: external_exports.string().optional().meta({
|
|
15414
|
+
description: "Decision nodes only: Context \u2014 the Why (markdown)."
|
|
15415
|
+
}),
|
|
15416
|
+
consequences: external_exports.string().optional().meta({
|
|
15417
|
+
description: "Decision nodes only: Consequences \u2014 the How (markdown)."
|
|
15418
|
+
}),
|
|
15419
|
+
decided_at: external_exports.string().optional().meta({
|
|
15420
|
+
description: "Decision nodes only: ISO 8601 date the decision was made."
|
|
15011
15421
|
})
|
|
15012
15422
|
}).catchall(external_exports.unknown()).meta({ id: "NodeMetadata", description: "Optional metadata for a node." });
|
|
15013
15423
|
var NodeSchema = external_exports.object({
|
|
@@ -15016,7 +15426,8 @@ var NodeSchema = external_exports.object({
|
|
|
15016
15426
|
species: SpeciesSchema,
|
|
15017
15427
|
title: external_exports.string().meta({ description: "Human-readable node title." }),
|
|
15018
15428
|
description: external_exports.string().optional().meta({ description: "Optional description of the node's purpose." }),
|
|
15019
|
-
|
|
15429
|
+
// legacy-tolerant: migrateStatusVocabulary normalizes on load
|
|
15430
|
+
status: AnyStatusSchema,
|
|
15020
15431
|
platforms: external_exports.array(PlatformSchema).meta({ description: "One or more target platforms." }),
|
|
15021
15432
|
metadata: NodeMetadataSchema.optional()
|
|
15022
15433
|
}).meta({ id: "Node" });
|
|
@@ -15028,6 +15439,18 @@ var EdgeSchema = external_exports.object({
|
|
|
15028
15439
|
edge_type: EdgeTypeSchema,
|
|
15029
15440
|
metadata: external_exports.record(external_exports.string(), external_exports.unknown()).optional().meta({ description: "Optional edge metadata." })
|
|
15030
15441
|
}).meta({ id: "Edge" });
|
|
15442
|
+
var MapDisplayOptionsSchema = external_exports.object({
|
|
15443
|
+
images: external_exports.boolean().optional().meta({ description: "Screenshot (or cover) art on view cards." }),
|
|
15444
|
+
flow_platforms: external_exports.string().optional().meta({
|
|
15445
|
+
description: "A flow card's platform delivery: rings (default) | bars."
|
|
15446
|
+
}),
|
|
15447
|
+
view_platforms: external_exports.string().optional().meta({
|
|
15448
|
+
description: "A view card's platform availability: chips (default) | rows."
|
|
15449
|
+
}),
|
|
15450
|
+
minimap_color: external_exports.string().optional().meta({
|
|
15451
|
+
description: "What a minimap node's fill encodes: status (default) | species."
|
|
15452
|
+
})
|
|
15453
|
+
}).catchall(external_exports.unknown()).meta({ id: "MapDisplayOptions", description: "How a map draws its cards (docs/spec/maps.md \xA7 Display Options)." });
|
|
15031
15454
|
var MapDefinitionSchema = external_exports.object({
|
|
15032
15455
|
id: external_exports.string().meta({
|
|
15033
15456
|
description: "Kebab-case, unique within the project; built-in ids (journey, system) are reserved."
|
|
@@ -15046,14 +15469,34 @@ var MapDefinitionSchema = external_exports.object({
|
|
|
15046
15469
|
root_node_id: external_exports.string().optional().meta({
|
|
15047
15470
|
description: "Scope anchor: the subgraph is the undirected neighborhood reachable from this node."
|
|
15048
15471
|
}),
|
|
15472
|
+
product: external_exports.string().optional().meta({ description: "Product scope; absent = every product." }),
|
|
15049
15473
|
depth: external_exports.number().optional().meta({ description: "Traversal bound from the root; absent = unbounded." }),
|
|
15050
|
-
layout: external_exports.object({ direction: external_exports.string().optional() }).catchall(external_exports.unknown()).optional().meta({ description: "Renderer layout hints (e.g. direction: DOWN | RIGHT)." })
|
|
15474
|
+
layout: external_exports.object({ direction: external_exports.string().optional() }).catchall(external_exports.unknown()).optional().meta({ description: "Renderer layout hints (e.g. direction: DOWN | RIGHT)." }),
|
|
15475
|
+
display: MapDisplayOptionsSchema.optional().meta({
|
|
15476
|
+
description: "Card rendering; the human twin is project.metadata.map_display[id]."
|
|
15477
|
+
})
|
|
15051
15478
|
}).catchall(external_exports.unknown()).meta({ id: "MapDefinition", description: "A stored map definition (docs/spec/maps.md \xA7 MapDefinition)." });
|
|
15479
|
+
var ProductDefinitionSchema = external_exports.object({
|
|
15480
|
+
id: external_exports.string().meta({ description: "Kebab-case, unique within the project." }),
|
|
15481
|
+
title: external_exports.string().meta({ description: "Display title." }),
|
|
15482
|
+
description: external_exports.string().optional().meta({ description: "What this product is." }),
|
|
15483
|
+
platforms: external_exports.array(PlatformSchema).meta({
|
|
15484
|
+
description: "The platforms this product can ship on; empty means availability is not tracked."
|
|
15485
|
+
}),
|
|
15486
|
+
root_node_id: external_exports.string().optional().meta({ description: "This product's journey anchor." })
|
|
15487
|
+
}).catchall(external_exports.unknown()).meta({ id: "ProductDefinition", description: "A product definition (docs/spec/bundle-format.md \xA7 Products)." });
|
|
15052
15488
|
var ProjectMetadataSchema = external_exports.object({
|
|
15053
15489
|
view_card_variant: external_exports.enum(["compact", "large"]).optional(),
|
|
15054
15490
|
maps: external_exports.array(MapDefinitionSchema).optional().meta({
|
|
15055
15491
|
description: "Stored map definitions (docs/spec/maps.md \xA7 Storage) \u2014 additive; unknown fields preserved."
|
|
15056
|
-
})
|
|
15492
|
+
}),
|
|
15493
|
+
map_display: external_exports.record(external_exports.string(), MapDisplayOptionsSchema).optional().meta({
|
|
15494
|
+
description: "Per-map display overrides keyed by map id (docs/spec/maps.md \xA7 Display Options) \u2014 the only path open to the built-in maps."
|
|
15495
|
+
}),
|
|
15496
|
+
products: external_exports.array(ProductDefinitionSchema).optional().meta({
|
|
15497
|
+
description: "Product definitions (docs/spec/bundle-format.md \xA7 Products) \u2014 additive; unknown fields preserved."
|
|
15498
|
+
}),
|
|
15499
|
+
pollen: external_exports.object({ plant: external_exports.string().regex(/^[a-z0-9][a-z0-9-]*$/).optional() }).catchall(external_exports.unknown()).optional().meta({ description: "Federation settings \u2014 docs/spec/services.md \xA7 Pollen feed. `plant` enables the feed." })
|
|
15057
15500
|
}).catchall(external_exports.unknown()).meta({ id: "ProjectMetadata", description: "Optional project-level UI settings." });
|
|
15058
15501
|
var ProjectSchema = external_exports.object({
|
|
15059
15502
|
id: external_exports.string().meta({ description: "Unique project identifier." }),
|
|
@@ -15081,6 +15524,9 @@ var ProjectBundleSchema = external_exports.object({
|
|
|
15081
15524
|
edges: external_exports.array(EdgeSchema).meta({ description: "All edges (relationships) between nodes." }),
|
|
15082
15525
|
journal: external_exports.array(JournalEventSchema).optional().meta({
|
|
15083
15526
|
description: "Optional embedded journal events \u2014 the Level 2 interchange projection (docs/spec/journal.md). Canonical storage in repos is the JSONL sidecar; a bundle without a journal is Level 0/1, not an error."
|
|
15527
|
+
}),
|
|
15528
|
+
quality: QualitySectionSchema.optional().meta({
|
|
15529
|
+
description: "Optional Kritik quality state (docs/rfcs/kritik.md \xA7 4.1) \u2014 the pinned pack version, the project's surface profile, its assessments and findings. Additive: a bundle without it is not an error, and the comparative matrix is derived from it (deriveQualityMatrix), never stored."
|
|
15084
15530
|
})
|
|
15085
15531
|
}).catchall(external_exports.unknown()).meta({
|
|
15086
15532
|
id: "ProjectBundle",
|
|
@@ -15088,12 +15534,55 @@ var ProjectBundleSchema = external_exports.object({
|
|
|
15088
15534
|
description: "The import/export format for an Arkaik product graph. Contains a project, its nodes (flows, views, data models, API endpoints), and edges (relationships between nodes)."
|
|
15089
15535
|
});
|
|
15090
15536
|
|
|
15537
|
+
// ../schema/src/products.ts
|
|
15538
|
+
var PRODUCT_MEMBERSHIP_SPECIES = ["flow", "view", "acceptance"];
|
|
15539
|
+
function resolveProducts(project) {
|
|
15540
|
+
const stored = project?.metadata?.products;
|
|
15541
|
+
if (!Array.isArray(stored)) return [];
|
|
15542
|
+
const seen = /* @__PURE__ */ new Set();
|
|
15543
|
+
const products = [];
|
|
15544
|
+
for (const entry of stored) {
|
|
15545
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) continue;
|
|
15546
|
+
const candidate = entry;
|
|
15547
|
+
if (typeof candidate.id !== "string" || candidate.id.trim() === "" || seen.has(candidate.id)) continue;
|
|
15548
|
+
seen.add(candidate.id);
|
|
15549
|
+
products.push(candidate);
|
|
15550
|
+
}
|
|
15551
|
+
return products;
|
|
15552
|
+
}
|
|
15553
|
+
|
|
15091
15554
|
// ../schema/src/maps.ts
|
|
15555
|
+
var MAP_FLOW_PLATFORMS_MODES = ["rings", "bars"];
|
|
15556
|
+
var MAP_VIEW_PLATFORMS_MODES = ["chips", "rows"];
|
|
15557
|
+
var MAP_MINIMAP_COLOR_MODES = ["status", "species"];
|
|
15092
15558
|
var BUILT_IN_MAP_IDS = ["journey", "system"];
|
|
15093
15559
|
function isBuiltInMapId(id) {
|
|
15094
15560
|
return BUILT_IN_MAP_IDS.includes(id);
|
|
15095
15561
|
}
|
|
15096
15562
|
|
|
15563
|
+
// ../schema/src/quality.ts
|
|
15564
|
+
var asArray = (value) => Array.isArray(value) ? value : [];
|
|
15565
|
+
var CROSS_SURFACE_ID = "cross-surface";
|
|
15566
|
+
function mergeKritikLibrary(pack, overlay) {
|
|
15567
|
+
if (!overlay || typeof overlay !== "object") return pack;
|
|
15568
|
+
const domains = [...asArray(pack.domains)];
|
|
15569
|
+
for (const domain2 of asArray(overlay.domains)) {
|
|
15570
|
+
if (typeof domain2?.code !== "string" || domain2.code === "") continue;
|
|
15571
|
+
const at = domains.findIndex((existing) => existing?.code === domain2.code);
|
|
15572
|
+
if (at >= 0) domains[at] = domain2;
|
|
15573
|
+
else domains.push(domain2);
|
|
15574
|
+
}
|
|
15575
|
+
const criteria = [...asArray(pack.criteria)];
|
|
15576
|
+
for (const criterion of asArray(overlay.criteria)) {
|
|
15577
|
+
if (typeof criterion?.id !== "string" || criterion.id === "") continue;
|
|
15578
|
+
const at = criteria.findIndex((existing) => existing?.id === criterion.id);
|
|
15579
|
+
if (at >= 0) criteria[at] = criterion;
|
|
15580
|
+
else criteria.push(criterion);
|
|
15581
|
+
}
|
|
15582
|
+
const scales = pack.scales || overlay.scales ? { ...pack.scales ?? {}, ...overlay.scales ?? {} } : void 0;
|
|
15583
|
+
return { ...pack, domains, criteria, ...scales ? { scales } : {} };
|
|
15584
|
+
}
|
|
15585
|
+
|
|
15097
15586
|
// ../schema/src/validate.ts
|
|
15098
15587
|
var VALID_STAGES = ["beta", "monitoring", "deprecated"];
|
|
15099
15588
|
var VALID_VIEW_CARD_VARIANTS = ["compact", "large"];
|
|
@@ -15105,25 +15594,6 @@ function estimateDataUriBytes(dataUri) {
|
|
|
15105
15594
|
const padding = payload.endsWith("==") ? 2 : payload.endsWith("=") ? 1 : 0;
|
|
15106
15595
|
return Math.floor(payload.length * 3 / 4) - padding;
|
|
15107
15596
|
}
|
|
15108
|
-
var VALID_EDGE_SEMANTICS = {
|
|
15109
|
-
composes: [
|
|
15110
|
-
["flow", "view"],
|
|
15111
|
-
["flow", "flow"],
|
|
15112
|
-
["view", "flow"],
|
|
15113
|
-
["view", "view"]
|
|
15114
|
-
],
|
|
15115
|
-
calls: [
|
|
15116
|
-
["view", "api-endpoint"],
|
|
15117
|
-
["flow", "api-endpoint"],
|
|
15118
|
-
["api-endpoint", "api-endpoint"]
|
|
15119
|
-
],
|
|
15120
|
-
displays: [["view", "data-model"]],
|
|
15121
|
-
queries: [["api-endpoint", "data-model"]],
|
|
15122
|
-
covers: [
|
|
15123
|
-
["acceptance", "view"],
|
|
15124
|
-
["acceptance", "flow"]
|
|
15125
|
-
]
|
|
15126
|
-
};
|
|
15127
15597
|
function isIsoDate(value) {
|
|
15128
15598
|
return typeof value === "string" && !Number.isNaN(Date.parse(value));
|
|
15129
15599
|
}
|
|
@@ -15219,7 +15689,9 @@ function validateBundle(input) {
|
|
|
15219
15689
|
}
|
|
15220
15690
|
const platforms = node.platforms;
|
|
15221
15691
|
if (!platforms || platforms.length === 0) {
|
|
15222
|
-
|
|
15692
|
+
if (species !== "decision") {
|
|
15693
|
+
error51(`${base}.platforms`, "platforms-non-empty", `Node ${nodeId}: platforms array is empty or missing`);
|
|
15694
|
+
}
|
|
15223
15695
|
} else {
|
|
15224
15696
|
for (const p of platforms) {
|
|
15225
15697
|
if (!PLATFORM_IDS.includes(p)) {
|
|
@@ -15341,6 +15813,25 @@ function validateBundle(input) {
|
|
|
15341
15813
|
}
|
|
15342
15814
|
});
|
|
15343
15815
|
}
|
|
15816
|
+
const decisionStatus = md.decision_status;
|
|
15817
|
+
if (decisionStatus !== void 0 && species !== "decision") {
|
|
15818
|
+
warn(
|
|
15819
|
+
`${base}.metadata.decision_status`,
|
|
15820
|
+
"decision-status-wrong-species",
|
|
15821
|
+
`decision_status is meaningful on decision nodes only; "${nodeId}" is a ${species}.`
|
|
15822
|
+
);
|
|
15823
|
+
}
|
|
15824
|
+
if (species === "decision") {
|
|
15825
|
+
const effective = decisionStatusOf({ metadata: node.metadata });
|
|
15826
|
+
const expected = lifecycleStatusForDecision(effective);
|
|
15827
|
+
if (node.status !== expected) {
|
|
15828
|
+
warn(
|
|
15829
|
+
`${base}.status`,
|
|
15830
|
+
"decision-lifecycle-mismatch",
|
|
15831
|
+
`Decision "${nodeId}" is ${effective}, whose lifecycle status should be "${expected}", but status is "${node.status}" (spec \xA72).`
|
|
15832
|
+
);
|
|
15833
|
+
}
|
|
15834
|
+
}
|
|
15344
15835
|
if (species === "flow") {
|
|
15345
15836
|
const playlist = md.playlist;
|
|
15346
15837
|
if (!node.metadata || !playlist || !playlist.entries) {
|
|
@@ -15358,6 +15849,38 @@ function validateBundle(input) {
|
|
|
15358
15849
|
`project.root_node_id "${rootNodeId}" does not reference an existing node`
|
|
15359
15850
|
);
|
|
15360
15851
|
}
|
|
15852
|
+
const checkMapDisplay = (display, path, subject) => {
|
|
15853
|
+
if (typeof display !== "object" || display === null || Array.isArray(display)) return;
|
|
15854
|
+
const options = display;
|
|
15855
|
+
const modes = [
|
|
15856
|
+
["flow_platforms", MAP_FLOW_PLATFORMS_MODES],
|
|
15857
|
+
["view_platforms", MAP_VIEW_PLATFORMS_MODES],
|
|
15858
|
+
["minimap_color", MAP_MINIMAP_COLOR_MODES]
|
|
15859
|
+
];
|
|
15860
|
+
for (const [key, allowed] of modes) {
|
|
15861
|
+
const value = options[key];
|
|
15862
|
+
if (value !== void 0 && (typeof value !== "string" || !allowed.includes(value))) {
|
|
15863
|
+
warn(
|
|
15864
|
+
`${path}.${key}`,
|
|
15865
|
+
"map-unknown-display",
|
|
15866
|
+
`${subject} sets ${key} to "${String(value)}"; expected one of ${allowed.join(", ")} (renderers fall back to the default)`
|
|
15867
|
+
);
|
|
15868
|
+
}
|
|
15869
|
+
}
|
|
15870
|
+
if (options.images !== void 0 && typeof options.images !== "boolean") {
|
|
15871
|
+
warn(
|
|
15872
|
+
`${path}.images`,
|
|
15873
|
+
"map-unknown-display",
|
|
15874
|
+
`${subject} sets images to a non-boolean value (renderers fall back to the default)`
|
|
15875
|
+
);
|
|
15876
|
+
}
|
|
15877
|
+
};
|
|
15878
|
+
const mapDisplayOverrides = projectMetadata?.map_display;
|
|
15879
|
+
if (typeof mapDisplayOverrides === "object" && mapDisplayOverrides !== null && !Array.isArray(mapDisplayOverrides)) {
|
|
15880
|
+
for (const [mapId, display] of Object.entries(mapDisplayOverrides)) {
|
|
15881
|
+
checkMapDisplay(display, `project.metadata.map_display.${mapId}`, `Map "${mapId}"`);
|
|
15882
|
+
}
|
|
15883
|
+
}
|
|
15361
15884
|
const storedMaps = projectMetadata?.maps;
|
|
15362
15885
|
if (Array.isArray(storedMaps)) {
|
|
15363
15886
|
const seenMapIds = /* @__PURE__ */ new Set();
|
|
@@ -15405,8 +15928,122 @@ function validateBundle(input) {
|
|
|
15405
15928
|
}
|
|
15406
15929
|
});
|
|
15407
15930
|
}
|
|
15931
|
+
checkMapDisplay(map2.display, `${path}.display`, `Map "${mapId ?? index}"`);
|
|
15932
|
+
});
|
|
15933
|
+
}
|
|
15934
|
+
const storedProducts = projectMetadata?.products;
|
|
15935
|
+
const declaredProductIds = /* @__PURE__ */ new Set();
|
|
15936
|
+
if (Array.isArray(storedProducts)) {
|
|
15937
|
+
const seenProductIds = /* @__PURE__ */ new Set();
|
|
15938
|
+
storedProducts.forEach((definition, index) => {
|
|
15939
|
+
if (typeof definition !== "object" || definition === null || Array.isArray(definition)) return;
|
|
15940
|
+
const product = definition;
|
|
15941
|
+
const path = `project.metadata.products[${index}]`;
|
|
15942
|
+
const productId = typeof product.id === "string" ? product.id : void 0;
|
|
15943
|
+
if (productId === void 0) return;
|
|
15944
|
+
if (seenProductIds.has(productId)) {
|
|
15945
|
+
warn(`${path}.id`, "product-duplicate-id", `Duplicate product id "${productId}" \u2014 the first wins`);
|
|
15946
|
+
} else {
|
|
15947
|
+
seenProductIds.add(productId);
|
|
15948
|
+
if (productId.trim() !== "") declaredProductIds.add(productId);
|
|
15949
|
+
}
|
|
15950
|
+
if (!/^[a-z0-9]+(-[a-z0-9]+)*$/.test(productId)) {
|
|
15951
|
+
warn(`${path}.id`, "product-invalid-id", `Product id "${productId}" is not kebab-case`);
|
|
15952
|
+
}
|
|
15408
15953
|
});
|
|
15409
15954
|
}
|
|
15955
|
+
const hasProducts = declaredProductIds.size > 0;
|
|
15956
|
+
const anchorsByAcceptance = /* @__PURE__ */ new Map();
|
|
15957
|
+
for (const edge of edges) {
|
|
15958
|
+
if (edge.edge_type !== "covers") continue;
|
|
15959
|
+
const source = typeof edge.source_id === "string" ? edge.source_id : void 0;
|
|
15960
|
+
const target = typeof edge.target_id === "string" ? edge.target_id : void 0;
|
|
15961
|
+
if (source === void 0 || target === void 0) continue;
|
|
15962
|
+
const list = anchorsByAcceptance.get(source) ?? [];
|
|
15963
|
+
list.push(target);
|
|
15964
|
+
anchorsByAcceptance.set(source, list);
|
|
15965
|
+
}
|
|
15966
|
+
const menuByProduct = /* @__PURE__ */ new Map();
|
|
15967
|
+
if (hasProducts) {
|
|
15968
|
+
for (const definition of resolveProducts({ metadata: projectMetadata })) {
|
|
15969
|
+
menuByProduct.set(
|
|
15970
|
+
definition.id,
|
|
15971
|
+
new Set(Array.isArray(definition.platforms) ? definition.platforms : [])
|
|
15972
|
+
);
|
|
15973
|
+
}
|
|
15974
|
+
}
|
|
15975
|
+
const productByNodeId = /* @__PURE__ */ new Map();
|
|
15976
|
+
const indexByNodeId = /* @__PURE__ */ new Map();
|
|
15977
|
+
nodes.forEach((node, index) => {
|
|
15978
|
+
const nodeId = typeof node.id === "string" ? node.id : `#${index}`;
|
|
15979
|
+
const species = node.species;
|
|
15980
|
+
const base = `nodes[${index}]`;
|
|
15981
|
+
indexByNodeId.set(nodeId, index);
|
|
15982
|
+
const metadata = node.metadata ?? {};
|
|
15983
|
+
const membership = typeof metadata.product === "string" ? metadata.product : void 0;
|
|
15984
|
+
const storesMembership = species !== void 0 && PRODUCT_MEMBERSHIP_SPECIES.includes(species);
|
|
15985
|
+
if (membership !== void 0 && !storesMembership) {
|
|
15986
|
+
const detail = SPECIES_IDS.includes(species) ? `${species} membership is derived from consumers and must not be stored` : "metadata.product is only meaningful on flow, view, and acceptance nodes";
|
|
15987
|
+
warn(`${base}.metadata.product`, "product-membership-wrong-species", `Node ${nodeId}: ${detail}`);
|
|
15988
|
+
return;
|
|
15989
|
+
}
|
|
15990
|
+
if (!storesMembership) return;
|
|
15991
|
+
if (membership === void 0) {
|
|
15992
|
+
if (!hasProducts) return;
|
|
15993
|
+
if (species === "acceptance") {
|
|
15994
|
+
if (!anchorsByAcceptance.has(nodeId)) {
|
|
15995
|
+
warn(
|
|
15996
|
+
`${base}.metadata.product`,
|
|
15997
|
+
"acceptance-product-unassigned",
|
|
15998
|
+
`Acceptance ${nodeId} covers nothing and names no product \u2014 it will show only under "All products"`
|
|
15999
|
+
);
|
|
16000
|
+
}
|
|
16001
|
+
} else {
|
|
16002
|
+
warn(
|
|
16003
|
+
`${base}.metadata.product`,
|
|
16004
|
+
"unassigned-membership",
|
|
16005
|
+
`Node ${nodeId}: no product membership \u2014 it will show only under "All products"`
|
|
16006
|
+
);
|
|
16007
|
+
}
|
|
16008
|
+
return;
|
|
16009
|
+
}
|
|
16010
|
+
productByNodeId.set(nodeId, membership);
|
|
16011
|
+
if (!declaredProductIds.has(membership)) {
|
|
16012
|
+
warn(
|
|
16013
|
+
`${base}.metadata.product`,
|
|
16014
|
+
"product-unknown-reference",
|
|
16015
|
+
`Node ${nodeId}: product "${membership}" is not declared on the project`
|
|
16016
|
+
);
|
|
16017
|
+
return;
|
|
16018
|
+
}
|
|
16019
|
+
const menu = menuByProduct.get(membership);
|
|
16020
|
+
const nodePlatforms = Array.isArray(node.platforms) ? node.platforms : [];
|
|
16021
|
+
if (menu) {
|
|
16022
|
+
for (const platform of nodePlatforms) {
|
|
16023
|
+
if (typeof platform === "string" && !menu.has(platform)) {
|
|
16024
|
+
warn(
|
|
16025
|
+
`${base}.platforms`,
|
|
16026
|
+
"product-platform-not-in-menu",
|
|
16027
|
+
`Node ${nodeId}: platform "${platform}" is not in product "${membership}"'s menu`
|
|
16028
|
+
);
|
|
16029
|
+
}
|
|
16030
|
+
}
|
|
16031
|
+
}
|
|
16032
|
+
});
|
|
16033
|
+
if (hasProducts) {
|
|
16034
|
+
for (const [acceptanceId, anchors] of anchorsByAcceptance) {
|
|
16035
|
+
const acceptanceIndex = indexByNodeId.get(acceptanceId);
|
|
16036
|
+
if (acceptanceIndex === void 0) continue;
|
|
16037
|
+
const spanned = new Set(anchors.map((id) => productByNodeId.get(id)).filter((id) => Boolean(id)));
|
|
16038
|
+
if (spanned.size > 1) {
|
|
16039
|
+
warn(
|
|
16040
|
+
`nodes[${acceptanceIndex}].metadata.product`,
|
|
16041
|
+
"acceptance-covers-span-products",
|
|
16042
|
+
`Acceptance ${acceptanceId} covers anchors in ${[...spanned].sort().join(" and ")} \u2014 statuses may conflate products`
|
|
16043
|
+
);
|
|
16044
|
+
}
|
|
16045
|
+
}
|
|
16046
|
+
}
|
|
15410
16047
|
const edgeIds = /* @__PURE__ */ new Set();
|
|
15411
16048
|
const edgeSignatures = /* @__PURE__ */ new Set();
|
|
15412
16049
|
const composesSet = /* @__PURE__ */ new Set();
|
|
@@ -15462,13 +16099,98 @@ function validateBundle(input) {
|
|
|
15462
16099
|
composesSet.add(`${sourceId}->${targetId}`);
|
|
15463
16100
|
}
|
|
15464
16101
|
});
|
|
16102
|
+
const PLAYLIST_ENTRY_TYPES = ["view", "flow", "condition", "junction"];
|
|
16103
|
+
const checkPlaylistShape = (entries, flowId, path, depth = 0) => {
|
|
16104
|
+
if (depth > 50) return;
|
|
16105
|
+
if (!Array.isArray(entries)) {
|
|
16106
|
+
error51(path, "playlist-entry-shape", `Flow ${flowId}: ${path} must be an array of playlist entries`);
|
|
16107
|
+
return;
|
|
16108
|
+
}
|
|
16109
|
+
entries.forEach((raw, i) => {
|
|
16110
|
+
const p = `${path}[${i}]`;
|
|
16111
|
+
if (typeof raw !== "object" || raw === null || Array.isArray(raw)) {
|
|
16112
|
+
error51(p, "playlist-entry-shape", `Flow ${flowId}: ${p} must be a playlist entry object`);
|
|
16113
|
+
return;
|
|
16114
|
+
}
|
|
16115
|
+
const entry = raw;
|
|
16116
|
+
const type = entry.type;
|
|
16117
|
+
if (typeof type !== "string" || !PLAYLIST_ENTRY_TYPES.includes(type)) {
|
|
16118
|
+
error51(
|
|
16119
|
+
`${p}.type`,
|
|
16120
|
+
"playlist-entry-shape",
|
|
16121
|
+
`Flow ${flowId}: ${p}.type must be one of ${PLAYLIST_ENTRY_TYPES.join(", ")} (got ${JSON.stringify(type)})`
|
|
16122
|
+
);
|
|
16123
|
+
return;
|
|
16124
|
+
}
|
|
16125
|
+
if (type === "view") {
|
|
16126
|
+
if (typeof entry.view_id !== "string") {
|
|
16127
|
+
error51(`${p}.view_id`, "playlist-entry-shape", `Flow ${flowId}: ${p} is a view entry with no view_id string`);
|
|
16128
|
+
}
|
|
16129
|
+
} else if (type === "flow") {
|
|
16130
|
+
if (typeof entry.flow_id !== "string") {
|
|
16131
|
+
error51(`${p}.flow_id`, "playlist-entry-shape", `Flow ${flowId}: ${p} is a flow entry with no flow_id string`);
|
|
16132
|
+
}
|
|
16133
|
+
} else if (type === "condition") {
|
|
16134
|
+
if (typeof entry.label !== "string") {
|
|
16135
|
+
error51(`${p}.label`, "playlist-entry-shape", `Flow ${flowId}: ${p} is a condition with no label string`);
|
|
16136
|
+
}
|
|
16137
|
+
for (const branch of ["if_true", "if_false"]) {
|
|
16138
|
+
if (!Array.isArray(entry[branch])) {
|
|
16139
|
+
error51(
|
|
16140
|
+
`${p}.${branch}`,
|
|
16141
|
+
"playlist-entry-shape",
|
|
16142
|
+
`Flow ${flowId}: ${p}.${branch} must be an array of playlist entries (both branches are required, use [] for an empty one)`
|
|
16143
|
+
);
|
|
16144
|
+
} else {
|
|
16145
|
+
checkPlaylistShape(entry[branch], flowId, `${p}.${branch}`, depth + 1);
|
|
16146
|
+
}
|
|
16147
|
+
}
|
|
16148
|
+
} else {
|
|
16149
|
+
if (typeof entry.label !== "string") {
|
|
16150
|
+
error51(`${p}.label`, "playlist-entry-shape", `Flow ${flowId}: ${p} is a junction with no label string`);
|
|
16151
|
+
}
|
|
16152
|
+
if (!Array.isArray(entry.cases)) {
|
|
16153
|
+
error51(`${p}.cases`, "playlist-entry-shape", `Flow ${flowId}: ${p}.cases must be an array of junction cases`);
|
|
16154
|
+
return;
|
|
16155
|
+
}
|
|
16156
|
+
entry.cases.forEach((rawCase, j) => {
|
|
16157
|
+
const cp = `${p}.cases[${j}]`;
|
|
16158
|
+
if (typeof rawCase !== "object" || rawCase === null || Array.isArray(rawCase)) {
|
|
16159
|
+
error51(cp, "playlist-entry-shape", `Flow ${flowId}: ${cp} must be a junction case object`);
|
|
16160
|
+
return;
|
|
16161
|
+
}
|
|
16162
|
+
const branch = rawCase;
|
|
16163
|
+
if (typeof branch.label !== "string") {
|
|
16164
|
+
error51(`${cp}.label`, "playlist-entry-shape", `Flow ${flowId}: ${cp} has no label string`);
|
|
16165
|
+
}
|
|
16166
|
+
if (!Array.isArray(branch.entries)) {
|
|
16167
|
+
error51(
|
|
16168
|
+
`${cp}.entries`,
|
|
16169
|
+
"playlist-entry-shape",
|
|
16170
|
+
`Flow ${flowId}: ${cp}.entries must be an array of playlist entries \u2014 a junction case holds its own entries, not a bare view_id/flow_id`
|
|
16171
|
+
);
|
|
16172
|
+
} else {
|
|
16173
|
+
checkPlaylistShape(branch.entries, flowId, `${cp}.entries`, depth + 1);
|
|
16174
|
+
}
|
|
16175
|
+
});
|
|
16176
|
+
}
|
|
16177
|
+
});
|
|
16178
|
+
};
|
|
16179
|
+
nodes.forEach((node, i) => {
|
|
16180
|
+
const md = node.metadata;
|
|
16181
|
+
if (node.species === "flow" && md?.playlist?.entries) {
|
|
16182
|
+
checkPlaylistShape(md.playlist.entries, node.id, `nodes[${i}].metadata.playlist.entries`);
|
|
16183
|
+
}
|
|
16184
|
+
});
|
|
15465
16185
|
const collectPlaylistRefs = (entries, flowId, path, depth = 0) => {
|
|
15466
16186
|
if (depth > 50) {
|
|
15467
16187
|
error51(path, "playlist-depth", `Flow ${flowId}: playlist nesting too deep (possible cycle)`);
|
|
15468
16188
|
return [];
|
|
15469
16189
|
}
|
|
15470
16190
|
const refs = [];
|
|
16191
|
+
if (!Array.isArray(entries)) return refs;
|
|
15471
16192
|
for (const entry of entries) {
|
|
16193
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
15472
16194
|
if (entry.type === "view") {
|
|
15473
16195
|
if (!nodeIds.has(entry.view_id)) {
|
|
15474
16196
|
error51(path, "playlist-ref-exists", `Flow ${flowId}: playlist references non-existent view "${entry.view_id}"`);
|
|
@@ -15520,7 +16242,9 @@ function validateBundle(input) {
|
|
|
15520
16242
|
const nodeId = node.id;
|
|
15521
16243
|
const subFlows = [];
|
|
15522
16244
|
const findSubFlows = (entries) => {
|
|
16245
|
+
if (!Array.isArray(entries)) return;
|
|
15523
16246
|
for (const e of entries) {
|
|
16247
|
+
if (typeof e !== "object" || e === null) continue;
|
|
15524
16248
|
if (e.type === "flow") subFlows.push(e.flow_id);
|
|
15525
16249
|
if (e.type === "condition") {
|
|
15526
16250
|
if (e.if_true) findSubFlows(e.if_true);
|
|
@@ -15555,6 +16279,151 @@ function validateBundle(input) {
|
|
|
15555
16279
|
for (const id of flowGraph.keys()) {
|
|
15556
16280
|
dfs(id);
|
|
15557
16281
|
}
|
|
16282
|
+
const quality = bundle.quality;
|
|
16283
|
+
if (typeof quality === "object" && quality !== null && !Array.isArray(quality)) {
|
|
16284
|
+
const section = quality;
|
|
16285
|
+
const assessments = Array.isArray(section.assessments) ? section.assessments : [];
|
|
16286
|
+
const qualityFindings = Array.isArray(section.findings) ? section.findings : [];
|
|
16287
|
+
const profile = typeof section.profile === "object" && section.profile !== null ? section.profile : void 0;
|
|
16288
|
+
const declaredSurfaces = /* @__PURE__ */ new Set();
|
|
16289
|
+
if (Array.isArray(profile?.surfaces)) {
|
|
16290
|
+
profile.surfaces.forEach((entry, index) => {
|
|
16291
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return;
|
|
16292
|
+
const surface = entry;
|
|
16293
|
+
const surfaceId = typeof surface.id === "string" ? surface.id : void 0;
|
|
16294
|
+
if (surfaceId === void 0 || surfaceId.trim() === "") return;
|
|
16295
|
+
if (declaredSurfaces.has(surfaceId)) {
|
|
16296
|
+
warn(`quality.profile.surfaces[${index}].id`, "quality-duplicate-surface", `Duplicate surface id "${surfaceId}" \u2014 the first wins`);
|
|
16297
|
+
}
|
|
16298
|
+
declaredSurfaces.add(surfaceId);
|
|
16299
|
+
});
|
|
16300
|
+
}
|
|
16301
|
+
if (declaredSurfaces.size === 0 && (assessments.length > 0 || qualityFindings.length > 0)) {
|
|
16302
|
+
warn(
|
|
16303
|
+
"quality.profile.surfaces",
|
|
16304
|
+
"quality-no-surfaces",
|
|
16305
|
+
"Quality data is stored but the profile declares no surfaces \u2014 the matrix falls back to the surfaces the assessments name"
|
|
16306
|
+
);
|
|
16307
|
+
}
|
|
16308
|
+
const library = typeof section.library === "object" && section.library !== null ? section.library : void 0;
|
|
16309
|
+
const knownCriteria = /* @__PURE__ */ new Set();
|
|
16310
|
+
const retiredCriteria = /* @__PURE__ */ new Map();
|
|
16311
|
+
if (Array.isArray(library?.criteria)) {
|
|
16312
|
+
for (const entry of library.criteria) {
|
|
16313
|
+
if (typeof entry !== "object" || entry === null) continue;
|
|
16314
|
+
const criterion = entry;
|
|
16315
|
+
if (typeof criterion.id !== "string") continue;
|
|
16316
|
+
knownCriteria.add(criterion.id);
|
|
16317
|
+
if (typeof criterion.superseded_by === "string" && criterion.superseded_by !== "") {
|
|
16318
|
+
retiredCriteria.set(criterion.id, criterion.superseded_by);
|
|
16319
|
+
}
|
|
16320
|
+
}
|
|
16321
|
+
} else if (assessments.length > 0 || qualityFindings.length > 0) {
|
|
16322
|
+
warn(
|
|
16323
|
+
"quality.library",
|
|
16324
|
+
"quality-library-missing",
|
|
16325
|
+
"No criteria pack is embedded \u2014 criterion ids cannot be resolved to domains or weights; the matrix uses a synthesized library"
|
|
16326
|
+
);
|
|
16327
|
+
}
|
|
16328
|
+
if (typeof section.framework_version !== "string" || section.framework_version === "") {
|
|
16329
|
+
warn("quality.framework_version", "quality-framework-version-missing", "Quality section has no framework_version \u2014 scores are not comparable across audits without one");
|
|
16330
|
+
}
|
|
16331
|
+
const checkRowRefs = (row, path, label) => {
|
|
16332
|
+
const criterionId = typeof row.criterion_id === "string" ? row.criterion_id : void 0;
|
|
16333
|
+
if (criterionId !== void 0 && knownCriteria.size > 0 && !knownCriteria.has(criterionId)) {
|
|
16334
|
+
warn(`${path}.criterion_id`, "quality-unknown-criterion", `${label}: criterion "${criterionId}" is not in the pinned library`);
|
|
16335
|
+
}
|
|
16336
|
+
const supersededBy = criterionId !== void 0 ? retiredCriteria.get(criterionId) : void 0;
|
|
16337
|
+
if (supersededBy !== void 0) {
|
|
16338
|
+
warn(`${path}.criterion_id`, "quality-retired-criterion", `${label}: criterion "${criterionId}" is retired \u2014 superseded by "${supersededBy}"`);
|
|
16339
|
+
}
|
|
16340
|
+
const surface = typeof row.surface === "string" ? row.surface : void 0;
|
|
16341
|
+
if (surface !== void 0 && surface !== CROSS_SURFACE_ID && declaredSurfaces.size > 0 && !declaredSurfaces.has(surface)) {
|
|
16342
|
+
warn(`${path}.surface`, "quality-unknown-surface", `${label}: surface "${surface}" is not declared in the profile`);
|
|
16343
|
+
}
|
|
16344
|
+
};
|
|
16345
|
+
const seenCells = /* @__PURE__ */ new Set();
|
|
16346
|
+
assessments.forEach((entry, index) => {
|
|
16347
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return;
|
|
16348
|
+
const assessment = entry;
|
|
16349
|
+
const path = `quality.assessments[${index}]`;
|
|
16350
|
+
const label = `Assessment ${index}`;
|
|
16351
|
+
checkRowRefs(assessment, path, label);
|
|
16352
|
+
const level = assessment.level;
|
|
16353
|
+
if (typeof level !== "number" || !Number.isInteger(level) || level < 0 || level > 4) {
|
|
16354
|
+
warn(`${path}.level`, "quality-level-range", `${label}: level must be an integer 0-4 (N/A is the absence of the row, not a level)`);
|
|
16355
|
+
}
|
|
16356
|
+
if (assessment.surface === CROSS_SURFACE_ID) {
|
|
16357
|
+
warn(`${path}.surface`, "quality-cross-surface-assessment", `${label}: "${CROSS_SURFACE_ID}" is a findings-only lens \u2014 it carries no matrix column, so this score would render nowhere`);
|
|
16358
|
+
}
|
|
16359
|
+
if (typeof assessment.evidence !== "string" || assessment.evidence.trim() === "") {
|
|
16360
|
+
warn(`${path}.evidence`, "quality-assessment-no-evidence", `${label}: scored with no evidence \u2014 a score without a citation is an opinion, not an assessment`);
|
|
16361
|
+
}
|
|
16362
|
+
const criterionId = typeof assessment.criterion_id === "string" ? assessment.criterion_id : void 0;
|
|
16363
|
+
const surface = typeof assessment.surface === "string" ? assessment.surface : void 0;
|
|
16364
|
+
if (criterionId !== void 0 && surface !== void 0) {
|
|
16365
|
+
const cell = `${criterionId}\0${surface}`;
|
|
16366
|
+
if (seenCells.has(cell)) {
|
|
16367
|
+
warn(`${path}`, "quality-duplicate-assessment", `${label}: a second score for (${criterionId} x ${surface}) \u2014 the section stores latest-per-cell, so this cell is ambiguous`);
|
|
16368
|
+
}
|
|
16369
|
+
seenCells.add(cell);
|
|
16370
|
+
}
|
|
16371
|
+
});
|
|
16372
|
+
const seenFindingIds = /* @__PURE__ */ new Set();
|
|
16373
|
+
qualityFindings.forEach((entry, index) => {
|
|
16374
|
+
if (typeof entry !== "object" || entry === null || Array.isArray(entry)) return;
|
|
16375
|
+
const finding = entry;
|
|
16376
|
+
const path = `quality.findings[${index}]`;
|
|
16377
|
+
const label = `Finding ${index}`;
|
|
16378
|
+
checkRowRefs(finding, path, label);
|
|
16379
|
+
const findingId = typeof finding.id === "string" ? finding.id : void 0;
|
|
16380
|
+
if (findingId !== void 0) {
|
|
16381
|
+
if (seenFindingIds.has(findingId)) {
|
|
16382
|
+
warn(`${path}.id`, "quality-duplicate-finding-id", `Duplicate finding id "${findingId}"`);
|
|
16383
|
+
}
|
|
16384
|
+
seenFindingIds.add(findingId);
|
|
16385
|
+
}
|
|
16386
|
+
for (const field of ["impact", "likelihood"]) {
|
|
16387
|
+
const value = finding[field];
|
|
16388
|
+
if (typeof value !== "number" || !Number.isInteger(value) || value < 1 || value > 5) {
|
|
16389
|
+
warn(`${path}.${field}`, "quality-risk-range", `${label}: ${field} must be an integer 1-5 \u2014 severity is derived from impact x likelihood`);
|
|
16390
|
+
}
|
|
16391
|
+
}
|
|
16392
|
+
for (const derived of ["severity", "priority"]) {
|
|
16393
|
+
if (derived in finding) {
|
|
16394
|
+
warn(`${path}.${derived}`, "quality-derived-field-stored", `${label}: ${derived} is derived from impact, likelihood and cost \u2014 storing it lets it drift from the numbers behind it`);
|
|
16395
|
+
}
|
|
16396
|
+
}
|
|
16397
|
+
if (finding.status === "accepted-risk" && (typeof finding.detail !== "string" || finding.detail.trim() === "")) {
|
|
16398
|
+
warn(`${path}.detail`, "quality-accepted-risk-no-note", `${label}: accepted-risk with no note \u2014 an accepted risk is a decision and reads like one`);
|
|
16399
|
+
}
|
|
16400
|
+
});
|
|
16401
|
+
const openedInJournal = /* @__PURE__ */ new Set();
|
|
16402
|
+
const journalEvents = Array.isArray(bundle.journal) ? bundle.journal : [];
|
|
16403
|
+
journalEvents.forEach((entry) => {
|
|
16404
|
+
if (typeof entry !== "object" || entry === null) return;
|
|
16405
|
+
const event = entry;
|
|
16406
|
+
if (event.type === "quality.finding.opened" && typeof event.finding_id === "string") openedInJournal.add(event.finding_id);
|
|
16407
|
+
});
|
|
16408
|
+
journalEvents.forEach((entry, index) => {
|
|
16409
|
+
if (typeof entry !== "object" || entry === null) return;
|
|
16410
|
+
const event = entry;
|
|
16411
|
+
const type = typeof event.type === "string" ? event.type : "";
|
|
16412
|
+
if (!type.startsWith("quality.")) return;
|
|
16413
|
+
if (typeof event.actor !== "string" || event.actor.trim() === "") {
|
|
16414
|
+
warn(`journal[${index}].actor`, "quality-event-no-actor", `Journal event ${index}: ${type} has no actor \u2014 human, agent and CI scores become indistinguishable`);
|
|
16415
|
+
}
|
|
16416
|
+
if (type === "quality.finding.resolved" && typeof event.finding_id === "string") {
|
|
16417
|
+
if (!openedInJournal.has(event.finding_id) && !seenFindingIds.has(event.finding_id)) {
|
|
16418
|
+
warn(
|
|
16419
|
+
`journal[${index}].finding_id`,
|
|
16420
|
+
"quality-resolved-never-opened",
|
|
16421
|
+
`Journal event ${index}: resolves finding "${event.finding_id}", which no quality.finding.opened event or stored finding ever declared`
|
|
16422
|
+
);
|
|
16423
|
+
}
|
|
16424
|
+
}
|
|
16425
|
+
});
|
|
16426
|
+
}
|
|
15558
16427
|
for (const finding of crossCheckJournal(bundle)) findings.push(finding);
|
|
15559
16428
|
return result();
|
|
15560
16429
|
}
|
|
@@ -15562,8 +16431,89 @@ function validateBundle(input) {
|
|
|
15562
16431
|
// ../schema/src/emit.ts
|
|
15563
16432
|
var ENCODING = "0123456789ABCDEFGHJKMNPQRSTVWXYZ";
|
|
15564
16433
|
var ENCODING_LEN = ENCODING.length;
|
|
16434
|
+
var TIME_LEN = 10;
|
|
16435
|
+
var RANDOM_LEN = 16;
|
|
16436
|
+
function encodeTime(time3, len) {
|
|
16437
|
+
let out = "";
|
|
16438
|
+
let t = time3;
|
|
16439
|
+
for (let i = len - 1; i >= 0; i -= 1) {
|
|
16440
|
+
const mod = t % ENCODING_LEN;
|
|
16441
|
+
out = ENCODING[mod] + out;
|
|
16442
|
+
t = (t - mod) / ENCODING_LEN;
|
|
16443
|
+
}
|
|
16444
|
+
return out;
|
|
16445
|
+
}
|
|
16446
|
+
function randomSymbols(len) {
|
|
16447
|
+
const bytes = new Uint8Array(len);
|
|
16448
|
+
globalThis.crypto.getRandomValues(bytes);
|
|
16449
|
+
return Array.from(bytes, (b) => b & 31);
|
|
16450
|
+
}
|
|
16451
|
+
function incrementSymbols(symbols) {
|
|
16452
|
+
const out = symbols.slice();
|
|
16453
|
+
for (let i = out.length - 1; i >= 0; i -= 1) {
|
|
16454
|
+
if (out[i] < ENCODING_LEN - 1) {
|
|
16455
|
+
out[i] += 1;
|
|
16456
|
+
return out;
|
|
16457
|
+
}
|
|
16458
|
+
out[i] = 0;
|
|
16459
|
+
}
|
|
16460
|
+
return randomSymbols(RANDOM_LEN);
|
|
16461
|
+
}
|
|
16462
|
+
var lastTime = -1;
|
|
16463
|
+
var lastRandom = [];
|
|
16464
|
+
function ulid3(seedTime = Date.now()) {
|
|
16465
|
+
if (seedTime > lastTime) {
|
|
16466
|
+
lastTime = seedTime;
|
|
16467
|
+
lastRandom = randomSymbols(RANDOM_LEN);
|
|
16468
|
+
} else {
|
|
16469
|
+
lastRandom = incrementSymbols(lastRandom);
|
|
16470
|
+
}
|
|
16471
|
+
return encodeTime(lastTime, TIME_LEN) + lastRandom.map((s) => ENCODING[s]).join("");
|
|
16472
|
+
}
|
|
16473
|
+
function makeEvent(type, payload = {}, options = {}) {
|
|
16474
|
+
const ts = options.ts instanceof Date ? options.ts.toISOString() : options.ts ?? (/* @__PURE__ */ new Date()).toISOString();
|
|
16475
|
+
const id = options.id ?? ulid3();
|
|
16476
|
+
const event = {
|
|
16477
|
+
id,
|
|
16478
|
+
ts,
|
|
16479
|
+
...options.actor !== void 0 ? { actor: options.actor } : {},
|
|
16480
|
+
type,
|
|
16481
|
+
...payload
|
|
16482
|
+
};
|
|
16483
|
+
const schema = JOURNAL_EVENT_SCHEMAS[type] ?? JournalEventSchema;
|
|
16484
|
+
return schema.parse(event);
|
|
16485
|
+
}
|
|
16486
|
+
|
|
16487
|
+
// src/lib/bundle-io.ts
|
|
16488
|
+
function readBundle(filePath) {
|
|
16489
|
+
if (!existsSync(filePath)) throw new Error(`File not found: ${filePath}`);
|
|
16490
|
+
let parsed;
|
|
16491
|
+
try {
|
|
16492
|
+
parsed = JSON.parse(readFileSync(filePath, "utf8"));
|
|
16493
|
+
} catch (e) {
|
|
16494
|
+
throw new Error(`Cannot parse JSON \u2014 ${e.message}`);
|
|
16495
|
+
}
|
|
16496
|
+
if (typeof parsed !== "object" || parsed === null || Array.isArray(parsed)) {
|
|
16497
|
+
throw new Error("Bundle must be a JSON object.");
|
|
16498
|
+
}
|
|
16499
|
+
const record2 = parsed;
|
|
16500
|
+
if (!Array.isArray(record2.nodes)) return record2;
|
|
16501
|
+
return migrateStatusVocabulary(record2);
|
|
16502
|
+
}
|
|
16503
|
+
function nodesByIdOf(bundle) {
|
|
16504
|
+
const nodes = Array.isArray(bundle.nodes) ? bundle.nodes : [];
|
|
16505
|
+
const map2 = /* @__PURE__ */ new Map();
|
|
16506
|
+
for (const n of nodes) {
|
|
16507
|
+
if (n !== null && typeof n === "object" && typeof n.id === "string") {
|
|
16508
|
+
map2.set(n.id, n);
|
|
16509
|
+
}
|
|
16510
|
+
}
|
|
16511
|
+
return map2;
|
|
16512
|
+
}
|
|
15565
16513
|
|
|
15566
16514
|
// src/lib/journal-io.ts
|
|
16515
|
+
import { appendFileSync, existsSync as existsSync2, mkdirSync, readdirSync, readFileSync as readFileSync2, writeFileSync } from "node:fs";
|
|
16516
|
+
import { dirname, join } from "node:path";
|
|
15567
16517
|
var JOURNAL_SIDECAR = "journal.jsonl";
|
|
15568
16518
|
function journalPathFor(bundlePath) {
|
|
15569
16519
|
return join(dirname(bundlePath), JOURNAL_SIDECAR);
|
|
@@ -15571,10 +16521,22 @@ function journalPathFor(bundlePath) {
|
|
|
15571
16521
|
function archivePathFor(journalPath, version2) {
|
|
15572
16522
|
return join(dirname(journalPath), "journal", `archive-${version2}.jsonl`);
|
|
15573
16523
|
}
|
|
16524
|
+
function archivePathsFor(journalPath) {
|
|
16525
|
+
const dir = join(dirname(journalPath), "journal");
|
|
16526
|
+
if (!existsSync2(dir)) return [];
|
|
16527
|
+
return readdirSync(dir).filter((name) => name.startsWith("archive-") && name.endsWith(".jsonl")).sort().map((name) => join(dir, name));
|
|
16528
|
+
}
|
|
15574
16529
|
function readJournalEvents(journalPath) {
|
|
15575
16530
|
if (!existsSync2(journalPath)) return [];
|
|
15576
16531
|
return parseJournalLines(readFileSync2(journalPath, "utf8")).events;
|
|
15577
16532
|
}
|
|
16533
|
+
function readFullJournalEvents(journalPath) {
|
|
16534
|
+
const events = readJournalEvents(journalPath);
|
|
16535
|
+
for (const archivePath of archivePathsFor(journalPath)) {
|
|
16536
|
+
events.push(...readJournalEvents(archivePath));
|
|
16537
|
+
}
|
|
16538
|
+
return events;
|
|
16539
|
+
}
|
|
15578
16540
|
function loadJournalEvents(bundle, bundlePath) {
|
|
15579
16541
|
if (Array.isArray(bundle.journal)) return bundle.journal;
|
|
15580
16542
|
return readJournalEvents(journalPathFor(bundlePath));
|
|
@@ -15593,6 +16555,14 @@ function appendJournalEvent(journalPath, event) {
|
|
|
15593
16555
|
const prefix = existing.length > 0 && !existing.endsWith("\n") ? "\n" : "";
|
|
15594
16556
|
appendFileSync(journalPath, prefix + line);
|
|
15595
16557
|
}
|
|
16558
|
+
function ensureJournalBaseline(journalPath, bundle, actor) {
|
|
16559
|
+
const snapshotNodeIds = (Array.isArray(bundle.nodes) ? bundle.nodes : []).map((node) => node?.id).filter((id) => typeof id === "string");
|
|
16560
|
+
const missing = missingProvenanceNodeIds(snapshotNodeIds, readFullJournalEvents(journalPath));
|
|
16561
|
+
if (missing.length === 0) return void 0;
|
|
16562
|
+
const event = makeEvent("journal.baseline", { node_ids: missing }, { actor });
|
|
16563
|
+
appendJournalEvent(journalPath, event);
|
|
16564
|
+
return event;
|
|
16565
|
+
}
|
|
15596
16566
|
function compactSlice(journalPath, slice, version2) {
|
|
15597
16567
|
if (slice.length === 0) return;
|
|
15598
16568
|
const sliceIds = new Set(slice.map((ev) => ev.id));
|
|
@@ -15611,10 +16581,67 @@ function compactSlice(journalPath, slice, version2) {
|
|
|
15611
16581
|
writeFileSync(journalPath, surviving.map(toLine).join(""));
|
|
15612
16582
|
}
|
|
15613
16583
|
|
|
16584
|
+
// src/lib/kritik-io.ts
|
|
16585
|
+
import { existsSync as existsSync4 } from "node:fs";
|
|
16586
|
+
import { dirname as dirname3, join as join3 } from "node:path";
|
|
16587
|
+
import { fileURLToPath } from "node:url";
|
|
16588
|
+
|
|
16589
|
+
// ../schema/src/cli/kritik-paths.ts
|
|
16590
|
+
import { existsSync as existsSync3, mkdirSync as mkdirSync2, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "node:fs";
|
|
16591
|
+
import { dirname as dirname2, join as join2, resolve } from "node:path";
|
|
16592
|
+
var QUALITY_DIR = "docs/quality";
|
|
16593
|
+
var OVERLAY_FILE = "criteria.custom.json";
|
|
16594
|
+
var PACK_FILE = "library.json";
|
|
16595
|
+
function readJson(path) {
|
|
16596
|
+
return JSON.parse(readFileSync3(path, "utf8"));
|
|
16597
|
+
}
|
|
16598
|
+
var overlayPath = (root) => join2(root, QUALITY_DIR, OVERLAY_FILE);
|
|
16599
|
+
function loadOverlay(root) {
|
|
16600
|
+
const path = overlayPath(root);
|
|
16601
|
+
return existsSync3(path) ? readJson(path) : null;
|
|
16602
|
+
}
|
|
16603
|
+
|
|
16604
|
+
// src/lib/kritik-io.ts
|
|
16605
|
+
var KRITIK_ACTOR = "arkaik-cli";
|
|
16606
|
+
var DEFAULT_BUNDLE_PATH = join3("docs", "arkaik", "bundle.json");
|
|
16607
|
+
var VENDORED_PACK = join3(QUALITY_DIR, PACK_FILE);
|
|
16608
|
+
var BUNDLED_PACK = join3(dirname3(fileURLToPath(import.meta.url)), "assets", "kritik", "library.json");
|
|
16609
|
+
function resolvePack(root) {
|
|
16610
|
+
const vendored = join3(root, VENDORED_PACK);
|
|
16611
|
+
if (existsSync4(vendored)) return { library: readJson(vendored), path: vendored, vendored: true };
|
|
16612
|
+
if (!existsSync4(BUNDLED_PACK)) {
|
|
16613
|
+
throw new Error(
|
|
16614
|
+
`no criteria pack found. Looked in:
|
|
16615
|
+
${vendored}
|
|
16616
|
+
${BUNDLED_PACK}
|
|
16617
|
+
The second is shipped with this CLI, so its absence means a broken install \u2014 reinstall \`arkaik\`.`
|
|
16618
|
+
);
|
|
16619
|
+
}
|
|
16620
|
+
return { library: readJson(BUNDLED_PACK), path: BUNDLED_PACK, vendored: false };
|
|
16621
|
+
}
|
|
16622
|
+
function loadKritikLibrary(root) {
|
|
16623
|
+
const pack = resolvePack(root);
|
|
16624
|
+
return { library: mergeKritikLibrary(pack.library, loadOverlay(root)), pack };
|
|
16625
|
+
}
|
|
16626
|
+
function resolveJournal(root, bundlePath) {
|
|
16627
|
+
const resolved = bundlePath ?? join3(root, DEFAULT_BUNDLE_PATH);
|
|
16628
|
+
return { bundlePath: resolved, journalPath: journalPathFor(resolved), present: existsSync4(resolved) };
|
|
16629
|
+
}
|
|
16630
|
+
function appendQualityEvents(root, inputs, options = {}) {
|
|
16631
|
+
if (inputs.length === 0) return { events: [] };
|
|
16632
|
+
const actor = options.actor ?? KRITIK_ACTOR;
|
|
16633
|
+
const journal = resolveJournal(root, options.bundlePath);
|
|
16634
|
+
if (!journal.present) return { events: [] };
|
|
16635
|
+
const bundle = readBundle(journal.bundlePath);
|
|
16636
|
+
const baseline = ensureJournalBaseline(journal.journalPath, bundle, actor);
|
|
16637
|
+
const events = inputs.map((input) => makeEvent(input.type, input.payload, { actor }));
|
|
16638
|
+
for (const event of events) appendJournalEvent(journal.journalPath, event);
|
|
16639
|
+
return { journalPath: journal.journalPath, events, ...baseline !== void 0 ? { baseline } : {} };
|
|
16640
|
+
}
|
|
16641
|
+
|
|
15614
16642
|
// src/lib/bundle-validate.ts
|
|
15615
|
-
import { existsSync as
|
|
15616
|
-
import {
|
|
15617
|
-
var JOURNAL_SIDECAR2 = "journal.jsonl";
|
|
16643
|
+
import { existsSync as existsSync5, readFileSync as readFileSync4 } from "node:fs";
|
|
16644
|
+
import { basename } from "node:path";
|
|
15618
16645
|
function validateBundleAt(filePath) {
|
|
15619
16646
|
const bundle = readBundle(filePath);
|
|
15620
16647
|
const loose = bundle;
|
|
@@ -15623,31 +16650,69 @@ function validateBundleAt(filePath) {
|
|
|
15623
16650
|
}
|
|
15624
16651
|
let sidecarFindings = [];
|
|
15625
16652
|
let sidecarLoaded = false;
|
|
16653
|
+
const archiveFindings = [];
|
|
16654
|
+
const archivesLoaded = [];
|
|
15626
16655
|
if (loose.journal === void 0) {
|
|
15627
|
-
const sidecarPath =
|
|
15628
|
-
|
|
15629
|
-
|
|
16656
|
+
const sidecarPath = journalPathFor(filePath);
|
|
16657
|
+
const folded = [];
|
|
16658
|
+
if (existsSync5(sidecarPath)) {
|
|
16659
|
+
const { events, findings } = parseJournalLines(readFileSync4(sidecarPath, "utf8"));
|
|
15630
16660
|
sidecarFindings = findings;
|
|
15631
16661
|
sidecarLoaded = true;
|
|
15632
|
-
|
|
16662
|
+
folded.push(...events);
|
|
16663
|
+
}
|
|
16664
|
+
for (const archivePath of archivePathsFor(sidecarPath)) {
|
|
16665
|
+
const { events, findings } = parseJournalLines(readFileSync4(archivePath, "utf8"));
|
|
16666
|
+
const file2 = basename(archivePath);
|
|
16667
|
+
for (const finding of findings) archiveFindings.push({ ...finding, file: file2 });
|
|
16668
|
+
archivesLoaded.push(file2);
|
|
16669
|
+
folded.push(...events);
|
|
15633
16670
|
}
|
|
16671
|
+
if (sidecarLoaded || archivesLoaded.length > 0) loose.journal = folded;
|
|
15634
16672
|
}
|
|
15635
16673
|
const nodes = Array.isArray(loose.nodes) ? loose.nodes : [];
|
|
15636
16674
|
const edges = Array.isArray(loose.edges) ? loose.edges : [];
|
|
15637
16675
|
const journal = Array.isArray(loose.journal) ? loose.journal : [];
|
|
15638
16676
|
const result = validateBundle(bundle);
|
|
15639
|
-
const valid = sidecarFindings.length === 0 && result.errors.length === 0;
|
|
15640
|
-
return {
|
|
16677
|
+
const valid = sidecarFindings.length === 0 && archiveFindings.length === 0 && result.errors.length === 0;
|
|
16678
|
+
return {
|
|
16679
|
+
bundle,
|
|
16680
|
+
nodes,
|
|
16681
|
+
edges,
|
|
16682
|
+
journal,
|
|
16683
|
+
sidecarLoaded,
|
|
16684
|
+
sidecarFindings,
|
|
16685
|
+
archivesLoaded,
|
|
16686
|
+
archiveFindings,
|
|
16687
|
+
result,
|
|
16688
|
+
valid
|
|
16689
|
+
};
|
|
16690
|
+
}
|
|
16691
|
+
function journalLineErrorLines(v) {
|
|
16692
|
+
return [
|
|
16693
|
+
...v.sidecarFindings.map((f) => `ERROR [${f.rule}] line ${f.line}: ${f.message}`),
|
|
16694
|
+
...v.archiveFindings.map((f) => `ERROR [${f.rule}] ${f.file} line ${f.line}: ${f.message}`)
|
|
16695
|
+
];
|
|
15641
16696
|
}
|
|
15642
16697
|
export {
|
|
15643
16698
|
JOURNAL_SIDECAR,
|
|
16699
|
+
KRITIK_ACTOR,
|
|
16700
|
+
VENDORED_PACK,
|
|
15644
16701
|
appendJournalEvent,
|
|
16702
|
+
appendQualityEvents,
|
|
15645
16703
|
archivePathFor,
|
|
16704
|
+
archivePathsFor,
|
|
15646
16705
|
compactSlice,
|
|
16706
|
+
ensureJournalBaseline,
|
|
16707
|
+
journalLineErrorLines,
|
|
15647
16708
|
journalPathFor,
|
|
15648
16709
|
loadJournalEvents,
|
|
16710
|
+
loadKritikLibrary,
|
|
15649
16711
|
nodesByIdOf,
|
|
15650
16712
|
readBundle,
|
|
16713
|
+
readFullJournalEvents,
|
|
15651
16714
|
readJournalEvents,
|
|
16715
|
+
resolveJournal,
|
|
16716
|
+
resolvePack,
|
|
15652
16717
|
validateBundleAt
|
|
15653
16718
|
};
|