@fenglimg/fabric-shared 2.2.0-rc.1 → 2.2.0-rc.10
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chunk-2GLIAZ5M.js +251 -0
- package/dist/{chunk-JEXTOQVV.js → chunk-5AKCRBKJ.js} +541 -401
- package/dist/{chunk-7TZ2PMVH.js → chunk-AQMDXC6J.js} +471 -519
- package/dist/chunk-BDJQIOQO.js +206 -0
- package/dist/chunk-C7WZPYZE.js +129 -0
- package/dist/{chunk-3SZRB42B.js → chunk-O6GIHZF3.js} +14 -0
- package/dist/errors/index.d.ts +12 -1
- package/dist/errors/index.js +9 -3
- package/dist/i18n/index.d.ts +29 -23
- package/dist/i18n/index.js +7 -3
- package/dist/{index-J3Xn5h2J.d.ts → index-D_gT1CEA.d.ts} +108 -34
- package/dist/index.d.ts +829 -582
- package/dist/index.js +1495 -955
- package/dist/node/atomic-write.js +6 -106
- package/dist/node/mcp-payload-guard.js +1 -1
- package/dist/node.d.ts +10 -1
- package/dist/node.js +32 -1
- package/dist/schemas/api-contracts.d.ts +290 -118
- package/dist/schemas/api-contracts.js +3 -3
- package/dist/templates/bootstrap-canonical.d.ts +50 -23
- package/dist/templates/bootstrap-canonical.js +12 -9
- package/dist/types/index.d.ts +1 -1
- package/dist/types-qg4xXVuT.d.ts +8 -0
- package/package.json +3 -2
- package/dist/chunk-TX2XZ7AW.js +0 -102
package/dist/index.js
CHANGED
|
@@ -3,12 +3,14 @@ import {
|
|
|
3
3
|
PROTECTED_TOKENS,
|
|
4
4
|
createTranslator,
|
|
5
5
|
defaultMessages,
|
|
6
|
-
detectNodeLocale,
|
|
7
6
|
enMessages,
|
|
8
|
-
normalizeLocale,
|
|
9
7
|
resolveFabricLocale,
|
|
10
8
|
zhCNMessages
|
|
11
|
-
} from "./chunk-
|
|
9
|
+
} from "./chunk-AQMDXC6J.js";
|
|
10
|
+
import {
|
|
11
|
+
atomicWriteJson,
|
|
12
|
+
withFileLock
|
|
13
|
+
} from "./chunk-C7WZPYZE.js";
|
|
12
14
|
import {
|
|
13
15
|
FabExtractKnowledgeInputSchema,
|
|
14
16
|
FabExtractKnowledgeInputShape,
|
|
@@ -18,14 +20,17 @@ import {
|
|
|
18
20
|
FabReviewOutputSchema,
|
|
19
21
|
FabReviewOutputShape,
|
|
20
22
|
KNOWLEDGE_TYPE_CODES,
|
|
23
|
+
KNOWN_SCOPE_PREFIXES,
|
|
21
24
|
KnowledgeEntryFrontmatterSchema,
|
|
22
25
|
KnowledgeTypeSchema,
|
|
23
26
|
LayerSchema,
|
|
24
27
|
MaturitySchema,
|
|
25
28
|
ONBOARD_SLOT_NAMES,
|
|
26
29
|
ONBOARD_SLOT_TOTAL,
|
|
27
|
-
|
|
30
|
+
PERSONAL_SCOPE,
|
|
31
|
+
PROPOSED_REASON_DESCRIPTIONS_BY_LOCALE,
|
|
28
32
|
ProposedReasonSchema,
|
|
33
|
+
SCOPE_COORDINATE_PATTERN,
|
|
29
34
|
StableIdSchema,
|
|
30
35
|
annotateIntentRequestSchema,
|
|
31
36
|
archiveScanAnnotations,
|
|
@@ -34,12 +39,14 @@ import {
|
|
|
34
39
|
citeContractMetricsSchema,
|
|
35
40
|
citeCoverageReportSchema,
|
|
36
41
|
citeLayerTypeBreakdownSchema,
|
|
42
|
+
entryScopeMetadataSchema,
|
|
37
43
|
fabExtractKnowledgeAnnotations,
|
|
38
44
|
fabReviewAnnotations,
|
|
39
45
|
formatKnowledgeId,
|
|
40
46
|
historyStateQuerySchema,
|
|
41
47
|
humanLockApproveRequestSchema,
|
|
42
48
|
humanLockFileParamsSchema,
|
|
49
|
+
isPersonalScope,
|
|
43
50
|
knowledgeSectionsAnnotations,
|
|
44
51
|
knowledgeSectionsInputSchema,
|
|
45
52
|
knowledgeSectionsOutputSchema,
|
|
@@ -55,17 +62,55 @@ import {
|
|
|
55
62
|
recallAnnotations,
|
|
56
63
|
recallInputSchema,
|
|
57
64
|
recallOutputSchema,
|
|
65
|
+
scopeCoordinateSchema,
|
|
66
|
+
scopeRoot,
|
|
58
67
|
structuredWarningSchema
|
|
59
|
-
} from "./chunk-
|
|
68
|
+
} from "./chunk-5AKCRBKJ.js";
|
|
60
69
|
import {
|
|
61
|
-
|
|
70
|
+
BOOTSTRAP_CANONICAL_BY_LOCALE,
|
|
71
|
+
BOOTSTRAP_CANONICAL_EN,
|
|
72
|
+
BOOTSTRAP_CANONICAL_ZH,
|
|
62
73
|
BOOTSTRAP_MARKER_BEGIN,
|
|
63
74
|
BOOTSTRAP_MARKER_END,
|
|
64
75
|
BOOTSTRAP_REGEX,
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
matchBootstrapCanonicalLocale,
|
|
77
|
+
resolveBootstrapCanonical
|
|
78
|
+
} from "./chunk-BDJQIOQO.js";
|
|
79
|
+
import {
|
|
80
|
+
GLOBAL_BINDINGS_DIR,
|
|
81
|
+
GLOBAL_STATE_DIR,
|
|
82
|
+
PERSONAL_STORE_SENTINEL,
|
|
83
|
+
STORES_ROOT_DIR,
|
|
84
|
+
STORE_ALIAS_PATTERN,
|
|
85
|
+
STORE_KNOWLEDGE_TYPE_DIRS,
|
|
86
|
+
STORE_LAYOUT,
|
|
87
|
+
STORE_MOUNT_GROUPS,
|
|
88
|
+
STORE_MOUNT_NAME_PATTERN,
|
|
89
|
+
STORE_PROJECT_ID_PATTERN,
|
|
90
|
+
STORE_UUID_PATTERN,
|
|
91
|
+
deriveMountLabel,
|
|
92
|
+
detectNodeLocale,
|
|
93
|
+
globalConfigPath,
|
|
94
|
+
globalConfigSchema,
|
|
95
|
+
loadGlobalConfig,
|
|
96
|
+
mountedStoreSchema,
|
|
97
|
+
normalizeLocale,
|
|
98
|
+
requiredStoreEntrySchema,
|
|
99
|
+
resolveGlobalLocale,
|
|
100
|
+
resolveGlobalRoot,
|
|
101
|
+
saveGlobalConfig,
|
|
102
|
+
storeAliasSchema,
|
|
103
|
+
storeIdentitySchema,
|
|
104
|
+
storeKnowledgeTypeDir,
|
|
105
|
+
storeMountGroup,
|
|
106
|
+
storeMountNameSchema,
|
|
107
|
+
storeMountSubPath,
|
|
108
|
+
storeProjectSchema,
|
|
109
|
+
storeProjectsFileSchema,
|
|
110
|
+
storeRelativePath,
|
|
111
|
+
storeRelativePathForMount,
|
|
112
|
+
storeUuidSchema
|
|
113
|
+
} from "./chunk-2GLIAZ5M.js";
|
|
69
114
|
|
|
70
115
|
// src/schemas/agents-meta.ts
|
|
71
116
|
import { z } from "zod";
|
|
@@ -77,10 +122,8 @@ var KNOWLEDGE_TYPE_SINGULAR_TO_PLURAL = {
|
|
|
77
122
|
pitfall: "pitfalls",
|
|
78
123
|
process: "processes"
|
|
79
124
|
};
|
|
80
|
-
var AGENTS_META_LAYERS = ["L0", "L1", "L2"];
|
|
81
125
|
var AGENTS_META_TOPOLOGY_TYPES = ["mirror", "cross-cutting", "domain", "local", "global"];
|
|
82
126
|
var AGENTS_META_IDENTITY_SOURCES = ["declared", "derived"];
|
|
83
|
-
var agentsLayerSchema = z.enum(AGENTS_META_LAYERS);
|
|
84
127
|
var agentsTopologyTypeSchema = z.enum(AGENTS_META_TOPOLOGY_TYPES);
|
|
85
128
|
var agentsIdentitySourceSchema = z.enum(AGENTS_META_IDENTITY_SOURCES);
|
|
86
129
|
var ruleDescriptionSchema = z.object({
|
|
@@ -124,9 +167,6 @@ var ruleDescriptionSchema = z.object({
|
|
|
124
167
|
}).strict();
|
|
125
168
|
var ruleDescriptionIndexItemSchema = z.object({
|
|
126
169
|
stable_id: z.string(),
|
|
127
|
-
level: agentsLayerSchema,
|
|
128
|
-
required: z.boolean(),
|
|
129
|
-
selectable: z.boolean(),
|
|
130
170
|
description: ruleDescriptionSchema
|
|
131
171
|
}).strict();
|
|
132
172
|
var agentsMetaNodeBaseSchema = z.object({
|
|
@@ -136,10 +176,6 @@ var agentsMetaNodeBaseSchema = z.object({
|
|
|
136
176
|
hash: z.string(),
|
|
137
177
|
stable_id: z.string().optional(),
|
|
138
178
|
identity_source: agentsIdentitySourceSchema.optional(),
|
|
139
|
-
activation: z.object({
|
|
140
|
-
tier: z.enum(["always", "path", "description"]),
|
|
141
|
-
description: z.string().optional()
|
|
142
|
-
}).optional(),
|
|
143
179
|
description: ruleDescriptionSchema.optional(),
|
|
144
180
|
sections: z.array(z.string()).optional()
|
|
145
181
|
}).passthrough();
|
|
@@ -178,7 +214,6 @@ function withDerivedAgentsMetaNodeDefaults(node) {
|
|
|
178
214
|
const identitySource = isKnowledgeEntry ? "declared" : deriveAgentsMetaIdentitySource(node);
|
|
179
215
|
return {
|
|
180
216
|
...node,
|
|
181
|
-
level: node.level ?? deriveAgentsMetaLayer(node.file),
|
|
182
217
|
topology_type: node.topology_type ?? deriveAgentsMetaTopologyType(node.file),
|
|
183
218
|
stable_id: stableId,
|
|
184
219
|
identity_source: identitySource
|
|
@@ -222,34 +257,12 @@ function deriveAgentsMetaIdentitySource(node) {
|
|
|
222
257
|
const derivedStableId = deriveAgentsMetaStableId(node.file);
|
|
223
258
|
return node.stable_id !== void 0 && node.stable_id !== derivedStableId ? "declared" : "derived";
|
|
224
259
|
}
|
|
225
|
-
function deriveAgentsMetaLayer(file) {
|
|
226
|
-
const normalized = normalizePath(file);
|
|
227
|
-
if (normalized === "AGENTS.md") {
|
|
228
|
-
return "L0";
|
|
229
|
-
}
|
|
230
|
-
if (hasCrossCuttingSegment(normalized)) {
|
|
231
|
-
return "L1";
|
|
232
|
-
}
|
|
233
|
-
const depthSource = getDepthSource(normalized);
|
|
234
|
-
const directoryDepth = getDirectoryDepth(depthSource);
|
|
235
|
-
if (directoryDepth === 0) {
|
|
236
|
-
return "L0";
|
|
237
|
-
}
|
|
238
|
-
if (directoryDepth <= 2) {
|
|
239
|
-
return "L1";
|
|
240
|
-
}
|
|
241
|
-
return "L2";
|
|
242
|
-
}
|
|
243
260
|
function deriveAgentsMetaTopologyType(file) {
|
|
244
261
|
return hasCrossCuttingSegment(normalizePath(file)) ? "cross-cutting" : "mirror";
|
|
245
262
|
}
|
|
246
263
|
function getDepthSource(file) {
|
|
247
264
|
return file.startsWith(FABRIC_AGENTS_PREFIX) ? file.slice(FABRIC_AGENTS_PREFIX.length) : file;
|
|
248
265
|
}
|
|
249
|
-
function getDirectoryDepth(file) {
|
|
250
|
-
const segments = file.split("/").filter(Boolean);
|
|
251
|
-
return Math.max(segments.length - 1, 0);
|
|
252
|
-
}
|
|
253
266
|
function hasCrossCuttingSegment(file) {
|
|
254
267
|
return file.split("/").includes("_cross");
|
|
255
268
|
}
|
|
@@ -335,105 +348,35 @@ var humanLockFileSchema = z4.object({
|
|
|
335
348
|
});
|
|
336
349
|
|
|
337
350
|
// src/schemas/fabric-config.ts
|
|
338
|
-
import { z as z6 } from "zod";
|
|
339
|
-
|
|
340
|
-
// src/schemas/store.ts
|
|
341
351
|
import { z as z5 } from "zod";
|
|
342
|
-
var
|
|
343
|
-
var
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
store_uuid: storeUuidSchema,
|
|
348
|
-
// ISO-8601. When the store was first initialized.
|
|
349
|
-
created_at: z5.string(),
|
|
350
|
-
// Optional human-facing canonical alias baked into the store (e.g. the
|
|
351
|
-
// team picks "platform-kb"). Local per-machine aliases are resolved by the
|
|
352
|
-
// StoreResolver from config and may differ; this is the suggested default.
|
|
353
|
-
canonical_alias: z5.string().optional(),
|
|
354
|
-
// Optional one-line description surfaced in `store list` / onboarding.
|
|
355
|
-
description: z5.string().optional(),
|
|
356
|
-
// The semantic scopes this store is *allowed* to hold. A shared (team)
|
|
357
|
-
// store MUST NOT list "personal" (R5#3 privacy boundary, enforced at write
|
|
358
|
-
// time in P2). Open coordinate strings — see schemas/scope.ts.
|
|
359
|
-
allowed_scopes: z5.array(z5.string()).optional()
|
|
352
|
+
var auditModeSchema = z5.enum(["strict", "warn", "off"]);
|
|
353
|
+
var clientPathsSchema = z5.object({
|
|
354
|
+
claudeCodeCLI: z5.string().optional(),
|
|
355
|
+
claudeCodeDesktop: z5.string().optional(),
|
|
356
|
+
codexCLI: z5.string().optional()
|
|
360
357
|
}).strict();
|
|
361
|
-
var
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
}).
|
|
365
|
-
var
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
knowledgeDir: "knowledge",
|
|
375
|
-
bindingsDir: "bindings",
|
|
376
|
-
stateDir: "state"
|
|
377
|
-
};
|
|
378
|
-
var STORES_ROOT_DIR = "stores";
|
|
379
|
-
var GLOBAL_STATE_DIR = "state";
|
|
380
|
-
var GLOBAL_BINDINGS_DIR = "bindings";
|
|
381
|
-
function storeKnowledgeTypeDir(type) {
|
|
382
|
-
return `${STORE_LAYOUT.knowledgeDir}/${type}`;
|
|
383
|
-
}
|
|
384
|
-
function storeRelativePath(storeUuid) {
|
|
385
|
-
return `${STORES_ROOT_DIR}/${storeUuid}`;
|
|
386
|
-
}
|
|
387
|
-
var mountedStoreSchema = z5.object({
|
|
388
|
-
// Intrinsic identity of the mounted store (matches its store.json).
|
|
389
|
-
store_uuid: storeUuidSchema,
|
|
390
|
-
// Local per-machine alias the user references this store by (resolver maps
|
|
391
|
-
// alias → uuid). May differ from the store's canonical_alias.
|
|
392
|
-
alias: z5.string().min(1),
|
|
393
|
-
// Git remote locator for this clone, if any. Absent = local-only store
|
|
394
|
-
// (valid; doctor nudges to add a remote for backup — R5#5, P6).
|
|
395
|
-
remote: z5.string().min(1).optional(),
|
|
396
|
-
// v2.1.0-rc.1 P3: marks the implicit personal store (the one minted by
|
|
397
|
-
// `install --global`). Exactly one mounted store carries personal=true; it
|
|
398
|
-
// is the write target for personal-scope entries (R5#3) and always in the
|
|
399
|
-
// read-set (S11). Optional (no default) so the output type stays a plain
|
|
400
|
-
// optional — consumers coalesce `?? false` when building resolver input.
|
|
401
|
-
personal: z5.boolean().optional(),
|
|
402
|
-
// Whether writes are accepted into this store from this machine. Optional;
|
|
403
|
-
// consumers coalesce `?? true`. Shared stores cloned read-only set false.
|
|
404
|
-
writable: z5.boolean().optional()
|
|
358
|
+
var mcpPayloadLimitsSchema = z5.object({
|
|
359
|
+
warnBytes: z5.number().int().positive().optional(),
|
|
360
|
+
hardBytes: z5.number().int().positive().optional()
|
|
361
|
+
}).optional();
|
|
362
|
+
var selectionTokenTtlMsSchema = z5.number().int().min(3e4).max(36e5);
|
|
363
|
+
var planContextTopKSchema = z5.number().int().min(1).max(200);
|
|
364
|
+
var fabricLanguageSchema = z5.enum(["zh-CN", "en"]);
|
|
365
|
+
var defaultLayerFilterSchema = z5.enum(["team", "personal", "both"]);
|
|
366
|
+
var nudgeModeSchema = z5.enum(["silent", "minimal", "normal", "verbose"]);
|
|
367
|
+
var observeConfigSchema = z5.object({
|
|
368
|
+
session_start: z5.boolean().optional(),
|
|
369
|
+
pre_tool_use: z5.boolean().optional(),
|
|
370
|
+
stop: z5.boolean().optional()
|
|
405
371
|
}).strict();
|
|
406
|
-
var
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
stores: z5.array(mountedStoreSchema).optional().default([])
|
|
413
|
-
}).passthrough();
|
|
414
|
-
|
|
415
|
-
// src/schemas/fabric-config.ts
|
|
416
|
-
var auditModeSchema = z6.enum(["strict", "warn", "off"]);
|
|
417
|
-
var clientPathsSchema = z6.object({
|
|
418
|
-
claudeCodeCLI: z6.string().optional(),
|
|
419
|
-
claudeCodeDesktop: z6.string().optional(),
|
|
420
|
-
cursor: z6.string().optional(),
|
|
421
|
-
codexCLI: z6.string().optional()
|
|
372
|
+
var writeRouteSchema = z5.object({
|
|
373
|
+
scope: z5.string().regex(
|
|
374
|
+
SCOPE_COORDINATE_PATTERN,
|
|
375
|
+
"write route scope must be ':'-joined lowercase [a-z0-9_-] segments"
|
|
376
|
+
),
|
|
377
|
+
store: z5.string().min(1)
|
|
422
378
|
}).strict();
|
|
423
|
-
var
|
|
424
|
-
warnBytes: z6.number().int().positive().optional(),
|
|
425
|
-
hardBytes: z6.number().int().positive().optional()
|
|
426
|
-
}).optional();
|
|
427
|
-
var selectionTokenTtlMsSchema = z6.number().int().min(3e4).max(36e5);
|
|
428
|
-
var planContextTopKSchema = z6.number().int().min(1).max(200);
|
|
429
|
-
var fabricLanguageSchema = z6.enum([
|
|
430
|
-
"match-existing",
|
|
431
|
-
"zh-CN",
|
|
432
|
-
"en",
|
|
433
|
-
"zh-CN-hybrid"
|
|
434
|
-
]);
|
|
435
|
-
var defaultLayerFilterSchema = z6.enum(["team", "personal", "both"]);
|
|
436
|
-
var fabricConfigSchema = z6.object({
|
|
379
|
+
var fabricConfigSchema = z5.object({
|
|
437
380
|
clientPaths: clientPathsSchema.optional(),
|
|
438
381
|
// v2.1.0-rc.1 P0 (S13-projectid): the project's stable identity. A UUID
|
|
439
382
|
// bound at `fabric install` time; a remote-derived hash is only a SUGGESTED
|
|
@@ -442,19 +385,37 @@ var fabricConfigSchema = z6.object({
|
|
|
442
385
|
// fabric-config.json files simply lack it and the ProjectRootResolver mints
|
|
443
386
|
// one on next install. `.fabric/fabric-config.json` carrying this field is
|
|
444
387
|
// also the upward marker the ProjectRootResolver searches for (S15/S32).
|
|
445
|
-
project_id:
|
|
388
|
+
project_id: z5.string().optional(),
|
|
389
|
+
// Store-only runtime binding identity. Defaults to project_id when omitted,
|
|
390
|
+
// but worktrees / sandboxes can set this to isolate hook/runtime state while
|
|
391
|
+
// keeping the same committed project identity.
|
|
392
|
+
workspace_binding_id: z5.string().optional(),
|
|
446
393
|
// v2.1.0-rc.1 P0 (S59/B3): the stores this repo expects mounted. Each entry
|
|
447
394
|
// names a store by alias/UUID with an optional suggested_remote (or the
|
|
448
395
|
// `$personal` sentinel). Drives the read-set (required_stores ∪ implicit
|
|
449
396
|
// personal, S11/S54) and `clone`'s missing-store onboarding (S51). Optional
|
|
450
397
|
// + absent → read-set is just the implicit personal store.
|
|
451
|
-
required_stores:
|
|
398
|
+
required_stores: z5.array(requiredStoreEntrySchema).optional(),
|
|
452
399
|
// v2.1.0-rc.1 P3 (S60 / `store switch-write`): alias of the store that
|
|
453
400
|
// non-personal-scope writes land in for this project. Set by
|
|
454
401
|
// `fabric store switch-write <alias>`; consumed as the resolver's
|
|
455
402
|
// activeWriteAlias. Absent → no active write store yet. Personal-scope
|
|
456
403
|
// writes always target the implicit personal store regardless (R5#3).
|
|
457
|
-
active_write_store:
|
|
404
|
+
active_write_store: z5.string().optional(),
|
|
405
|
+
// v2.1 global-refactor (W1/A2 — store project registry): the project this repo
|
|
406
|
+
// currently participates in, as the SINGLE scope segment forming the
|
|
407
|
+
// `project:<id>` coordinate (schemas/scope.ts). Set by `store bind --project
|
|
408
|
+
// <id>` (validated against the bound store's projects.json). Drives:
|
|
409
|
+
// - write: project-scoped writes get `semantic_scope: project:<active_project>`.
|
|
410
|
+
// - recall: keep `project:<active_project>` + non-project coords, drop other
|
|
411
|
+
// `project:*` entries (G-FILTER).
|
|
412
|
+
// Absent → the repo has no project binding; recall does not project-filter.
|
|
413
|
+
active_project: z5.string().optional(),
|
|
414
|
+
// Global Store Topology: scope-aware write routing for multi shared/org stores.
|
|
415
|
+
// Personal scope ignores these routes and always resolves to the implicit
|
|
416
|
+
// personal store. `active_write_store` remains a backward-compatible fallback.
|
|
417
|
+
write_routes: z5.array(writeRouteSchema).optional(),
|
|
418
|
+
default_write_store: z5.string().optional(),
|
|
458
419
|
// rc.17 (R-cut): the dev/test fixture-path config field was removed
|
|
459
420
|
// end-to-end. The `EXTERNAL_FIXTURE_PATH` env var is now the sole source
|
|
460
421
|
// consumed by `resolveDevMode()`. No z.preprocess alias — pre-rc.17
|
|
@@ -462,14 +423,23 @@ var fabricConfigSchema = z6.object({
|
|
|
462
423
|
// the lenient root parser (no .strict() at root). Pre-user clean-slate per
|
|
463
424
|
// memory/feedback_clean_slate.md; mirrors the rc.12 hard-rename precedent
|
|
464
425
|
// documented above.
|
|
465
|
-
scanIgnores:
|
|
426
|
+
scanIgnores: z5.array(z5.string()).optional(),
|
|
466
427
|
audit_mode: auditModeSchema.optional(),
|
|
467
428
|
mcpPayloadLimits: mcpPayloadLimitsSchema,
|
|
468
|
-
//
|
|
469
|
-
//
|
|
470
|
-
//
|
|
471
|
-
|
|
429
|
+
// grill-6fixes (D1): `fabric_language` is no longer a per-project field —
|
|
430
|
+
// language is a single machine-wide tone in `~/.fabric/fabric-global.json`.
|
|
431
|
+
// The root parser is lenient (no .strict()), so any stale `fabric_language`
|
|
432
|
+
// key left in an existing project config is silently dropped.
|
|
472
433
|
default_layer_filter: defaultLayerFilterSchema.optional().default("both"),
|
|
434
|
+
// v2.2 dual-sink (Goal A / D4): human-output preset. See nudgeModeSchema for
|
|
435
|
+
// the level semantics + the flow ⊥ observation invariant (nudge_mode never
|
|
436
|
+
// touches the AI additionalContext sink). Default "normal" preserves the
|
|
437
|
+
// pre-dual-sink human visibility so existing dogfood repos see no regression.
|
|
438
|
+
nudge_mode: nudgeModeSchema.optional().default("normal"),
|
|
439
|
+
// v2.2 dual-sink (Goal A / D4): per-event human-output overrides. A set value
|
|
440
|
+
// wins over the nudge_mode preset for that event; absent events fall back to
|
|
441
|
+
// the preset. AI sink unaffected (same invariant as nudge_mode).
|
|
442
|
+
observe: observeConfigSchema.optional(),
|
|
473
443
|
// Cooldown for the fabric-hint Stop hook (formerly archive-hint, renamed in
|
|
474
444
|
// rc.5 TASK-010). After ANY of the three signals (archive / review / import)
|
|
475
445
|
// fires, that signal stays silent for this many hours regardless of state
|
|
@@ -477,7 +447,7 @@ var fabricConfigSchema = z6.object({
|
|
|
477
447
|
// day if the user keeps ignoring it." Set to 24 to align with the archive
|
|
478
448
|
// trigger threshold. The legacy `archive_hint_` key is retained for backward
|
|
479
449
|
// compat with existing user fabric-config.json files.
|
|
480
|
-
archive_hint_cooldown_hours:
|
|
450
|
+
archive_hint_cooldown_hours: z5.number().int().positive().optional().default(12),
|
|
481
451
|
// Underseed-node threshold for the fabric-hint Stop hook's import signal
|
|
482
452
|
// (rc.5 TASK-010). When the canonical knowledge node count is strictly less
|
|
483
453
|
// than this value AND a successful `init_scan_completed` event happened at
|
|
@@ -486,7 +456,7 @@ var fabricConfigSchema = z6.object({
|
|
|
486
456
|
// the rule-of-thumb that a workspace with fewer than ten knowledge entries
|
|
487
457
|
// is below the floor for plan_context retrieval to be meaningful. Also
|
|
488
458
|
// consumed by `doctor` lint #22 (knowledge_underseeded).
|
|
489
|
-
underseed_node_threshold:
|
|
459
|
+
underseed_node_threshold: z5.number().int().positive().optional().default(10),
|
|
490
460
|
// Edit-count threshold for the fabric-hint Stop hook's Signal A
|
|
491
461
|
// (rc.6 TASK-022 / E5). Signal A fires when EITHER (a) >=24h have elapsed
|
|
492
462
|
// since the last `knowledge_proposed` event, OR (b) >=archive_edit_threshold
|
|
@@ -497,102 +467,102 @@ var fabricConfigSchema = z6.object({
|
|
|
497
467
|
// there is probably something worth archiving"; lowered values nag more
|
|
498
468
|
// aggressively, higher values rely on the 24h fallback. Missing or absent
|
|
499
469
|
// edit-counter file degrades safely to the 24h-only path.
|
|
500
|
-
archive_edit_threshold:
|
|
470
|
+
archive_edit_threshold: z5.number().int().positive().optional().default(20),
|
|
501
471
|
// rc.7 T7: hours-since-last-knowledge_proposed cutoff for Signal A's
|
|
502
472
|
// time branch. Was hardcoded as 24 in fabric-hint.cjs's THRESHOLD_HOURS;
|
|
503
473
|
// externalized so chatty workspaces can lower the bar and quiet ones can
|
|
504
474
|
// raise it. Default 24 preserves rc.6 behavior. See docs/configuration.md.
|
|
505
|
-
archive_hint_hours:
|
|
475
|
+
archive_hint_hours: z5.number().int().positive().optional().default(24),
|
|
506
476
|
// rc.7 T7: pending-count cutoff for Signal B (review skill). Was
|
|
507
477
|
// hardcoded as 10 in fabric-hint.cjs's THRESHOLD_PENDING_COUNT.
|
|
508
478
|
// Default 10 preserves rc.6 behavior. See docs/configuration.md for
|
|
509
479
|
// small/medium/large repo recommendations.
|
|
510
|
-
review_hint_pending_count:
|
|
480
|
+
review_hint_pending_count: z5.number().int().positive().optional().default(10),
|
|
511
481
|
// rc.7 T7: pending-age cutoff (in days) for Signal B (review skill).
|
|
512
482
|
// Was hardcoded as 7 in fabric-hint.cjs's THRESHOLD_PENDING_AGE_DAYS.
|
|
513
483
|
// Default 7 preserves rc.6 behavior. See docs/configuration.md.
|
|
514
|
-
review_hint_pending_age_days:
|
|
484
|
+
review_hint_pending_age_days: z5.number().int().positive().optional().default(7),
|
|
515
485
|
// rc.7 T7 + T10 pre-wiring: days-since-last-doctor cutoff for the future
|
|
516
486
|
// Signal D (maintenance hint). T10 will consume this to decide when the
|
|
517
487
|
// fabric-hint Stop hook surfaces a "run `fabric doctor`" reminder.
|
|
518
488
|
// Default 14 reflects a fortnightly cadence — long enough to avoid nag,
|
|
519
489
|
// short enough to catch index drift before it compounds.
|
|
520
|
-
maintenance_hint_days:
|
|
490
|
+
maintenance_hint_days: z5.number().int().positive().optional().default(14),
|
|
521
491
|
// rc.7 T7 + T10 pre-wiring: cooldown between Signal D reminders, in
|
|
522
492
|
// days. Once Signal D fires, it stays silent for this many days even if
|
|
523
493
|
// the user doesn't run doctor. Default 7 keeps the reminder weekly at
|
|
524
494
|
// worst — pairing 14d trigger + 7d cooldown means at most ~2 reminders
|
|
525
495
|
// per month for a workspace that ignores them.
|
|
526
|
-
maintenance_hint_cooldown_days:
|
|
496
|
+
maintenance_hint_cooldown_days: z5.number().int().positive().optional().default(7),
|
|
527
497
|
// rc.9+ (skill-contract-fix B1): first-run import window in months. The
|
|
528
498
|
// `fabric-import` skill scans this many months of git history on the very
|
|
529
499
|
// first invocation (when no prior `import_run_completed` event exists).
|
|
530
500
|
// Default 60 (~5 years) captures the bulk of a mature repo's signal in
|
|
531
501
|
// one pass; small / fresh repos can lower to 12-24 with no loss.
|
|
532
|
-
import_window_first_run_months:
|
|
502
|
+
import_window_first_run_months: z5.number().int().min(1).optional().default(60),
|
|
533
503
|
// rc.9+ (skill-contract-fix B1): rerun import window in months. After
|
|
534
504
|
// the first successful import, subsequent runs only scan this many
|
|
535
505
|
// recent months — assumed everything older has already been crystallized
|
|
536
506
|
// into pending or canonical knowledge. Default 2 keeps incremental cost
|
|
537
507
|
// low; raise to 6 if the workspace pauses fabric-import for long stretches.
|
|
538
|
-
import_window_rerun_months:
|
|
508
|
+
import_window_rerun_months: z5.number().int().min(1).optional().default(2),
|
|
539
509
|
// rc.9+ (skill-contract-fix B1): hard cap on pending entries produced
|
|
540
510
|
// per fabric-import invocation. Prevents one run from dumping hundreds
|
|
541
511
|
// of proposals when a backfill window is wide open. Default 10 matches
|
|
542
512
|
// the rule-of-thumb "human can triage ~10 pending entries in one
|
|
543
513
|
// review pass." Range 1-50.
|
|
544
|
-
import_max_pending_per_run:
|
|
514
|
+
import_max_pending_per_run: z5.number().int().min(1).max(50).optional().default(10),
|
|
545
515
|
// rc.9+ (skill-contract-fix B1): hard cap on commits scanned per
|
|
546
516
|
// fabric-import invocation. Bounds runtime on monorepos with high
|
|
547
517
|
// commit velocity. Default 500 covers ~2 months of typical churn;
|
|
548
518
|
// range 50-2000. Hitting the cap mid-window is logged but non-fatal.
|
|
549
|
-
import_max_commits_scan:
|
|
519
|
+
import_max_commits_scan: z5.number().int().min(50).max(2e3).optional().default(500),
|
|
550
520
|
// rc.9+ (skill-contract-fix B1): canonical-node count above which
|
|
551
521
|
// fabric-import's pre-flight should warn / suggest review instead of
|
|
552
522
|
// proceeding. A workspace with 50+ canonical entries usually benefits
|
|
553
523
|
// more from `fabric-review` to consolidate than from importing more.
|
|
554
524
|
// Default 50; raise to 100+ for large polyglot repos.
|
|
555
|
-
import_skip_canonical_threshold:
|
|
525
|
+
import_skip_canonical_threshold: z5.number().int().positive().optional().default(50),
|
|
556
526
|
// rc.9+ (skill-contract-fix B1): max candidate entries surfaced per
|
|
557
527
|
// fabric-archive batch (one invocation of the skill). Pagination knob
|
|
558
528
|
// for the archive UI flow. Default 8 keeps each batch reviewable in
|
|
559
529
|
// one sitting; raise for large repos with high archive throughput.
|
|
560
|
-
archive_max_candidates_per_batch:
|
|
530
|
+
archive_max_candidates_per_batch: z5.number().int().positive().optional().default(8),
|
|
561
531
|
// rc.9+ (skill-contract-fix B1): max recently-touched paths included
|
|
562
532
|
// in fabric-archive's "relevant context" lookup. Limits the size of
|
|
563
533
|
// the path-relevance digest the skill emits when ranking candidates.
|
|
564
534
|
// Default 20; large repos with deep directory fan-out can raise to
|
|
565
535
|
// 50+ if archive candidates feel under-contextualized.
|
|
566
|
-
archive_max_recent_paths:
|
|
536
|
+
archive_max_recent_paths: z5.number().int().positive().optional().default(20),
|
|
567
537
|
// rc.9+ (skill-contract-fix B1): max prior fabric-archive sessions
|
|
568
538
|
// summarised in the digest the skill loads on start. Prevents the
|
|
569
539
|
// digest from ballooning past the model context budget on workspaces
|
|
570
540
|
// that have archived repeatedly. Default 10; lower if context pressure
|
|
571
541
|
// bites, raise if you want longer-range archive trend visibility.
|
|
572
|
-
archive_digest_max_sessions:
|
|
542
|
+
archive_digest_max_sessions: z5.number().int().positive().optional().default(10),
|
|
573
543
|
// rc.9+ (skill-contract-fix B1): max review results returned per
|
|
574
544
|
// topic when `fabric-review` clusters pending entries. Pagination
|
|
575
545
|
// knob analogous to archive_max_candidates_per_batch but scoped to
|
|
576
546
|
// each topic cluster. Default 8; raise to 15-20 for large repos
|
|
577
547
|
// where each topic legitimately groups many pending entries.
|
|
578
|
-
review_topic_result_cap:
|
|
548
|
+
review_topic_result_cap: z5.number().int().positive().optional().default(8),
|
|
579
549
|
// rc.9+ (skill-contract-fix B1): age threshold (in days) above which
|
|
580
550
|
// a pending entry is considered "stale" by fabric-review and surfaced
|
|
581
551
|
// for explicit resolve-or-drop decision. Default 14; tighter than the
|
|
582
552
|
// 7d Signal-B trigger because review specifically targets the long
|
|
583
553
|
// tail. Large repos with slower cadence can raise to 30.
|
|
584
|
-
review_stale_pending_days:
|
|
554
|
+
review_stale_pending_days: z5.number().int().positive().optional().default(14),
|
|
585
555
|
// v2.0.0-rc.34 TASK-05: reverse-unarchive opt-in. When true, callers of the
|
|
586
556
|
// `unarchiveKnowledge` primitive (and any future doctor auto-detect lint built
|
|
587
557
|
// on top) will execute the file move + ledger emit. When false (default),
|
|
588
558
|
// the same callers MUST short-circuit before any mutation — the primitive is
|
|
589
559
|
// shipped but inert until explicitly enabled. Opt-in posture mirrors the
|
|
590
560
|
// archive-flow precedent: destructive-ish file moves stay behind a flag.
|
|
591
|
-
reverse_unarchive_enabled:
|
|
561
|
+
reverse_unarchive_enabled: z5.boolean().optional().default(false),
|
|
592
562
|
// v2.0.0-rc.34 TASK-05: forces `unarchiveKnowledge` into dry-run mode even
|
|
593
563
|
// when called with `options.dryRun=false`. Lets operators preview a
|
|
594
564
|
// restoration pass before flipping `reverse_unarchive_enabled` to true.
|
|
595
|
-
reverse_unarchive_dry_run:
|
|
565
|
+
reverse_unarchive_dry_run: z5.boolean().optional().default(false),
|
|
596
566
|
// v2.0.0-rc.34 TASK-06: long-session cite-policy evict window in user-prompt
|
|
597
567
|
// turns. UserPromptSubmit hook (Claude Code only) maintains a per-session
|
|
598
568
|
// counter and re-injects the cite contract reminder via
|
|
@@ -600,7 +570,31 @@ var fabricConfigSchema = z6.object({
|
|
|
600
570
|
// Default 0 = OFF (opt-in). Recommend 10-20 for active sessions; 5 for
|
|
601
571
|
// high-contract-criticality projects. Other strategies (time-based,
|
|
602
572
|
// token-budget) deferred to rc.35 per plan locked-decisions 2026-05-26.
|
|
603
|
-
cite_evict_interval:
|
|
573
|
+
cite_evict_interval: z5.number().int().min(0).optional().default(0),
|
|
574
|
+
// v2.1 ⑤ cite-redesign (P5): recall-based cite-accounting hook config. The
|
|
575
|
+
// rc.34 cite_evict_interval turn-counter above is superseded by the
|
|
576
|
+
// PreToolUse(Edit/Write) recall-aware nudge in cite-policy-evict.cjs; the old
|
|
577
|
+
// key is retained for back-compat (inert now that the hook moved off
|
|
578
|
+
// UserPromptSubmit). `cite_recall_nudge` is the master switch (default true =
|
|
579
|
+
// ON); set false to silence the "改前先 fab_recall" nudge entirely (mirrors
|
|
580
|
+
// the cite_evict_interval=0 opt-out convention). `cite_recall_window_minutes`
|
|
581
|
+
// bounds how far back an in-session fab_recall counts as "informing" the edit
|
|
582
|
+
// (default 30; 0 = unbounded).
|
|
583
|
+
cite_recall_nudge: z5.boolean().optional().default(true),
|
|
584
|
+
cite_recall_window_minutes: z5.number().int().min(0).optional().default(30),
|
|
585
|
+
// F2: glob exemptions for the cite nudge (cite-policy-evict.cjs). Edit paths
|
|
586
|
+
// matching any glob skip the "改前先 fab_recall" nudge — meta/orchestration
|
|
587
|
+
// files (e.g. `.workflow/` scratchpads) are not source the cite policy
|
|
588
|
+
// governs. MERGED with the hook's built-in [".workflow/**"] default; an
|
|
589
|
+
// omitted/empty value keeps just that default. `*` = within a path segment,
|
|
590
|
+
// `**` = across segments.
|
|
591
|
+
cite_nudge_ignore_globs: z5.array(z5.string()).optional(),
|
|
592
|
+
// v2.1 ④ conflict-detection (P4): bm25 content-similarity threshold (0..1)
|
|
593
|
+
// for the knowledge-conflict lint (`fabric doctor --lint-conflicts`). A
|
|
594
|
+
// same-(type,layer) pair whose normalized bm25 similarity reaches this floor
|
|
595
|
+
// is surfaced as a candidate (possible duplicate OR conflict). Conservative
|
|
596
|
+
// default 0.5 — raise to reduce noise, lower to catch looser pairs.
|
|
597
|
+
conflict_lint_similarity_threshold: z5.number().min(0).max(1).optional().default(0.5),
|
|
604
598
|
// v2.0.0-rc.22 Scope A T3: sliding-window retention (in days) for the
|
|
605
599
|
// event ledger rotation primitive (`rotateEventLedgerIfNeeded`). Lines
|
|
606
600
|
// whose `ts` is older than `now - fabric_event_retention_days * 86_400_000`
|
|
@@ -613,7 +607,7 @@ var fabricConfigSchema = z6.object({
|
|
|
613
607
|
// Mirrors cite-policy precedent of locking enum-style numeric tunables
|
|
614
608
|
// to a small literal set (vs free `.positive()`) to prevent fat-finger
|
|
615
609
|
// misconfig.
|
|
616
|
-
fabric_event_retention_days:
|
|
610
|
+
fabric_event_retention_days: z5.union([z5.literal(7), z5.literal(30), z5.literal(90)]).optional(),
|
|
617
611
|
// v2.0.0-rc.23 TASK-014 (F8c): onboard slot opt-out list. Tracks slot
|
|
618
612
|
// names the user explicitly dismissed during fabric-archive's first-run
|
|
619
613
|
// onboard phase (or via `fabric config dismiss-slot <slot>`). Dismissed
|
|
@@ -630,7 +624,7 @@ var fabricConfigSchema = z6.object({
|
|
|
630
624
|
//
|
|
631
625
|
// Default `[]` keeps the field optional on existing configs — fresh
|
|
632
626
|
// installs land with no opt-outs.
|
|
633
|
-
onboard_slots_opted_out:
|
|
627
|
+
onboard_slots_opted_out: z5.array(z5.string()).optional().default([]),
|
|
634
628
|
// v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the broad SessionStart hint
|
|
635
629
|
// banner emitted by knowledge-hint-broad.cjs. After plan-context-hint returns
|
|
636
630
|
// its full broad-scoped index, the hook slices the entries to this many
|
|
@@ -642,20 +636,25 @@ var fabricConfigSchema = z6.object({
|
|
|
642
636
|
// Range 1..50; values above 20 effectively disable the cap because the
|
|
643
637
|
// TRUNCATION_THRESHOLD=12 grouped-render kicks in. Mirrors the rc.7 T7 +
|
|
644
638
|
// archive_max_* pattern of externalizing previously-hardcoded thresholds.
|
|
645
|
-
hint_broad_top_k:
|
|
646
|
-
//
|
|
647
|
-
//
|
|
648
|
-
//
|
|
649
|
-
//
|
|
650
|
-
//
|
|
651
|
-
//
|
|
652
|
-
|
|
639
|
+
hint_broad_top_k: z5.number().int().min(1).max(50).optional().default(8),
|
|
640
|
+
// KT-DEC-0036: the SessionStart broad-menu is now index-only (title + summary
|
|
641
|
+
// per always-active entry, no eager body), so the former `hint_broad_budget_chars`
|
|
642
|
+
// body char-budget knob was retired — there is no rendered body left to bound.
|
|
643
|
+
// W4-1 (KT-DEC-0028 / KT-MOD-0001): scale backstop for the FULL broad index.
|
|
644
|
+
// After W2-1 retired the hint_broad_top_k hard cap, the broad banner shows
|
|
645
|
+
// every broad entry (completeness); this is the only guard — once a store's
|
|
646
|
+
// rendered broad index exceeds this many lines the overflow tail folds into a
|
|
647
|
+
// single drift marker. The doctor `broad-index-drift` lint (W4-2) warns at 80%
|
|
648
|
+
// of this value per store so the corpus can be pruned (fabric-audit) BEFORE the
|
|
649
|
+
// banner silently truncates. Default 50; range 20..500 (read inline by
|
|
650
|
+
// knowledge-hint-broad.cjs#readBroadIndexBackstop with the same bounds).
|
|
651
|
+
broad_index_backstop: z5.number().int().min(20).max(500).optional().default(50),
|
|
653
652
|
// v2.0.0-rc.37 NEW-16: durable per-signal dismiss for the fabric-hint Stop
|
|
654
653
|
// hook nudges. Any signal type listed here is suppressed at emit time across
|
|
655
654
|
// all sessions (the session-scoped sibling lives in a .fabric/.cache sidecar
|
|
656
655
|
// written on request). Mirrors the cite_evict_interval=0 opt-out convention —
|
|
657
656
|
// a knob for an existing surface, not a new feature. Unknown types ignored.
|
|
658
|
-
hint_dismiss_signals:
|
|
657
|
+
hint_dismiss_signals: z5.array(z5.enum(["archive", "review", "import", "maintenance"])).optional(),
|
|
659
658
|
// v2.1 ADJ-NEWN-4: user-override escape hatches for the two strong behavioral
|
|
660
659
|
// policies (cite-before-edit + self-archive). The strong policies can make an
|
|
661
660
|
// agent feel like a "stubborn parrot" (D2 user-in-control red line); these
|
|
@@ -667,15 +666,15 @@ var fabricConfigSchema = z6.object({
|
|
|
667
666
|
// surface, mirroring the cite_evict_interval=0 / hint_dismiss_signals opt-out
|
|
668
667
|
// convention, NOT a new feature. Wave3 J32 will quantify the friction these
|
|
669
668
|
// relieve; until then they ship as inert-safe opt-outs.
|
|
670
|
-
cite_policy_enabled:
|
|
671
|
-
self_archive_policy_enabled:
|
|
669
|
+
cite_policy_enabled: z5.boolean().optional().default(true),
|
|
670
|
+
self_archive_policy_enabled: z5.boolean().optional().default(true),
|
|
672
671
|
// v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the narrow PreToolUse hint
|
|
673
672
|
// emitted by knowledge-hint-narrow.cjs. After filtering to entries whose
|
|
674
673
|
// `relevance_scope === "narrow"` (rc.27 TASK-005 audit §2.5 fix), the hook
|
|
675
674
|
// slices to this many before the E3 emit-gate / renderSummary pipeline.
|
|
676
675
|
// Default 5 keeps each per-Edit hint terse — five lines max so the agent's
|
|
677
676
|
// working memory is not displaced by an unwieldy banner. Range 1..20.
|
|
678
|
-
hint_narrow_top_k:
|
|
677
|
+
hint_narrow_top_k: z5.number().int().min(1).max(20).optional().default(5),
|
|
679
678
|
// v2.0.0-rc.33 W2-1 (P0-9): per-file dedup window (in PreToolUse turns) for
|
|
680
679
|
// the narrow hint. Same (file_path, stable_id) tuple stays silent for this
|
|
681
680
|
// many turns even when the E3 cross-session cache would otherwise re-emit.
|
|
@@ -685,7 +684,7 @@ var fabricConfigSchema = z6.object({
|
|
|
685
684
|
// Storage: .fabric/.cache/narrow-dedup-window.json — distinct from session-
|
|
686
685
|
// hints cache so a window-only suppression does not poison cross-session
|
|
687
686
|
// dedupe semantics.
|
|
688
|
-
hint_narrow_dedup_window_turns:
|
|
687
|
+
hint_narrow_dedup_window_turns: z5.number().int().min(1).max(50).optional().default(5),
|
|
689
688
|
// v2.0.0-rc.33 W2-5 (P1-8): cooldown between broad SessionStart hint emits,
|
|
690
689
|
// in hours. Distinct from the archive_hint_cooldown_hours that gates the
|
|
691
690
|
// fabric-hint Stop hook — knowledge-hint-broad re-fires on every
|
|
@@ -694,29 +693,34 @@ var fabricConfigSchema = z6.object({
|
|
|
694
693
|
// menu at most once per hour"; 0 means "no cooldown, current behavior."
|
|
695
694
|
// Range 0..168 (one week). Stored alongside fabric-hint's cooldown cache
|
|
696
695
|
// under a distinct knowledge-hint-broad key.
|
|
697
|
-
hint_broad_cooldown_hours:
|
|
696
|
+
hint_broad_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
|
|
698
697
|
// v2.0.0-rc.33 W2-5 (P1-8): cooldown for the narrow PreToolUse hint.
|
|
699
698
|
// Same shape as hint_broad_cooldown_hours but applies to per-Edit hint
|
|
700
699
|
// re-emission across the cooldown window — independent of E3 session-
|
|
701
700
|
// hints dedupe. Default 0 preserves rc.32 behavior; set to e.g. 1 to
|
|
702
701
|
// throttle hint frequency during rapid-fire editing sprints. Range
|
|
703
702
|
// 0..168 (one week).
|
|
704
|
-
hint_narrow_cooldown_hours:
|
|
703
|
+
hint_narrow_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
|
|
705
704
|
// v2.0.0-rc.33 W4-B3 (T5 P2): per-maturity inactivity thresholds (days)
|
|
706
|
-
// driving orphan_demote. Hardcoded at
|
|
705
|
+
// driving orphan_demote. Hardcoded at proven=90/verified=30/draft=14 in
|
|
707
706
|
// rc.32; chatty workspaces want them tighter, slow ones want them looser.
|
|
708
707
|
// Each field optional; absent → defaults inside doctor.ts apply. Ranges
|
|
709
708
|
// chosen so a typo can't accidentally disable the lint (min 1).
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
709
|
+
//
|
|
710
|
+
// v2.2 W3-T5 (F-MATURITY-ENDORSED): the canonical maturity enum is
|
|
711
|
+
// draft/verified/proven (KT-DEC-0005). These threshold keys use the canonical
|
|
712
|
+
// vocabulary; the loader maps proven→stable / verified→endorsed onto the
|
|
713
|
+
// doctor's internal orphan_demote ladder.
|
|
714
|
+
orphan_demote_proven_days: z5.number().int().min(1).max(3650).optional(),
|
|
715
|
+
orphan_demote_verified_days: z5.number().int().min(1).max(3650).optional(),
|
|
716
|
+
orphan_demote_draft_days: z5.number().int().min(1).max(3650).optional(),
|
|
713
717
|
// v2.0.0-rc.33 W4-A3 (T4 P2): per-entry summary truncation length used by
|
|
714
718
|
// knowledge-hint-{broad,narrow}.cjs. Hard-coded at 80 chars in rc.32 — too
|
|
715
719
|
// short for entries with parameterized summaries (e.g. "Use bcrypt with
|
|
716
720
|
// cost=12 for password hashing"), too long for terse pitfalls. Range 40..240;
|
|
717
721
|
// default 80 preserves rc.32 behavior. Both hooks read the same key so the
|
|
718
722
|
// banner styling stays consistent across SessionStart + PreToolUse.
|
|
719
|
-
hint_summary_max_len:
|
|
723
|
+
hint_summary_max_len: z5.number().int().min(40).max(240).optional().default(80),
|
|
720
724
|
// v2.0.0-rc.33 W2-6 (P0-7 + P0-8): when true, knowledge-hint hooks emit
|
|
721
725
|
// their banners as `hookSpecificOutput.additionalContext` JSON on stdout
|
|
722
726
|
// (per Claude Code PreToolUse hook contract — see
|
|
@@ -726,7 +730,7 @@ var fabricConfigSchema = z6.object({
|
|
|
726
730
|
// coverage focus (rc.32 baseline 3.1% → primary cause: reminders never
|
|
727
731
|
// entered model context). Set false to revert to legacy stderr-only mode
|
|
728
732
|
// for hosts that don't honor the JSON contract.
|
|
729
|
-
hint_reminder_to_context:
|
|
733
|
+
hint_reminder_to_context: z5.boolean().optional().default(true),
|
|
730
734
|
// v2.0.0-rc.29 TASK-008 (BUG-F3): selection-token TTL override. The
|
|
731
735
|
// `fab_plan_context` MCP tool hands clients a `selection_token` whose default
|
|
732
736
|
// 5-minute lifetime (`SELECTION_TOKEN_TTL_MS` at
|
|
@@ -747,31 +751,51 @@ var fabricConfigSchema = z6.object({
|
|
|
747
751
|
// applied after BM25 ranking. Absent → library default (24). See
|
|
748
752
|
// planContextTopKSchema for the range/calibration rationale.
|
|
749
753
|
plan_context_top_k: planContextTopKSchema.optional(),
|
|
750
|
-
//
|
|
751
|
-
//
|
|
752
|
-
//
|
|
753
|
-
//
|
|
754
|
-
//
|
|
755
|
-
//
|
|
756
|
-
|
|
757
|
-
|
|
754
|
+
// KT-DEC-0038: ratio-to-top relevance floor (α) for recall / plan_context.
|
|
755
|
+
// After ranking, keep only candidates whose fused score >= α × the top
|
|
756
|
+
// candidate's score — self-normalizing against the current query's max, so it
|
|
757
|
+
// is immune to BM25's uncalibrated cross-query scale. top_k is a pure safety
|
|
758
|
+
// cap above this. Range 0..1; absent → library default (0.25). 0 disables the
|
|
759
|
+
// floor (keep all up to top_k).
|
|
760
|
+
recall_relevance_ratio: z5.number().min(0).max(1).optional(),
|
|
761
|
+
// KT-DEC-0037: the `retrieval_budget_profile` enum was deleted. top_k is the
|
|
762
|
+
// sole retrieval knob (plan_context_top_k above); payload limits pass through
|
|
763
|
+
// explicit `mcpPayloadLimits`, else the fixed PAYLOAD_LIMIT_DEFAULT_* guardrail.
|
|
758
764
|
// v2.2 C2-vector (W2-T7): OPTIONAL dense-embedding semantic retrieval, layered
|
|
759
765
|
// as a recall supplement after BM25. Default OFF (`--no-embed` is the baseline);
|
|
760
766
|
// requires the operator to install the optional `fastembed` package — absent →
|
|
761
767
|
// text-only fallback. Never grows the default install footprint.
|
|
762
|
-
embed_enabled:
|
|
768
|
+
embed_enabled: z5.boolean().optional().default(false),
|
|
763
769
|
// Weight applied to the 0..1 cosine similarity before it joins the additive
|
|
764
770
|
// score. Capped at 49 — strictly BELOW BM25_WEIGHT (50) — so a perfect vector
|
|
765
771
|
// match (weight × 1) can never outscore a single strong BM25 term match. This
|
|
766
772
|
// ENFORCES the "vectors supplement, never override lexical relevance"
|
|
767
773
|
// invariant in the schema rather than leaving it to a comment (W2-REVIEW codex
|
|
768
774
|
// MED-4). Range 0..49; default 30.
|
|
769
|
-
embed_weight:
|
|
775
|
+
embed_weight: z5.number().int().min(0).max(49).optional().default(30),
|
|
776
|
+
// v2.1 ③ vector-chinese-model (P3): which fastembed model to load. The prior
|
|
777
|
+
// code pinned fastembed's English default (bge-small-en-v1.5) — wrong for the
|
|
778
|
+
// Chinese-heavy zh-CN-hybrid KB. Values are the fastembed@2.x EmbeddingModel
|
|
779
|
+
// enum strings. Default `fast-bge-small-zh-v1.5` (BGESmallZH): light, fast,
|
|
780
|
+
// Chinese-capable (bm25 already covers English/code tokens; the vector term
|
|
781
|
+
// supplements Chinese semantics). `fast-multilingual-e5-large` (MLE5Large) is
|
|
782
|
+
// available for full multilingual recall at a ~1GB download + slower CPU cost.
|
|
783
|
+
// (V1 research: fastembed@2.1.0 has NO multilingual-e5-SMALL — the originally
|
|
784
|
+
// planned pin — so bge-small-zh is the light Chinese choice.)
|
|
785
|
+
embed_model: z5.enum([
|
|
786
|
+
"fast-bge-small-zh-v1.5",
|
|
787
|
+
"fast-multilingual-e5-large",
|
|
788
|
+
"fast-bge-small-en-v1.5",
|
|
789
|
+
"fast-bge-small-en",
|
|
790
|
+
"fast-bge-base-en-v1.5",
|
|
791
|
+
"fast-bge-base-en",
|
|
792
|
+
"fast-all-MiniLM-L6-v2"
|
|
793
|
+
]).optional().default("fast-bge-small-zh-v1.5")
|
|
770
794
|
});
|
|
771
795
|
|
|
772
796
|
// src/schemas/fabric-config-introspect.ts
|
|
773
|
-
import { z as
|
|
774
|
-
var positiveIntSchema =
|
|
797
|
+
import { z as z6 } from "zod";
|
|
798
|
+
var positiveIntSchema = z6.coerce.number().int().positive();
|
|
775
799
|
function makePositiveIntField(key, defaultValue) {
|
|
776
800
|
return {
|
|
777
801
|
key,
|
|
@@ -827,6 +851,28 @@ function makeEnumField(key, group, enumValues, defaultValue) {
|
|
|
827
851
|
}
|
|
828
852
|
};
|
|
829
853
|
}
|
|
854
|
+
function makeBooleanField(key, defaultValue) {
|
|
855
|
+
return {
|
|
856
|
+
key,
|
|
857
|
+
group: "D_behavior",
|
|
858
|
+
widget: "select",
|
|
859
|
+
label_i18n_key: `cli.config.fields.${key}.label`,
|
|
860
|
+
description_i18n_key: `cli.config.fields.${key}.description`,
|
|
861
|
+
default: String(defaultValue),
|
|
862
|
+
enum_values: ["true", "false"],
|
|
863
|
+
validate(raw) {
|
|
864
|
+
const trimmed = raw.trim();
|
|
865
|
+
if (trimmed === "true") return { ok: true, value: true };
|
|
866
|
+
if (trimmed === "false") return { ok: true, value: false };
|
|
867
|
+
return { ok: false, error: "Must be one of: true, false." };
|
|
868
|
+
},
|
|
869
|
+
format_for_display(value) {
|
|
870
|
+
if (typeof value === "boolean") return String(value);
|
|
871
|
+
if (value === void 0 || value === null) return String(defaultValue);
|
|
872
|
+
return String(value);
|
|
873
|
+
}
|
|
874
|
+
};
|
|
875
|
+
}
|
|
830
876
|
var SCHEMA_DEFAULTS = fabricConfigSchema.parse({});
|
|
831
877
|
function pickNumberDefault(key) {
|
|
832
878
|
const v = SCHEMA_DEFAULTS[key];
|
|
@@ -855,12 +901,13 @@ function getPanelFieldByKey(key) {
|
|
|
855
901
|
}
|
|
856
902
|
var PANEL_FIELDS = [
|
|
857
903
|
// --- Group A: Locale (2) ---
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
904
|
+
// grill-6fixes (D1): `fabric_language` is no longer a project-config field —
|
|
905
|
+
// it is the single machine-wide tone in `~/.fabric/fabric-global.json`. The
|
|
906
|
+
// panel still surfaces it (the `fabric config` language entry), but config.ts
|
|
907
|
+
// special-cases this key to read/write the GLOBAL config instead of the
|
|
908
|
+
// project file. Default is a literal "en" since there is no project-schema
|
|
909
|
+
// default to derive from.
|
|
910
|
+
makeEnumField("fabric_language", "A_locale", fabricLanguageSchema.options, "en"),
|
|
864
911
|
makeEnumField(
|
|
865
912
|
"default_layer_filter",
|
|
866
913
|
"A_locale",
|
|
@@ -904,39 +951,23 @@ var PANEL_FIELDS = [
|
|
|
904
951
|
"C_audit",
|
|
905
952
|
auditModeSchema.options,
|
|
906
953
|
AUDIT_MODE_PANEL_DEFAULT
|
|
907
|
-
)
|
|
954
|
+
),
|
|
955
|
+
// --- Group D: Behavior / features (2) ---
|
|
956
|
+
// nudge_mode — the master switch for the human-visible nudge experience
|
|
957
|
+
// (the most user-facing runtime knob, previously JSON-only). embed_enabled —
|
|
958
|
+
// vector semantic recall, panel-editable now that config lives in `.fabric`
|
|
959
|
+
// (A1); enabling also needs the host-side `fabric install --enable-embed`.
|
|
960
|
+
makeEnumField("nudge_mode", "D_behavior", nudgeModeSchema.options, "normal"),
|
|
961
|
+
makeBooleanField("embed_enabled", false)
|
|
908
962
|
];
|
|
909
963
|
|
|
910
|
-
// src/schemas/scope.ts
|
|
911
|
-
import { z as z8 } from "zod";
|
|
912
|
-
var PERSONAL_SCOPE = "personal";
|
|
913
|
-
var KNOWN_SCOPE_PREFIXES = ["personal", "team", "project", "org"];
|
|
914
|
-
var SCOPE_COORDINATE_PATTERN = /^[a-z0-9_-]+(:[a-z0-9_-]+)*$/u;
|
|
915
|
-
var scopeCoordinateSchema = z8.string().min(1).regex(
|
|
916
|
-
SCOPE_COORDINATE_PATTERN,
|
|
917
|
-
"scope coordinate must be ':'-joined lowercase [a-z0-9_-] segments"
|
|
918
|
-
);
|
|
919
|
-
function scopeRoot(coordinate) {
|
|
920
|
-
const colon = coordinate.indexOf(":");
|
|
921
|
-
return colon === -1 ? coordinate : coordinate.slice(0, colon);
|
|
922
|
-
}
|
|
923
|
-
function isPersonalScope(coordinate) {
|
|
924
|
-
return scopeRoot(coordinate) === PERSONAL_SCOPE;
|
|
925
|
-
}
|
|
926
|
-
var entryScopeMetadataSchema = z8.object({
|
|
927
|
-
semantic_scope: scopeCoordinateSchema,
|
|
928
|
-
// Store alias or UUID. Validated as a non-empty string here; the resolver
|
|
929
|
-
// (P0.6) maps alias→UUID and verifies the store is in the read-set.
|
|
930
|
-
visibility_store: z8.string().min(1)
|
|
931
|
-
}).strict();
|
|
932
|
-
|
|
933
964
|
// src/schemas/store-stable-id.ts
|
|
934
|
-
import { z as
|
|
965
|
+
import { z as z7 } from "zod";
|
|
935
966
|
var localKnowledgeIdSchema = StableIdSchema;
|
|
936
967
|
var UID_SEGMENT_PATTERN = /^[a-z0-9-]+$/u;
|
|
937
|
-
var uidSchema =
|
|
968
|
+
var uidSchema = z7.string().min(1).regex(UID_SEGMENT_PATTERN, "uid must be lowercase [a-z0-9-] segments");
|
|
938
969
|
var GLOBAL_REF_PATTERN = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}(:[a-z0-9-]+)?:K[PT]-(MOD|DEC|GLD|PIT|PRO)-\d{4,}$/u;
|
|
939
|
-
var globalRefSchema =
|
|
970
|
+
var globalRefSchema = z7.string().regex(GLOBAL_REF_PATTERN, "global_ref must be <store_uuid>[:<uid>]:<local_id>");
|
|
940
971
|
function formatGlobalRef(parts) {
|
|
941
972
|
const { store_uuid, uid, local_id } = parts;
|
|
942
973
|
return uid === void 0 ? `${store_uuid}:${local_id}` : `${store_uuid}:${uid}:${local_id}`;
|
|
@@ -961,14 +992,14 @@ function parseGlobalRef(ref) {
|
|
|
961
992
|
local_id
|
|
962
993
|
};
|
|
963
994
|
}
|
|
964
|
-
var storeKnowledgeTypeCountersSchema =
|
|
965
|
-
MOD:
|
|
966
|
-
DEC:
|
|
967
|
-
GLD:
|
|
968
|
-
PIT:
|
|
969
|
-
PRO:
|
|
995
|
+
var storeKnowledgeTypeCountersSchema = z7.object({
|
|
996
|
+
MOD: z7.number().int().nonnegative().default(0),
|
|
997
|
+
DEC: z7.number().int().nonnegative().default(0),
|
|
998
|
+
GLD: z7.number().int().nonnegative().default(0),
|
|
999
|
+
PIT: z7.number().int().nonnegative().default(0),
|
|
1000
|
+
PRO: z7.number().int().nonnegative().default(0)
|
|
970
1001
|
}).default({ MOD: 0, DEC: 0, GLD: 0, PIT: 0, PRO: 0 });
|
|
971
|
-
var storeCountersSchema =
|
|
1002
|
+
var storeCountersSchema = z7.object({
|
|
972
1003
|
KP: storeKnowledgeTypeCountersSchema,
|
|
973
1004
|
KT: storeKnowledgeTypeCountersSchema
|
|
974
1005
|
}).default({
|
|
@@ -977,64 +1008,63 @@ var storeCountersSchema = z9.object({
|
|
|
977
1008
|
});
|
|
978
1009
|
|
|
979
1010
|
// src/schemas/parity-matrix.ts
|
|
980
|
-
import { z as
|
|
981
|
-
var PARITY_CLIENTS = ["claudeCode", "codexCLI"
|
|
982
|
-
var parityClientSchema =
|
|
1011
|
+
import { z as z8 } from "zod";
|
|
1012
|
+
var PARITY_CLIENTS = ["claudeCode", "codexCLI"];
|
|
1013
|
+
var parityClientSchema = z8.enum(PARITY_CLIENTS);
|
|
983
1014
|
var PARITY_SURFACES = ["skill", "hook", "mcp", "render"];
|
|
984
|
-
var paritySurfaceSchema =
|
|
985
|
-
var parityClientExpectationSchema =
|
|
986
|
-
supported:
|
|
987
|
-
mechanism:
|
|
988
|
-
notes:
|
|
1015
|
+
var paritySurfaceSchema = z8.enum(PARITY_SURFACES);
|
|
1016
|
+
var parityClientExpectationSchema = z8.object({
|
|
1017
|
+
supported: z8.boolean(),
|
|
1018
|
+
mechanism: z8.string().optional(),
|
|
1019
|
+
notes: z8.string().optional()
|
|
989
1020
|
}).strict();
|
|
990
|
-
var parityCapabilitySchema =
|
|
991
|
-
id:
|
|
1021
|
+
var parityCapabilitySchema = z8.object({
|
|
1022
|
+
id: z8.string().min(1),
|
|
992
1023
|
surface: paritySurfaceSchema,
|
|
993
|
-
description:
|
|
994
|
-
clients:
|
|
1024
|
+
description: z8.string().min(1),
|
|
1025
|
+
clients: z8.object({
|
|
995
1026
|
claudeCode: parityClientExpectationSchema,
|
|
996
|
-
codexCLI: parityClientExpectationSchema
|
|
997
|
-
cursor: parityClientExpectationSchema
|
|
1027
|
+
codexCLI: parityClientExpectationSchema
|
|
998
1028
|
}).strict()
|
|
999
1029
|
}).strict();
|
|
1000
|
-
var parityMatrixSchema =
|
|
1030
|
+
var parityMatrixSchema = z8.object({
|
|
1001
1031
|
// Schema/version tag of the matrix document itself.
|
|
1002
|
-
version:
|
|
1032
|
+
version: z8.string().min(1),
|
|
1003
1033
|
// Free-form note on what release/milestone this matrix targets.
|
|
1004
|
-
generated_for:
|
|
1005
|
-
capabilities:
|
|
1034
|
+
generated_for: z8.string().min(1),
|
|
1035
|
+
capabilities: z8.array(parityCapabilitySchema).min(1)
|
|
1006
1036
|
}).strict();
|
|
1007
1037
|
|
|
1008
1038
|
// src/resolver/contracts.ts
|
|
1009
|
-
import { z as
|
|
1039
|
+
import { z as z9 } from "zod";
|
|
1010
1040
|
var PROJECT_ROOT_SIGNALS = ["env", "marker", "cwd", "repo"];
|
|
1011
|
-
var projectRootSignalSchema =
|
|
1012
|
-
var projectRootSignalsSchema =
|
|
1041
|
+
var projectRootSignalSchema = z9.enum(PROJECT_ROOT_SIGNALS);
|
|
1042
|
+
var projectRootSignalsSchema = z9.object({
|
|
1013
1043
|
// FABRIC_PROJECT_ROOT, if set.
|
|
1014
|
-
env:
|
|
1044
|
+
env: z9.string().optional(),
|
|
1015
1045
|
// Nearest directory AT-OR-ABOVE cwd holding `.fabric/fabric-config.json`,
|
|
1016
1046
|
// if any (the upward marker search result; may equal cwd).
|
|
1017
|
-
markerDir:
|
|
1047
|
+
markerDir: z9.string().optional(),
|
|
1018
1048
|
// Always present — the process cwd.
|
|
1019
|
-
cwd:
|
|
1049
|
+
cwd: z9.string().min(1),
|
|
1020
1050
|
// git repo root, if inside a repo.
|
|
1021
|
-
repoRoot:
|
|
1051
|
+
repoRoot: z9.string().optional(),
|
|
1022
1052
|
// The `project_id` read from the winning root's fabric-config.json during
|
|
1023
1053
|
// (fs) signal collection. The pure resolver echoes it — it cannot invent a
|
|
1024
1054
|
// UUID. Worktrees of one repo share the committed config, hence the same
|
|
1025
1055
|
// project_id (S45 merge). Absent when no .fabric config exists at the root
|
|
1026
1056
|
// yet (fresh repo-fallback) → resolution still yields the root with a null
|
|
1027
1057
|
// projectId so the caller can mint+persist one at install time.
|
|
1028
|
-
discoveredProjectId:
|
|
1058
|
+
discoveredProjectId: z9.string().optional()
|
|
1029
1059
|
}).strict();
|
|
1030
|
-
var projectRootResolutionSchema =
|
|
1060
|
+
var projectRootResolutionSchema = z9.object({
|
|
1031
1061
|
// Absolute project root directory.
|
|
1032
|
-
projectRoot:
|
|
1062
|
+
projectRoot: z9.string().min(1),
|
|
1033
1063
|
// Stable project identity. One repo = one .fabric = one project_id (S32);
|
|
1034
1064
|
// git worktrees of the same repo resolve to the SAME project_id (S45).
|
|
1035
1065
|
// Null when the resolved root has no fabric-config.json yet (fresh
|
|
1036
1066
|
// repo-fallback) — the caller mints + persists a UUID at install time.
|
|
1037
|
-
projectId:
|
|
1067
|
+
projectId: z9.string().min(1).nullable(),
|
|
1038
1068
|
// Which signal won.
|
|
1039
1069
|
signalUsed: projectRootSignalSchema
|
|
1040
1070
|
}).strict();
|
|
@@ -1043,91 +1073,96 @@ var STORE_RESOLVER_WARNING_CODES = [
|
|
|
1043
1073
|
// required_stores entry has no matching mounted store (S51)
|
|
1044
1074
|
"local_only_no_remote",
|
|
1045
1075
|
// mounted but local-only (R5#5 nudge, non-fatal)
|
|
1046
|
-
"alias_unresolved"
|
|
1076
|
+
"alias_unresolved",
|
|
1047
1077
|
// referenced alias maps to no mounted store
|
|
1078
|
+
"missing_write_route"
|
|
1079
|
+
// multi/shared write requires an explicit route
|
|
1048
1080
|
];
|
|
1049
|
-
var storeResolverWarningCodeSchema =
|
|
1050
|
-
var storeResolverWarningSchema =
|
|
1081
|
+
var storeResolverWarningCodeSchema = z9.enum(STORE_RESOLVER_WARNING_CODES);
|
|
1082
|
+
var storeResolverWarningSchema = z9.object({
|
|
1051
1083
|
code: storeResolverWarningCodeSchema,
|
|
1052
1084
|
// The alias/UUID/id the warning concerns.
|
|
1053
|
-
ref:
|
|
1054
|
-
message:
|
|
1085
|
+
ref: z9.string().min(1),
|
|
1086
|
+
message: z9.string().min(1)
|
|
1055
1087
|
}).strict();
|
|
1056
|
-
var readSetEntrySchema =
|
|
1057
|
-
store_uuid:
|
|
1058
|
-
alias:
|
|
1059
|
-
remote:
|
|
1088
|
+
var readSetEntrySchema = z9.object({
|
|
1089
|
+
store_uuid: z9.string().min(1),
|
|
1090
|
+
alias: z9.string().min(1),
|
|
1091
|
+
remote: z9.string().min(1).optional(),
|
|
1060
1092
|
// Whether this store accepts writes from the current context. Personal
|
|
1061
1093
|
// store is writable; shared stores writable iff mounted with write intent.
|
|
1062
|
-
writable:
|
|
1094
|
+
writable: z9.boolean()
|
|
1063
1095
|
}).strict();
|
|
1064
|
-
var storeReadSetSchema =
|
|
1065
|
-
stores:
|
|
1066
|
-
warnings:
|
|
1096
|
+
var storeReadSetSchema = z9.object({
|
|
1097
|
+
stores: z9.array(readSetEntrySchema),
|
|
1098
|
+
warnings: z9.array(storeResolverWarningSchema)
|
|
1067
1099
|
}).strict();
|
|
1068
|
-
var writeTargetSchema =
|
|
1069
|
-
store_uuid:
|
|
1070
|
-
alias:
|
|
1100
|
+
var writeTargetSchema = z9.object({
|
|
1101
|
+
store_uuid: z9.string().min(1),
|
|
1102
|
+
alias: z9.string().min(1)
|
|
1071
1103
|
}).strict();
|
|
1072
|
-
var storeResolveInputSchema =
|
|
1104
|
+
var storeResolveInputSchema = z9.object({
|
|
1073
1105
|
// Machine identity (S33) — namespaces personal ids; identifies personal store.
|
|
1074
|
-
uid:
|
|
1106
|
+
uid: z9.string().min(1),
|
|
1075
1107
|
// Stores mounted on this machine (from global config).
|
|
1076
|
-
mountedStores:
|
|
1077
|
-
|
|
1078
|
-
store_uuid:
|
|
1079
|
-
alias:
|
|
1080
|
-
|
|
1081
|
-
|
|
1108
|
+
mountedStores: z9.array(
|
|
1109
|
+
z9.object({
|
|
1110
|
+
store_uuid: z9.string().min(1),
|
|
1111
|
+
alias: z9.string().min(1),
|
|
1112
|
+
mount_name: z9.string().min(1).optional(),
|
|
1113
|
+
remote: z9.string().min(1).optional(),
|
|
1114
|
+
writable: z9.boolean().default(true),
|
|
1082
1115
|
// Marks the implicit personal store.
|
|
1083
|
-
personal:
|
|
1116
|
+
personal: z9.boolean().default(false)
|
|
1084
1117
|
}).strict()
|
|
1085
1118
|
),
|
|
1086
1119
|
// The project's declared required_stores (ids/aliases + optional remote).
|
|
1087
|
-
requiredStores:
|
|
1088
|
-
|
|
1089
|
-
id:
|
|
1090
|
-
suggested_remote:
|
|
1120
|
+
requiredStores: z9.array(
|
|
1121
|
+
z9.object({
|
|
1122
|
+
id: z9.string().min(1),
|
|
1123
|
+
suggested_remote: z9.string().min(1).optional()
|
|
1091
1124
|
}).strict()
|
|
1092
1125
|
),
|
|
1093
1126
|
// Alias selected as the active write store for non-personal scopes, if any.
|
|
1094
|
-
activeWriteAlias:
|
|
1127
|
+
activeWriteAlias: z9.string().min(1).optional(),
|
|
1128
|
+
// Scope-aware write routes. Exact scope wins first, then longest prefix route.
|
|
1129
|
+
writeRoutes: z9.array(
|
|
1130
|
+
z9.object({
|
|
1131
|
+
scope: z9.string().min(1),
|
|
1132
|
+
store: z9.string().min(1)
|
|
1133
|
+
}).strict()
|
|
1134
|
+
).optional().default([]),
|
|
1135
|
+
defaultWriteAlias: z9.string().min(1).optional()
|
|
1095
1136
|
}).strict();
|
|
1096
|
-
var projectRootGoldenCaseSchema =
|
|
1097
|
-
name:
|
|
1098
|
-
note:
|
|
1137
|
+
var projectRootGoldenCaseSchema = z9.object({
|
|
1138
|
+
name: z9.string().min(1),
|
|
1139
|
+
note: z9.string().optional(),
|
|
1099
1140
|
signals: projectRootSignalsSchema,
|
|
1100
1141
|
// null expected = resolver should return no root for these signals.
|
|
1101
1142
|
expected: projectRootResolutionSchema.nullable()
|
|
1102
1143
|
}).strict();
|
|
1103
|
-
var projectRootGoldenFileSchema =
|
|
1104
|
-
contract:
|
|
1105
|
-
cases:
|
|
1144
|
+
var projectRootGoldenFileSchema = z9.object({
|
|
1145
|
+
contract: z9.literal("project-root.golden"),
|
|
1146
|
+
cases: z9.array(projectRootGoldenCaseSchema).min(1)
|
|
1106
1147
|
}).strict();
|
|
1107
|
-
var readSetGoldenCaseSchema =
|
|
1108
|
-
name:
|
|
1109
|
-
note:
|
|
1148
|
+
var readSetGoldenCaseSchema = z9.object({
|
|
1149
|
+
name: z9.string().min(1),
|
|
1150
|
+
note: z9.string().optional(),
|
|
1110
1151
|
input: storeResolveInputSchema,
|
|
1111
1152
|
// Scope under test for the write-target expectation.
|
|
1112
|
-
writeScope:
|
|
1113
|
-
expected:
|
|
1153
|
+
writeScope: z9.string().min(1),
|
|
1154
|
+
expected: z9.object({
|
|
1114
1155
|
readSet: storeReadSetSchema,
|
|
1115
1156
|
writeTarget: writeTargetSchema.nullable(),
|
|
1116
|
-
writeWarnings:
|
|
1157
|
+
writeWarnings: z9.array(storeResolverWarningSchema)
|
|
1117
1158
|
}).strict()
|
|
1118
1159
|
}).strict();
|
|
1119
|
-
var readSetGoldenFileSchema =
|
|
1120
|
-
contract:
|
|
1121
|
-
cases:
|
|
1160
|
+
var readSetGoldenFileSchema = z9.object({
|
|
1161
|
+
contract: z9.literal("read-set.golden"),
|
|
1162
|
+
cases: z9.array(readSetGoldenCaseSchema).min(1)
|
|
1122
1163
|
}).strict();
|
|
1123
1164
|
|
|
1124
1165
|
// src/resolver/project-root-resolver.ts
|
|
1125
|
-
var ResolverNotImplementedError = class extends Error {
|
|
1126
|
-
constructor(what) {
|
|
1127
|
-
super(`${what} is not implemented yet (TDD target)`);
|
|
1128
|
-
this.name = "ResolverNotImplementedError";
|
|
1129
|
-
}
|
|
1130
|
-
};
|
|
1131
1166
|
function createProjectRootResolver() {
|
|
1132
1167
|
return {
|
|
1133
1168
|
resolve(signals) {
|
|
@@ -1165,12 +1200,65 @@ function personalEntry(input) {
|
|
|
1165
1200
|
}
|
|
1166
1201
|
return entry;
|
|
1167
1202
|
}
|
|
1203
|
+
function readSetEntryFromMounted(store) {
|
|
1204
|
+
const entry = {
|
|
1205
|
+
store_uuid: store.store_uuid,
|
|
1206
|
+
alias: store.alias,
|
|
1207
|
+
writable: store.writable
|
|
1208
|
+
};
|
|
1209
|
+
if (store.remote !== void 0) {
|
|
1210
|
+
entry.remote = store.remote;
|
|
1211
|
+
return { entry };
|
|
1212
|
+
}
|
|
1213
|
+
return {
|
|
1214
|
+
entry,
|
|
1215
|
+
warning: {
|
|
1216
|
+
code: "local_only_no_remote",
|
|
1217
|
+
ref: store.alias,
|
|
1218
|
+
message: `store '${store.alias}' is local-only; add a git remote to back it up (\`fabric store ... \` / doctor nudge)`
|
|
1219
|
+
}
|
|
1220
|
+
};
|
|
1221
|
+
}
|
|
1222
|
+
function isMountedPersonal(input, storeUuid) {
|
|
1223
|
+
return input.mountedStores.some((store) => store.store_uuid === storeUuid && store.personal);
|
|
1224
|
+
}
|
|
1225
|
+
function routeMatches(routeScope, scope) {
|
|
1226
|
+
return scope === routeScope || scope.startsWith(`${routeScope}:`);
|
|
1227
|
+
}
|
|
1228
|
+
function resolveRouteAlias(input, scope) {
|
|
1229
|
+
const routes = input.writeRoutes ?? [];
|
|
1230
|
+
const exact = routes.find((route) => route.scope === scope);
|
|
1231
|
+
if (exact !== void 0) {
|
|
1232
|
+
return exact.store;
|
|
1233
|
+
}
|
|
1234
|
+
const prefix = routes.filter((route) => routeMatches(route.scope, scope)).sort((a, b) => b.scope.length - a.scope.length)[0];
|
|
1235
|
+
return prefix?.store ?? input.defaultWriteAlias ?? input.activeWriteAlias;
|
|
1236
|
+
}
|
|
1237
|
+
function hasMultipleSharedStores(input, readSet) {
|
|
1238
|
+
return readSet.stores.filter((store) => !isMountedPersonal(input, store.store_uuid)).length > 1;
|
|
1239
|
+
}
|
|
1240
|
+
function hasExplicitRouteOrDefault(input, scope) {
|
|
1241
|
+
const routes = input.writeRoutes ?? [];
|
|
1242
|
+
return routes.some((route) => routeMatches(route.scope, scope)) || input.defaultWriteAlias !== void 0;
|
|
1243
|
+
}
|
|
1168
1244
|
function createStoreResolver() {
|
|
1169
1245
|
return {
|
|
1170
1246
|
resolveReadSet(input) {
|
|
1171
1247
|
const stores = [];
|
|
1172
1248
|
const warnings = [];
|
|
1249
|
+
const seenStoreUuids = /* @__PURE__ */ new Set();
|
|
1173
1250
|
for (const req of input.requiredStores) {
|
|
1251
|
+
if (req.suggested_remote === "$personal") {
|
|
1252
|
+
const personal2 = findPersonal(input);
|
|
1253
|
+
if (personal2 === void 0) {
|
|
1254
|
+
warnings.push({
|
|
1255
|
+
code: "missing_store",
|
|
1256
|
+
ref: req.id,
|
|
1257
|
+
message: `required store '${req.id}' is not mounted; run \`fabric store add\` (suggested remote: $personal)`
|
|
1258
|
+
});
|
|
1259
|
+
}
|
|
1260
|
+
continue;
|
|
1261
|
+
}
|
|
1174
1262
|
const matched = input.mountedStores.find(
|
|
1175
1263
|
(m) => !m.personal && (m.alias === req.id || m.store_uuid === req.id)
|
|
1176
1264
|
);
|
|
@@ -1183,25 +1271,20 @@ function createStoreResolver() {
|
|
|
1183
1271
|
});
|
|
1184
1272
|
continue;
|
|
1185
1273
|
}
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
entry.remote = matched.remote;
|
|
1193
|
-
} else {
|
|
1194
|
-
warnings.push({
|
|
1195
|
-
code: "local_only_no_remote",
|
|
1196
|
-
ref: matched.alias,
|
|
1197
|
-
message: `store '${matched.alias}' is local-only; add a git remote to back it up (\`fabric store ... \` / doctor nudge)`
|
|
1198
|
-
});
|
|
1274
|
+
if (seenStoreUuids.has(matched.store_uuid)) {
|
|
1275
|
+
continue;
|
|
1276
|
+
}
|
|
1277
|
+
const { entry, warning } = readSetEntryFromMounted(matched);
|
|
1278
|
+
if (warning !== void 0) {
|
|
1279
|
+
warnings.push(warning);
|
|
1199
1280
|
}
|
|
1200
1281
|
stores.push(entry);
|
|
1282
|
+
seenStoreUuids.add(matched.store_uuid);
|
|
1201
1283
|
}
|
|
1202
1284
|
const personal = personalEntry(input);
|
|
1203
|
-
if (personal !== void 0) {
|
|
1285
|
+
if (personal !== void 0 && !seenStoreUuids.has(personal.store_uuid)) {
|
|
1204
1286
|
stores.push(personal);
|
|
1287
|
+
seenStoreUuids.add(personal.store_uuid);
|
|
1205
1288
|
}
|
|
1206
1289
|
return { stores, warnings };
|
|
1207
1290
|
},
|
|
@@ -1222,15 +1305,31 @@ function createStoreResolver() {
|
|
|
1222
1305
|
}
|
|
1223
1306
|
return { target: { store_uuid: p.store_uuid, alias: p.alias }, warnings: [] };
|
|
1224
1307
|
}
|
|
1225
|
-
const
|
|
1308
|
+
const readSet = this.resolveReadSet(input);
|
|
1309
|
+
if (hasMultipleSharedStores(input, readSet) && !hasExplicitRouteOrDefault(input, scope)) {
|
|
1310
|
+
return {
|
|
1311
|
+
target: null,
|
|
1312
|
+
warnings: [
|
|
1313
|
+
{
|
|
1314
|
+
code: "missing_write_route",
|
|
1315
|
+
ref: scope,
|
|
1316
|
+
message: `scope '${scope}' has no explicit write route; set \`fabric store route-write ${scope} <alias>\``
|
|
1317
|
+
}
|
|
1318
|
+
]
|
|
1319
|
+
};
|
|
1320
|
+
}
|
|
1321
|
+
const routeAlias = resolveRouteAlias(input, scope);
|
|
1322
|
+
const active = routeAlias === void 0 ? void 0 : readSet.stores.find(
|
|
1323
|
+
(store) => store.writable && !isMountedPersonal(input, store.store_uuid) && (store.alias === routeAlias || store.store_uuid === routeAlias)
|
|
1324
|
+
);
|
|
1226
1325
|
if (active === void 0) {
|
|
1227
1326
|
return {
|
|
1228
1327
|
target: null,
|
|
1229
1328
|
warnings: [
|
|
1230
1329
|
{
|
|
1231
1330
|
code: "alias_unresolved",
|
|
1232
|
-
ref:
|
|
1233
|
-
message: `no writable store for scope '${scope}'; set
|
|
1331
|
+
ref: routeAlias ?? scope,
|
|
1332
|
+
message: `no writable store for scope '${scope}'; set a write route or default write store`
|
|
1234
1333
|
}
|
|
1235
1334
|
]
|
|
1236
1335
|
};
|
|
@@ -1244,7 +1343,8 @@ function createStoreResolver() {
|
|
|
1244
1343
|
}
|
|
1245
1344
|
|
|
1246
1345
|
// src/resolver/store-disk-reader.ts
|
|
1247
|
-
import { existsSync, readdirSync, readFileSync
|
|
1346
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
|
|
1347
|
+
import { readFile } from "fs/promises";
|
|
1248
1348
|
import { join } from "path";
|
|
1249
1349
|
function readStoreIdentity(absDir) {
|
|
1250
1350
|
const identityFile = join(absDir, STORE_LAYOUT.identityFile);
|
|
@@ -1260,6 +1360,17 @@ function readStoreIdentity(absDir) {
|
|
|
1260
1360
|
const parsed = storeIdentitySchema.safeParse(raw);
|
|
1261
1361
|
return parsed.success ? parsed.data : null;
|
|
1262
1362
|
}
|
|
1363
|
+
async function readStoreIdentityAsync(absDir) {
|
|
1364
|
+
const identityFile = join(absDir, STORE_LAYOUT.identityFile);
|
|
1365
|
+
let raw;
|
|
1366
|
+
try {
|
|
1367
|
+
raw = JSON.parse(await readFile(identityFile, "utf8"));
|
|
1368
|
+
} catch {
|
|
1369
|
+
return null;
|
|
1370
|
+
}
|
|
1371
|
+
const parsed = storeIdentitySchema.safeParse(raw);
|
|
1372
|
+
return parsed.success ? parsed.data : null;
|
|
1373
|
+
}
|
|
1263
1374
|
function recognizeStoreDir(absDir) {
|
|
1264
1375
|
return readStoreIdentity(absDir) !== null;
|
|
1265
1376
|
}
|
|
@@ -1319,10 +1430,14 @@ function findStoreExecutableViolations(absDir, options = {}) {
|
|
|
1319
1430
|
const relPath = rel === "" ? entry : `${rel}/${entry}`;
|
|
1320
1431
|
let stat;
|
|
1321
1432
|
try {
|
|
1322
|
-
stat =
|
|
1433
|
+
stat = lstatSync(abs);
|
|
1323
1434
|
} catch {
|
|
1324
1435
|
continue;
|
|
1325
1436
|
}
|
|
1437
|
+
if (stat.isSymbolicLink()) {
|
|
1438
|
+
violations.push(relPath);
|
|
1439
|
+
continue;
|
|
1440
|
+
}
|
|
1326
1441
|
if (stat.isDirectory()) {
|
|
1327
1442
|
walk(abs, relPath, depth + 1);
|
|
1328
1443
|
continue;
|
|
@@ -1436,9 +1551,10 @@ function resolveCandidates(candidates, options = {}) {
|
|
|
1436
1551
|
}
|
|
1437
1552
|
|
|
1438
1553
|
// src/store/core.ts
|
|
1439
|
-
import {
|
|
1440
|
-
import {
|
|
1554
|
+
import { execFile } from "child_process";
|
|
1555
|
+
import { access, mkdir, readdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
1441
1556
|
import { join as join2 } from "path";
|
|
1557
|
+
import { promisify } from "util";
|
|
1442
1558
|
var STORE_PENDING_DIR = "pending";
|
|
1443
1559
|
var STORE_GITIGNORE = [
|
|
1444
1560
|
"# v2.1 store \u2014 volatile / derived data is never committed",
|
|
@@ -1447,74 +1563,288 @@ var STORE_GITIGNORE = [
|
|
|
1447
1563
|
".cache/",
|
|
1448
1564
|
""
|
|
1449
1565
|
].join("\n");
|
|
1450
|
-
|
|
1451
|
-
|
|
1566
|
+
var execFileAsync = promisify(execFile);
|
|
1567
|
+
async function git(cwd, args) {
|
|
1568
|
+
await execFileAsync("git", args, { cwd });
|
|
1452
1569
|
}
|
|
1453
|
-
function initStore(absDir, identity, options = {}) {
|
|
1570
|
+
async function initStore(absDir, identity, options = {}) {
|
|
1454
1571
|
const parsed = storeIdentitySchema.parse(identity);
|
|
1455
1572
|
const identityFile = join2(absDir, STORE_LAYOUT.identityFile);
|
|
1456
|
-
|
|
1573
|
+
try {
|
|
1574
|
+
await access(identityFile);
|
|
1457
1575
|
throw new Error(`store already initialized at ${absDir} (store.json exists)`);
|
|
1576
|
+
} catch (err) {
|
|
1577
|
+
if (err instanceof Error && err.message.includes("already initialized")) {
|
|
1578
|
+
throw err;
|
|
1579
|
+
}
|
|
1458
1580
|
}
|
|
1459
1581
|
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1460
|
-
|
|
1582
|
+
const typeDir = join2(absDir, STORE_LAYOUT.knowledgeDir, type);
|
|
1583
|
+
await mkdir(typeDir, { recursive: true });
|
|
1584
|
+
await writeFile(join2(typeDir, ".gitkeep"), "", "utf8");
|
|
1461
1585
|
}
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1586
|
+
await mkdir(join2(absDir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR), { recursive: true });
|
|
1587
|
+
await mkdir(join2(absDir, STORE_LAYOUT.bindingsDir), { recursive: true });
|
|
1588
|
+
await mkdir(join2(absDir, STORE_LAYOUT.stateDir), { recursive: true });
|
|
1589
|
+
await writeFile(identityFile, `${JSON.stringify(parsed, null, 2)}
|
|
1466
1590
|
`, "utf8");
|
|
1467
|
-
|
|
1591
|
+
await writeFile(join2(absDir, ".gitignore"), STORE_GITIGNORE, "utf8");
|
|
1468
1592
|
if (options.git !== false) {
|
|
1469
|
-
git(absDir, ["init", "-b", "main"]);
|
|
1593
|
+
await git(absDir, ["init", "-b", "main"]);
|
|
1470
1594
|
}
|
|
1471
|
-
const readBack =
|
|
1595
|
+
const readBack = await readStoreIdentityAsync(absDir);
|
|
1472
1596
|
if (readBack === null) {
|
|
1473
1597
|
throw new Error(`store init wrote an unrecognizable store.json at ${absDir}`);
|
|
1474
1598
|
}
|
|
1475
1599
|
return readBack;
|
|
1476
1600
|
}
|
|
1477
|
-
function listMarkdown(dir) {
|
|
1478
|
-
|
|
1601
|
+
async function listMarkdown(dir) {
|
|
1602
|
+
let entries;
|
|
1603
|
+
try {
|
|
1604
|
+
entries = await readdir(dir);
|
|
1605
|
+
} catch {
|
|
1479
1606
|
return [];
|
|
1480
1607
|
}
|
|
1481
|
-
return
|
|
1608
|
+
return entries.filter((name) => name.endsWith(".md")).sort().map((name) => join2(dir, name));
|
|
1482
1609
|
}
|
|
1483
|
-
function listStoreKnowledge(store) {
|
|
1610
|
+
async function listStoreKnowledge(store) {
|
|
1484
1611
|
const refs = [];
|
|
1485
1612
|
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1486
|
-
for (const file of listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, type))) {
|
|
1613
|
+
for (const file of await listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, type))) {
|
|
1487
1614
|
refs.push({ store_uuid: store.store_uuid, alias: store.alias, type, file });
|
|
1488
1615
|
}
|
|
1489
1616
|
}
|
|
1490
1617
|
return refs;
|
|
1491
1618
|
}
|
|
1492
|
-
function readKnowledgeAcrossStores(stores) {
|
|
1493
|
-
|
|
1619
|
+
async function readKnowledgeAcrossStores(stores) {
|
|
1620
|
+
const lists = await Promise.all(stores.map((store) => listStoreKnowledge(store)));
|
|
1621
|
+
return lists.flat();
|
|
1622
|
+
}
|
|
1623
|
+
function storeProjectsPath(storeDir) {
|
|
1624
|
+
return join2(storeDir, STORE_LAYOUT.projectsFile);
|
|
1625
|
+
}
|
|
1626
|
+
async function readStoreProjects(storeDir) {
|
|
1627
|
+
const path = storeProjectsPath(storeDir);
|
|
1628
|
+
let raw;
|
|
1629
|
+
try {
|
|
1630
|
+
raw = JSON.parse(await readFile2(path, "utf8"));
|
|
1631
|
+
} catch {
|
|
1632
|
+
return [];
|
|
1633
|
+
}
|
|
1634
|
+
const parsed = storeProjectsFileSchema.safeParse(raw);
|
|
1635
|
+
return parsed.success ? parsed.data.projects : [];
|
|
1636
|
+
}
|
|
1637
|
+
async function storeHasProject(storeDir, id) {
|
|
1638
|
+
return (await readStoreProjects(storeDir)).some((p) => p.id === id);
|
|
1494
1639
|
}
|
|
1495
|
-
function
|
|
1496
|
-
|
|
1497
|
-
|
|
1640
|
+
async function addStoreProject(storeDir, project) {
|
|
1641
|
+
const parsed = storeProjectSchema.parse(project);
|
|
1642
|
+
const existing = await readStoreProjects(storeDir);
|
|
1643
|
+
if (existing.some((p) => p.id === parsed.id)) {
|
|
1644
|
+
throw new Error(`project '${parsed.id}' already exists in store at ${storeDir}`);
|
|
1645
|
+
}
|
|
1646
|
+
const next = [...existing, parsed];
|
|
1647
|
+
const validated = storeProjectsFileSchema.parse({ projects: next });
|
|
1648
|
+
await writeFile(storeProjectsPath(storeDir), `${JSON.stringify(validated, null, 2)}
|
|
1649
|
+
`, "utf8");
|
|
1650
|
+
return validated.projects;
|
|
1651
|
+
}
|
|
1652
|
+
async function aggregatePendingAcrossStores(stores) {
|
|
1653
|
+
const lists = await Promise.all(stores.map(
|
|
1654
|
+
async (store) => (await listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR))).map((file) => ({
|
|
1498
1655
|
store_uuid: store.store_uuid,
|
|
1499
1656
|
alias: store.alias,
|
|
1500
1657
|
type: STORE_PENDING_DIR,
|
|
1501
1658
|
file
|
|
1502
1659
|
}))
|
|
1503
|
-
);
|
|
1660
|
+
));
|
|
1661
|
+
return lists.flat();
|
|
1662
|
+
}
|
|
1663
|
+
|
|
1664
|
+
// src/store/store-counters.ts
|
|
1665
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync } from "fs";
|
|
1666
|
+
import { join as join3 } from "path";
|
|
1667
|
+
function storeCountersPath(storeDir) {
|
|
1668
|
+
return join3(storeDir, STORE_LAYOUT.countersFile);
|
|
1669
|
+
}
|
|
1670
|
+
function readStoreCounters(storeDir) {
|
|
1671
|
+
let raw;
|
|
1672
|
+
try {
|
|
1673
|
+
raw = readFileSync2(storeCountersPath(storeDir), "utf8");
|
|
1674
|
+
} catch {
|
|
1675
|
+
return defaultAgentsMetaCounters();
|
|
1676
|
+
}
|
|
1677
|
+
let parsed;
|
|
1678
|
+
try {
|
|
1679
|
+
parsed = JSON.parse(raw);
|
|
1680
|
+
} catch {
|
|
1681
|
+
return defaultAgentsMetaCounters();
|
|
1682
|
+
}
|
|
1683
|
+
const result = AgentsMetaCountersSchema.safeParse(parsed);
|
|
1684
|
+
return result.success ? result.data : defaultAgentsMetaCounters();
|
|
1685
|
+
}
|
|
1686
|
+
function preserveCorruptCounters(path, raw) {
|
|
1687
|
+
const corruptedPath = `${path}.corrupted.${Date.now()}`;
|
|
1688
|
+
writeFileSync(corruptedPath, raw, "utf8");
|
|
1689
|
+
return corruptedPath;
|
|
1690
|
+
}
|
|
1691
|
+
function readStoreCountersForAllocation(storeDir) {
|
|
1692
|
+
const path = storeCountersPath(storeDir);
|
|
1693
|
+
if (!existsSync2(path)) {
|
|
1694
|
+
return defaultAgentsMetaCounters();
|
|
1695
|
+
}
|
|
1696
|
+
const raw = readFileSync2(path, "utf8");
|
|
1697
|
+
let parsed;
|
|
1698
|
+
try {
|
|
1699
|
+
parsed = JSON.parse(raw);
|
|
1700
|
+
} catch (error) {
|
|
1701
|
+
const corruptedPath = preserveCorruptCounters(path, raw);
|
|
1702
|
+
throw new Error(
|
|
1703
|
+
`store counters.json is corrupt; forensic copy saved to ${corruptedPath}. Run doctor --fix or reconcileStoreCounters before allocating a new stable_id. Parse error: ${error instanceof Error ? error.message : String(error)}`
|
|
1704
|
+
);
|
|
1705
|
+
}
|
|
1706
|
+
const result = AgentsMetaCountersSchema.safeParse(parsed);
|
|
1707
|
+
if (!result.success) {
|
|
1708
|
+
const corruptedPath = preserveCorruptCounters(path, raw);
|
|
1709
|
+
throw new Error(
|
|
1710
|
+
`store counters.json is schema-invalid; forensic copy saved to ${corruptedPath}. Run doctor --fix or reconcileStoreCounters before allocating a new stable_id.`
|
|
1711
|
+
);
|
|
1712
|
+
}
|
|
1713
|
+
return result.data;
|
|
1714
|
+
}
|
|
1715
|
+
async function allocateStoreKnowledgeId(layer, type, storeDir) {
|
|
1716
|
+
const countersPath = storeCountersPath(storeDir);
|
|
1717
|
+
return withFileLock(`${countersPath}.lock`, async () => {
|
|
1718
|
+
const counters = readStoreCountersForAllocation(storeDir);
|
|
1719
|
+
const { id, nextCounters } = allocateKnowledgeId(layer, type, counters);
|
|
1720
|
+
await atomicWriteJson(countersPath, nextCounters, { indent: 2 });
|
|
1721
|
+
return id;
|
|
1722
|
+
});
|
|
1723
|
+
}
|
|
1724
|
+
function readEntryId(file) {
|
|
1725
|
+
let content;
|
|
1726
|
+
try {
|
|
1727
|
+
content = readFileSync2(file, "utf8");
|
|
1728
|
+
} catch {
|
|
1729
|
+
return null;
|
|
1730
|
+
}
|
|
1731
|
+
const match = content.match(/^id:\s*(\S+)\s*$/mu);
|
|
1732
|
+
if (match) {
|
|
1733
|
+
return match[1] ?? null;
|
|
1734
|
+
}
|
|
1735
|
+
const stem = file.slice(file.lastIndexOf("/") + 1).replace(/\.md$/u, "");
|
|
1736
|
+
const idPart = stem.split("--")[0];
|
|
1737
|
+
return idPart.length > 0 ? idPart : null;
|
|
1738
|
+
}
|
|
1739
|
+
function reconcileStoreCounters(storeDir) {
|
|
1740
|
+
const current = readStoreCounters(storeDir);
|
|
1741
|
+
const next = {
|
|
1742
|
+
KP: { ...current.KP },
|
|
1743
|
+
KT: { ...current.KT }
|
|
1744
|
+
};
|
|
1745
|
+
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1746
|
+
const dir = join3(storeDir, STORE_LAYOUT.knowledgeDir, type);
|
|
1747
|
+
if (!existsSync2(dir)) {
|
|
1748
|
+
continue;
|
|
1749
|
+
}
|
|
1750
|
+
for (const name of readdirSync2(dir)) {
|
|
1751
|
+
if (!name.endsWith(".md")) {
|
|
1752
|
+
continue;
|
|
1753
|
+
}
|
|
1754
|
+
const parsed = parseKnowledgeId(readEntryId(join3(dir, name)) ?? "");
|
|
1755
|
+
if (parsed === null) {
|
|
1756
|
+
continue;
|
|
1757
|
+
}
|
|
1758
|
+
const layerKey = parsed.layer === "personal" ? "KP" : "KT";
|
|
1759
|
+
const typeCode = KNOWLEDGE_TYPE_CODES[parsed.type];
|
|
1760
|
+
next[layerKey][typeCode] = Math.max(next[layerKey][typeCode], parsed.counter);
|
|
1761
|
+
}
|
|
1762
|
+
}
|
|
1763
|
+
writeFileSync(storeCountersPath(storeDir), `${JSON.stringify(next, null, 2)}
|
|
1764
|
+
`, "utf8");
|
|
1765
|
+
return next;
|
|
1766
|
+
}
|
|
1767
|
+
|
|
1768
|
+
// src/store/project-config-io.ts
|
|
1769
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1770
|
+
import { join as join4 } from "path";
|
|
1771
|
+
function projectConfigPath(projectRoot) {
|
|
1772
|
+
return join4(projectRoot, ".fabric", "fabric-config.json");
|
|
1773
|
+
}
|
|
1774
|
+
function loadProjectConfig(projectRoot) {
|
|
1775
|
+
const path = projectConfigPath(projectRoot);
|
|
1776
|
+
if (!existsSync3(path)) {
|
|
1777
|
+
return null;
|
|
1778
|
+
}
|
|
1779
|
+
return fabricConfigSchema.parse(JSON.parse(readFileSync3(path, "utf8")));
|
|
1780
|
+
}
|
|
1781
|
+
function saveProjectConfig(config, projectRoot) {
|
|
1782
|
+
const validated = fabricConfigSchema.parse(config);
|
|
1783
|
+
mkdirSync(join4(projectRoot, ".fabric"), { recursive: true });
|
|
1784
|
+
writeFileSync2(projectConfigPath(projectRoot), `${JSON.stringify(validated, null, 2)}
|
|
1785
|
+
`, "utf8");
|
|
1786
|
+
}
|
|
1787
|
+
|
|
1788
|
+
// src/store/resolve-input.ts
|
|
1789
|
+
function buildStoreResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
1790
|
+
const global = loadGlobalConfig(globalRoot);
|
|
1791
|
+
if (global === null) {
|
|
1792
|
+
return null;
|
|
1793
|
+
}
|
|
1794
|
+
const project = loadProjectConfig(projectRoot);
|
|
1795
|
+
return {
|
|
1796
|
+
uid: global.uid,
|
|
1797
|
+
mountedStores: global.stores.map((s) => ({
|
|
1798
|
+
store_uuid: s.store_uuid,
|
|
1799
|
+
alias: s.alias,
|
|
1800
|
+
...s.mount_name === void 0 ? {} : { mount_name: s.mount_name },
|
|
1801
|
+
...s.remote === void 0 ? {} : { remote: s.remote },
|
|
1802
|
+
writable: s.writable ?? true,
|
|
1803
|
+
personal: s.personal ?? false
|
|
1804
|
+
})),
|
|
1805
|
+
requiredStores: (project?.required_stores ?? []).map(
|
|
1806
|
+
(r) => ({
|
|
1807
|
+
id: r.id,
|
|
1808
|
+
...r.suggested_remote === void 0 ? {} : { suggested_remote: r.suggested_remote }
|
|
1809
|
+
})
|
|
1810
|
+
),
|
|
1811
|
+
...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store },
|
|
1812
|
+
writeRoutes: project?.write_routes ?? [],
|
|
1813
|
+
...project?.default_write_store === void 0 ? {} : { defaultWriteAlias: project.default_write_store }
|
|
1814
|
+
};
|
|
1504
1815
|
}
|
|
1505
1816
|
|
|
1506
1817
|
// src/store/secret-scan.ts
|
|
1507
|
-
var
|
|
1508
|
-
{ rule: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b
|
|
1509
|
-
{ rule: "private-key-block", re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY
|
|
1510
|
-
{ rule: "openai-api-key", re: /\bsk-[A-Za-z0-9]{20,}\b
|
|
1511
|
-
{ rule: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b
|
|
1512
|
-
{ rule: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b
|
|
1818
|
+
var CREDENTIAL_RULES = [
|
|
1819
|
+
{ rule: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/, category: "credential" },
|
|
1820
|
+
{ rule: "private-key-block", re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/, category: "credential" },
|
|
1821
|
+
{ rule: "openai-api-key", re: /\bsk-[A-Za-z0-9]{20,}\b/, category: "credential" },
|
|
1822
|
+
{ rule: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/, category: "credential" },
|
|
1823
|
+
{ rule: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, category: "credential" },
|
|
1513
1824
|
{
|
|
1514
1825
|
rule: "credential-assignment",
|
|
1515
|
-
re: /(?:password|passwd|secret|api[_-]?key|access[_-]?token|token)\s*[:=]\s*['"][^'"\s]{8,}[
|
|
1826
|
+
re: /(?:password|passwd|secret|api[_-]?key|access[_-]?token|token)\s*[:=]\s*(?:"[^'"\s]{8,}"|'[^'"\s]{8,}'|[A-Za-z0-9_./+=:@-]{8,})/i,
|
|
1827
|
+
category: "credential"
|
|
1828
|
+
}
|
|
1829
|
+
];
|
|
1830
|
+
var PII_RULES = [
|
|
1831
|
+
{
|
|
1832
|
+
rule: "email-address",
|
|
1833
|
+
re: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i,
|
|
1834
|
+
category: "pii"
|
|
1835
|
+
},
|
|
1836
|
+
{
|
|
1837
|
+
rule: "ipv4-address",
|
|
1838
|
+
re: /\b(?:(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\.){3}(?:25[0-5]|2[0-4]\d|1\d\d|[1-9]?\d)\b/,
|
|
1839
|
+
category: "pii"
|
|
1840
|
+
},
|
|
1841
|
+
{
|
|
1842
|
+
rule: "phone-number",
|
|
1843
|
+
re: /(?<!\d)(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?)\d{3}[-.\s]?\d{4}(?!\d)/,
|
|
1844
|
+
category: "pii"
|
|
1516
1845
|
}
|
|
1517
1846
|
];
|
|
1847
|
+
var SECRET_RULES = [...CREDENTIAL_RULES, ...PII_RULES];
|
|
1518
1848
|
function scanForSecrets(content) {
|
|
1519
1849
|
const findings = [];
|
|
1520
1850
|
const lines = content.split(/\r?\n/u);
|
|
@@ -1528,18 +1858,36 @@ function scanForSecrets(content) {
|
|
|
1528
1858
|
return findings;
|
|
1529
1859
|
}
|
|
1530
1860
|
function hasSecrets(content) {
|
|
1531
|
-
|
|
1861
|
+
const lines = content.split(/\r?\n/u);
|
|
1862
|
+
for (const line of lines) {
|
|
1863
|
+
for (const { re } of CREDENTIAL_RULES) {
|
|
1864
|
+
if (re.test(line)) {
|
|
1865
|
+
return true;
|
|
1866
|
+
}
|
|
1867
|
+
}
|
|
1868
|
+
}
|
|
1869
|
+
return false;
|
|
1532
1870
|
}
|
|
1533
1871
|
var REDACTION_PLACEHOLDER_PREFIX = "[REDACTED:";
|
|
1534
1872
|
function redactSecrets(content) {
|
|
1873
|
+
return redactByRules(content, SECRET_RULES);
|
|
1874
|
+
}
|
|
1875
|
+
function redactPii(content) {
|
|
1876
|
+
return redactByRules(content, PII_RULES);
|
|
1877
|
+
}
|
|
1878
|
+
function redactByRules(content, rules) {
|
|
1535
1879
|
let out = content;
|
|
1536
|
-
for (const { rule, re } of
|
|
1880
|
+
for (const { rule, re } of rules) {
|
|
1537
1881
|
const flags = re.flags.includes("i") ? "gi" : "g";
|
|
1538
1882
|
out = out.replace(new RegExp(re.source, flags), `${REDACTION_PLACEHOLDER_PREFIX}${rule}]`);
|
|
1539
1883
|
}
|
|
1540
1884
|
return out;
|
|
1541
1885
|
}
|
|
1542
1886
|
function scrubRemoteUrl(remote) {
|
|
1887
|
+
const httpStripped = remote.replace(/^(https?:\/\/)[^/@]+@/i, "$1");
|
|
1888
|
+
if (httpStripped !== remote) {
|
|
1889
|
+
return httpStripped;
|
|
1890
|
+
}
|
|
1543
1891
|
return remote.replace(
|
|
1544
1892
|
/^([a-zA-Z][a-zA-Z0-9+.-]*:\/\/)[^/@]*:[^/@]*@/,
|
|
1545
1893
|
"$1"
|
|
@@ -1652,10 +2000,10 @@ function buildDebugBundle(input) {
|
|
|
1652
2000
|
}
|
|
1653
2001
|
|
|
1654
2002
|
// src/schemas/provenance.ts
|
|
1655
|
-
import { z as
|
|
1656
|
-
var knowledgeProvenanceSchema =
|
|
2003
|
+
import { z as z10 } from "zod";
|
|
2004
|
+
var knowledgeProvenanceSchema = z10.object({
|
|
1657
2005
|
store_uuid: storeUuidSchema,
|
|
1658
|
-
alias:
|
|
2006
|
+
alias: z10.string().min(1),
|
|
1659
2007
|
local_id: localKnowledgeIdSchema,
|
|
1660
2008
|
global_ref: globalRefSchema,
|
|
1661
2009
|
// Optional scope coordinate of the entry (resolution axis); present when the
|
|
@@ -1664,7 +2012,7 @@ var knowledgeProvenanceSchema = z12.object({
|
|
|
1664
2012
|
}).strict();
|
|
1665
2013
|
|
|
1666
2014
|
// src/schemas/mcp-store-contracts.ts
|
|
1667
|
-
import { z as
|
|
2015
|
+
import { z as z11 } from "zod";
|
|
1668
2016
|
var MCP_STORE_AWARE_TOOLS = [
|
|
1669
2017
|
"fab_recall",
|
|
1670
2018
|
"fab_plan_context",
|
|
@@ -1673,14 +2021,14 @@ var MCP_STORE_AWARE_TOOLS = [
|
|
|
1673
2021
|
"fab_extract_knowledge",
|
|
1674
2022
|
"fab_review"
|
|
1675
2023
|
];
|
|
1676
|
-
var storeAwareEntrySchema =
|
|
1677
|
-
stable_id:
|
|
2024
|
+
var storeAwareEntrySchema = z11.object({
|
|
2025
|
+
stable_id: z11.string(),
|
|
1678
2026
|
global_ref: globalRefSchema,
|
|
1679
2027
|
provenance: knowledgeProvenanceSchema
|
|
1680
2028
|
}).strict();
|
|
1681
|
-
var writtenToStoreSchema =
|
|
2029
|
+
var writtenToStoreSchema = z11.object({
|
|
1682
2030
|
store_uuid: storeUuidSchema,
|
|
1683
|
-
alias:
|
|
2031
|
+
alias: z11.string().min(1)
|
|
1684
2032
|
}).strict();
|
|
1685
2033
|
var MCP_STORE_AWARE_CONTRACTS = {
|
|
1686
2034
|
fab_recall: { tool: "fab_recall", surfacesEntries: true, echoesWrittenStore: false },
|
|
@@ -1693,7 +2041,7 @@ var MCP_STORE_AWARE_CONTRACTS = {
|
|
|
1693
2041
|
fab_archive_scan: {
|
|
1694
2042
|
tool: "fab_archive_scan",
|
|
1695
2043
|
surfacesEntries: false,
|
|
1696
|
-
echoesWrittenStore:
|
|
2044
|
+
echoesWrittenStore: false
|
|
1697
2045
|
},
|
|
1698
2046
|
fab_extract_knowledge: {
|
|
1699
2047
|
tool: "fab_extract_knowledge",
|
|
@@ -1704,50 +2052,184 @@ var MCP_STORE_AWARE_CONTRACTS = {
|
|
|
1704
2052
|
};
|
|
1705
2053
|
|
|
1706
2054
|
// src/schemas/bindings-snapshot.ts
|
|
1707
|
-
import { z as
|
|
1708
|
-
var resolvedBindingsSnapshotSchema =
|
|
2055
|
+
import { z as z12 } from "zod";
|
|
2056
|
+
var resolvedBindingsSnapshotSchema = z12.object({
|
|
1709
2057
|
// Schema version of the snapshot document.
|
|
1710
|
-
version:
|
|
2058
|
+
version: z12.literal(1),
|
|
1711
2059
|
// The project this snapshot is bound to (S13).
|
|
1712
|
-
project_id:
|
|
2060
|
+
project_id: z12.string().min(1),
|
|
2061
|
+
// The local runtime binding key. Defaults to project_id for standard repos;
|
|
2062
|
+
// worktrees may isolate state by setting fabric-config.workspace_binding_id.
|
|
2063
|
+
workspace_binding_id: z12.string().min(1),
|
|
1713
2064
|
// ISO-8601 generation timestamp (provenance / staleness signal for doctor).
|
|
1714
|
-
generated_at:
|
|
2065
|
+
generated_at: z12.string().min(1),
|
|
1715
2066
|
// Pre-resolved read-set (required_stores ∪ implicit personal + warnings).
|
|
1716
2067
|
read_set: storeReadSetSchema,
|
|
1717
2068
|
// Pre-resolved active write target for non-personal scopes (null if none).
|
|
1718
|
-
write_target: writeTargetSchema.nullable()
|
|
2069
|
+
write_target: writeTargetSchema.nullable(),
|
|
2070
|
+
// Pre-computed store-backed knowledge counts, snapshotted at write time.
|
|
2071
|
+
// PROVENANCE ONLY: these are store-global counts cached in a per-workspace
|
|
2072
|
+
// file, so they go stale whenever store content changes out-of-band (a `git
|
|
2073
|
+
// pull` in the store repo, a sync run from a *different* bound workspace,
|
|
2074
|
+
// etc.) — the snapshot is only regenerated by install/sync/store-ops in the
|
|
2075
|
+
// workspace that runs them (KT-PIT-0017). Hooks MUST NOT trust these numbers
|
|
2076
|
+
// for nudges; they recount live from `knowledge_store_dirs`. Retained for
|
|
2077
|
+
// doctor provenance + backward-compatible fallback when dirs are absent.
|
|
2078
|
+
knowledge_stats: z12.object({
|
|
2079
|
+
pending_count: z12.number().int().nonnegative(),
|
|
2080
|
+
canonical_count: z12.number().int().nonnegative(),
|
|
2081
|
+
oldest_pending_mtime_ms: z12.number().nonnegative().nullable()
|
|
2082
|
+
}).strict().optional(),
|
|
2083
|
+
// Resolved absolute store ROOT dirs the knowledge_stats were derived from.
|
|
2084
|
+
// STABLE across content sync — they only change when mounts/bindings change,
|
|
2085
|
+
// which DOES regenerate the snapshot. Hooks walk `<dir>/knowledge/<type>` +
|
|
2086
|
+
// `<dir>/knowledge/pending` LIVE off these roots so nudge counts are always
|
|
2087
|
+
// fresh regardless of how store content changed (the underseed / review-
|
|
2088
|
+
// backlog false-positive root cure; pairs with knowledge_stats above).
|
|
2089
|
+
knowledge_store_dirs: z12.array(z12.string().min(1)).optional()
|
|
1719
2090
|
}).strict();
|
|
1720
2091
|
|
|
1721
2092
|
// src/store/bindings.ts
|
|
1722
|
-
import {
|
|
1723
|
-
|
|
1724
|
-
|
|
1725
|
-
|
|
2093
|
+
import {
|
|
2094
|
+
existsSync as existsSync4,
|
|
2095
|
+
mkdirSync as mkdirSync2,
|
|
2096
|
+
readdirSync as readdirSync3,
|
|
2097
|
+
readFileSync as readFileSync4,
|
|
2098
|
+
renameSync,
|
|
2099
|
+
statSync,
|
|
2100
|
+
unlinkSync,
|
|
2101
|
+
writeFileSync as writeFileSync3
|
|
2102
|
+
} from "fs";
|
|
2103
|
+
import { join as join5, resolve, sep } from "path";
|
|
2104
|
+
var SAFE_BINDING_ID = /^[A-Za-z0-9._-]+$/;
|
|
2105
|
+
function assertSafeBindingId(bindingId) {
|
|
2106
|
+
if (!SAFE_BINDING_ID.test(bindingId) || bindingId.includes("..")) {
|
|
2107
|
+
throw new Error(
|
|
2108
|
+
`bindingsSnapshotPath: refusing unsafe workspace_binding_id ${JSON.stringify(bindingId)} (must match ${SAFE_BINDING_ID} and contain no "..")`
|
|
2109
|
+
);
|
|
2110
|
+
}
|
|
2111
|
+
}
|
|
2112
|
+
function bindingsSnapshotPath(globalRoot, bindingId) {
|
|
2113
|
+
assertSafeBindingId(bindingId);
|
|
2114
|
+
const bindingsDir = resolve(join5(globalRoot, GLOBAL_STATE_DIR, GLOBAL_BINDINGS_DIR));
|
|
2115
|
+
const path = resolve(join5(bindingsDir, `${bindingId}_resolved.json`));
|
|
2116
|
+
if (path !== bindingsDir && !path.startsWith(bindingsDir + sep)) {
|
|
2117
|
+
throw new Error(`bindingsSnapshotPath: resolved path escapes bindings dir for ${JSON.stringify(bindingId)}`);
|
|
2118
|
+
}
|
|
2119
|
+
return path;
|
|
2120
|
+
}
|
|
2121
|
+
function resolveWorkspaceBindingId(config) {
|
|
2122
|
+
return config.workspace_binding_id ?? config.project_id;
|
|
2123
|
+
}
|
|
2124
|
+
function countMarkdownFiles(dir) {
|
|
2125
|
+
let count = 0;
|
|
2126
|
+
let oldestMtimeMs = null;
|
|
2127
|
+
if (!existsSync4(dir)) {
|
|
2128
|
+
return { count, oldestMtimeMs };
|
|
2129
|
+
}
|
|
2130
|
+
let entries;
|
|
2131
|
+
try {
|
|
2132
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2133
|
+
} catch {
|
|
2134
|
+
return { count, oldestMtimeMs };
|
|
2135
|
+
}
|
|
2136
|
+
for (const entry of entries) {
|
|
2137
|
+
const fullPath = join5(dir, entry.name);
|
|
2138
|
+
if (entry.isDirectory()) {
|
|
2139
|
+
const nested = countMarkdownFiles(fullPath);
|
|
2140
|
+
count += nested.count;
|
|
2141
|
+
if (nested.oldestMtimeMs !== null && (oldestMtimeMs === null || nested.oldestMtimeMs < oldestMtimeMs)) {
|
|
2142
|
+
oldestMtimeMs = nested.oldestMtimeMs;
|
|
2143
|
+
}
|
|
2144
|
+
continue;
|
|
2145
|
+
}
|
|
2146
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
2147
|
+
continue;
|
|
2148
|
+
}
|
|
2149
|
+
let mtimeMs;
|
|
2150
|
+
try {
|
|
2151
|
+
mtimeMs = statSync(fullPath).mtimeMs;
|
|
2152
|
+
} catch {
|
|
2153
|
+
continue;
|
|
2154
|
+
}
|
|
2155
|
+
count += 1;
|
|
2156
|
+
if (oldestMtimeMs === null || mtimeMs < oldestMtimeMs) {
|
|
2157
|
+
oldestMtimeMs = mtimeMs;
|
|
2158
|
+
}
|
|
2159
|
+
}
|
|
2160
|
+
return { count, oldestMtimeMs };
|
|
2161
|
+
}
|
|
2162
|
+
function collectKnowledgeStats(globalRoot, resolveInput, readSet) {
|
|
2163
|
+
let pendingCount = 0;
|
|
2164
|
+
let oldestPendingMtimeMs = null;
|
|
2165
|
+
let canonicalCount = 0;
|
|
2166
|
+
const storeDirs = [];
|
|
2167
|
+
for (const store of readSet.stores) {
|
|
2168
|
+
const mounted = resolveInput.mountedStores.find((entry) => entry.store_uuid === store.store_uuid) ?? {
|
|
2169
|
+
store_uuid: store.store_uuid
|
|
2170
|
+
};
|
|
2171
|
+
const storeDir = join5(globalRoot, storeRelativePathForMount(mounted));
|
|
2172
|
+
storeDirs.push(storeDir);
|
|
2173
|
+
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
2174
|
+
const canonical = countMarkdownFiles(join5(storeDir, STORE_LAYOUT.knowledgeDir, type));
|
|
2175
|
+
canonicalCount += canonical.count;
|
|
2176
|
+
}
|
|
2177
|
+
const pending = countMarkdownFiles(join5(storeDir, STORE_LAYOUT.knowledgeDir, "pending"));
|
|
2178
|
+
pendingCount += pending.count;
|
|
2179
|
+
if (pending.oldestMtimeMs !== null && (oldestPendingMtimeMs === null || pending.oldestMtimeMs < oldestPendingMtimeMs)) {
|
|
2180
|
+
oldestPendingMtimeMs = pending.oldestMtimeMs;
|
|
2181
|
+
}
|
|
2182
|
+
}
|
|
2183
|
+
return {
|
|
2184
|
+
stats: {
|
|
2185
|
+
pending_count: pendingCount,
|
|
2186
|
+
canonical_count: canonicalCount,
|
|
2187
|
+
oldest_pending_mtime_ms: oldestPendingMtimeMs
|
|
2188
|
+
},
|
|
2189
|
+
storeDirs
|
|
2190
|
+
};
|
|
2191
|
+
}
|
|
2192
|
+
function atomicWriteJsonSync(path, value) {
|
|
2193
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
2194
|
+
try {
|
|
2195
|
+
writeFileSync3(tmpPath, `${JSON.stringify(value, null, 2)}
|
|
2196
|
+
`, "utf8");
|
|
2197
|
+
renameSync(tmpPath, path);
|
|
2198
|
+
} catch (error) {
|
|
2199
|
+
try {
|
|
2200
|
+
unlinkSync(tmpPath);
|
|
2201
|
+
} catch {
|
|
2202
|
+
}
|
|
2203
|
+
throw error;
|
|
2204
|
+
}
|
|
1726
2205
|
}
|
|
1727
2206
|
function writeBindingsSnapshot(options) {
|
|
1728
2207
|
const resolver = createStoreResolver();
|
|
1729
2208
|
const read_set = resolver.resolveReadSet(options.resolveInput);
|
|
1730
2209
|
const { target } = resolver.resolveWriteTarget(options.resolveInput, options.writeScope);
|
|
2210
|
+
const { stats, storeDirs } = collectKnowledgeStats(options.globalRoot, options.resolveInput, read_set);
|
|
1731
2211
|
const snapshot = resolvedBindingsSnapshotSchema.parse({
|
|
1732
2212
|
version: 1,
|
|
1733
2213
|
project_id: options.projectId,
|
|
2214
|
+
workspace_binding_id: options.workspaceBindingId ?? options.projectId,
|
|
1734
2215
|
generated_at: options.now,
|
|
1735
2216
|
read_set,
|
|
1736
|
-
write_target: target
|
|
2217
|
+
write_target: target,
|
|
2218
|
+
knowledge_stats: stats,
|
|
2219
|
+
knowledge_store_dirs: storeDirs
|
|
1737
2220
|
});
|
|
1738
|
-
const path = bindingsSnapshotPath(options.globalRoot,
|
|
1739
|
-
mkdirSync2(
|
|
1740
|
-
|
|
1741
|
-
`, "utf8");
|
|
2221
|
+
const path = bindingsSnapshotPath(options.globalRoot, snapshot.workspace_binding_id);
|
|
2222
|
+
mkdirSync2(join5(path, ".."), { recursive: true });
|
|
2223
|
+
atomicWriteJsonSync(path, snapshot);
|
|
1742
2224
|
return snapshot;
|
|
1743
2225
|
}
|
|
1744
2226
|
function readBindingsSnapshot(globalRoot, projectId) {
|
|
1745
2227
|
const path = bindingsSnapshotPath(globalRoot, projectId);
|
|
1746
|
-
if (!
|
|
2228
|
+
if (!existsSync4(path)) {
|
|
1747
2229
|
return null;
|
|
1748
2230
|
}
|
|
1749
2231
|
try {
|
|
1750
|
-
const parsed = resolvedBindingsSnapshotSchema.safeParse(JSON.parse(
|
|
2232
|
+
const parsed = resolvedBindingsSnapshotSchema.safeParse(JSON.parse(readFileSync4(path, "utf8")));
|
|
1751
2233
|
return parsed.success ? parsed.data : null;
|
|
1752
2234
|
} catch {
|
|
1753
2235
|
return null;
|
|
@@ -1756,7 +2238,9 @@ function readBindingsSnapshot(globalRoot, projectId) {
|
|
|
1756
2238
|
|
|
1757
2239
|
// src/store/store-lifecycle.ts
|
|
1758
2240
|
function findMountedStore(config, aliasOrUuid) {
|
|
1759
|
-
return config.stores.find(
|
|
2241
|
+
return config.stores.find(
|
|
2242
|
+
(s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid || s.mount_name === aliasOrUuid
|
|
2243
|
+
);
|
|
1760
2244
|
}
|
|
1761
2245
|
function addMountedStore(config, store) {
|
|
1762
2246
|
const aliasClash = config.stores.find(
|
|
@@ -1767,12 +2251,32 @@ function addMountedStore(config, store) {
|
|
|
1767
2251
|
`alias '${store.alias}' already mounts store ${aliasClash.store_uuid}; choose another alias`
|
|
1768
2252
|
);
|
|
1769
2253
|
}
|
|
2254
|
+
const mountNameClash = store.mount_name === void 0 ? void 0 : config.stores.find(
|
|
2255
|
+
(s) => s.mount_name === store.mount_name && s.store_uuid !== store.store_uuid
|
|
2256
|
+
);
|
|
2257
|
+
if (mountNameClash !== void 0) {
|
|
2258
|
+
throw new Error(
|
|
2259
|
+
`mount_name '${store.mount_name}' already maps to store ${mountNameClash.store_uuid}; choose another mount_name`
|
|
2260
|
+
);
|
|
2261
|
+
}
|
|
1770
2262
|
const sanitized = store.remote === void 0 ? store : { ...store, remote: scrubRemoteUrl(store.remote) };
|
|
1771
2263
|
store = sanitized;
|
|
1772
2264
|
const existing = config.stores.find((s) => s.store_uuid === store.store_uuid);
|
|
1773
2265
|
const stores = existing === void 0 ? [...config.stores, store] : config.stores.map((s) => s.store_uuid === store.store_uuid ? store : s);
|
|
1774
2266
|
return { ...config, stores };
|
|
1775
2267
|
}
|
|
2268
|
+
function disambiguateAlias(existingAliases, desired) {
|
|
2269
|
+
const taken = new Set(existingAliases);
|
|
2270
|
+
if (!taken.has(desired)) {
|
|
2271
|
+
return desired;
|
|
2272
|
+
}
|
|
2273
|
+
for (let n = 2; ; n += 1) {
|
|
2274
|
+
const candidate = `${desired}-${n}`;
|
|
2275
|
+
if (!taken.has(candidate)) {
|
|
2276
|
+
return candidate;
|
|
2277
|
+
}
|
|
2278
|
+
}
|
|
2279
|
+
}
|
|
1776
2280
|
function detachMountedStore(config, alias) {
|
|
1777
2281
|
const detached = config.stores.find((s) => s.alias === alias) ?? null;
|
|
1778
2282
|
if (detached === null) {
|
|
@@ -1801,155 +2305,155 @@ function explainStore(config, alias) {
|
|
|
1801
2305
|
}
|
|
1802
2306
|
|
|
1803
2307
|
// src/schemas/forensic-report.ts
|
|
1804
|
-
import { z as
|
|
1805
|
-
var forensicCodeSampleSchema =
|
|
1806
|
-
path:
|
|
1807
|
-
lines:
|
|
1808
|
-
snippet:
|
|
1809
|
-
pattern_hint:
|
|
1810
|
-
});
|
|
1811
|
-
var forensicEvidenceAnchorSchema =
|
|
1812
|
-
file:
|
|
1813
|
-
line:
|
|
1814
|
-
snippet:
|
|
1815
|
-
});
|
|
1816
|
-
var forensicAssertionCoverageSchema =
|
|
1817
|
-
ratio:
|
|
1818
|
-
total:
|
|
1819
|
-
matched:
|
|
1820
|
-
co_occurring_patterns:
|
|
1821
|
-
});
|
|
1822
|
-
var forensicAssertionSchema =
|
|
1823
|
-
type:
|
|
1824
|
-
statement:
|
|
1825
|
-
confidence:
|
|
1826
|
-
evidence:
|
|
2308
|
+
import { z as z13 } from "zod";
|
|
2309
|
+
var forensicCodeSampleSchema = z13.object({
|
|
2310
|
+
path: z13.string(),
|
|
2311
|
+
lines: z13.string(),
|
|
2312
|
+
snippet: z13.string(),
|
|
2313
|
+
pattern_hint: z13.string()
|
|
2314
|
+
});
|
|
2315
|
+
var forensicEvidenceAnchorSchema = z13.object({
|
|
2316
|
+
file: z13.string(),
|
|
2317
|
+
line: z13.string(),
|
|
2318
|
+
snippet: z13.string()
|
|
2319
|
+
});
|
|
2320
|
+
var forensicAssertionCoverageSchema = z13.object({
|
|
2321
|
+
ratio: z13.number().min(0).max(1),
|
|
2322
|
+
total: z13.number().int().nonnegative(),
|
|
2323
|
+
matched: z13.number().int().nonnegative(),
|
|
2324
|
+
co_occurring_patterns: z13.array(z13.string())
|
|
2325
|
+
});
|
|
2326
|
+
var forensicAssertionSchema = z13.object({
|
|
2327
|
+
type: z13.enum(["framework", "pattern", "invariant", "domain"]),
|
|
2328
|
+
statement: z13.string(),
|
|
2329
|
+
confidence: z13.enum(["HIGH", "MEDIUM", "LOW"]),
|
|
2330
|
+
evidence: z13.array(forensicEvidenceAnchorSchema),
|
|
1827
2331
|
coverage: forensicAssertionCoverageSchema,
|
|
1828
|
-
proposed_rule:
|
|
1829
|
-
alternatives:
|
|
1830
|
-
});
|
|
1831
|
-
var forensicTopologySchema =
|
|
1832
|
-
total_files:
|
|
1833
|
-
by_ext:
|
|
1834
|
-
key_dirs:
|
|
1835
|
-
max_depth:
|
|
1836
|
-
});
|
|
1837
|
-
var forensicEntryPointSchema =
|
|
1838
|
-
path:
|
|
1839
|
-
reason:
|
|
1840
|
-
size_bytes:
|
|
1841
|
-
});
|
|
1842
|
-
var forensicFrameworkSchema =
|
|
1843
|
-
kind:
|
|
1844
|
-
version:
|
|
1845
|
-
subkind:
|
|
1846
|
-
evidence:
|
|
1847
|
-
});
|
|
1848
|
-
var forensicReadmeSchema =
|
|
1849
|
-
quality:
|
|
1850
|
-
line_count:
|
|
1851
|
-
has_contributing:
|
|
1852
|
-
});
|
|
1853
|
-
var candidateFileEntrySchema =
|
|
1854
|
-
path:
|
|
1855
|
-
family:
|
|
1856
|
-
rationale:
|
|
1857
|
-
});
|
|
1858
|
-
var forensicSamplingBudgetSchema =
|
|
1859
|
-
max_files:
|
|
1860
|
-
max_lines_per_file:
|
|
1861
|
-
});
|
|
1862
|
-
var forensicReportSchema =
|
|
1863
|
-
version:
|
|
1864
|
-
generated_at:
|
|
1865
|
-
generated_by:
|
|
1866
|
-
target:
|
|
1867
|
-
project_name:
|
|
2332
|
+
proposed_rule: z13.string().optional(),
|
|
2333
|
+
alternatives: z13.array(z13.string()).optional()
|
|
2334
|
+
});
|
|
2335
|
+
var forensicTopologySchema = z13.object({
|
|
2336
|
+
total_files: z13.number().int().nonnegative(),
|
|
2337
|
+
by_ext: z13.record(z13.number().int().nonnegative()),
|
|
2338
|
+
key_dirs: z13.array(z13.string()),
|
|
2339
|
+
max_depth: z13.number().int().nonnegative()
|
|
2340
|
+
});
|
|
2341
|
+
var forensicEntryPointSchema = z13.object({
|
|
2342
|
+
path: z13.string(),
|
|
2343
|
+
reason: z13.string(),
|
|
2344
|
+
size_bytes: z13.number().int().nonnegative().optional()
|
|
2345
|
+
});
|
|
2346
|
+
var forensicFrameworkSchema = z13.object({
|
|
2347
|
+
kind: z13.string(),
|
|
2348
|
+
version: z13.string(),
|
|
2349
|
+
subkind: z13.string(),
|
|
2350
|
+
evidence: z13.array(z13.string())
|
|
2351
|
+
});
|
|
2352
|
+
var forensicReadmeSchema = z13.object({
|
|
2353
|
+
quality: z13.enum(["missing", "stub", "ok"]),
|
|
2354
|
+
line_count: z13.number().int().nonnegative(),
|
|
2355
|
+
has_contributing: z13.boolean()
|
|
2356
|
+
});
|
|
2357
|
+
var candidateFileEntrySchema = z13.object({
|
|
2358
|
+
path: z13.string(),
|
|
2359
|
+
family: z13.enum(["entry", "component", "config", "test", "domain"]),
|
|
2360
|
+
rationale: z13.string()
|
|
2361
|
+
});
|
|
2362
|
+
var forensicSamplingBudgetSchema = z13.object({
|
|
2363
|
+
max_files: z13.literal(15),
|
|
2364
|
+
max_lines_per_file: z13.literal(100)
|
|
2365
|
+
});
|
|
2366
|
+
var forensicReportSchema = z13.object({
|
|
2367
|
+
version: z13.string(),
|
|
2368
|
+
generated_at: z13.string(),
|
|
2369
|
+
generated_by: z13.string(),
|
|
2370
|
+
target: z13.string(),
|
|
2371
|
+
project_name: z13.string(),
|
|
1868
2372
|
framework: forensicFrameworkSchema,
|
|
1869
2373
|
topology: forensicTopologySchema,
|
|
1870
|
-
entry_points:
|
|
1871
|
-
code_samples:
|
|
1872
|
-
assertions:
|
|
1873
|
-
candidate_files:
|
|
2374
|
+
entry_points: z13.array(forensicEntryPointSchema),
|
|
2375
|
+
code_samples: z13.array(forensicCodeSampleSchema),
|
|
2376
|
+
assertions: z13.array(forensicAssertionSchema),
|
|
2377
|
+
candidate_files: z13.array(candidateFileEntrySchema),
|
|
1874
2378
|
sampling_budget: forensicSamplingBudgetSchema,
|
|
1875
2379
|
readme: forensicReadmeSchema,
|
|
1876
|
-
recommendations_for_skill:
|
|
2380
|
+
recommendations_for_skill: z13.array(z13.string()).optional()
|
|
1877
2381
|
});
|
|
1878
2382
|
|
|
1879
2383
|
// src/schemas/init-context.ts
|
|
1880
|
-
import { z as
|
|
1881
|
-
var initContextFrameworkSchema =
|
|
1882
|
-
kind:
|
|
1883
|
-
version:
|
|
1884
|
-
subkind:
|
|
1885
|
-
});
|
|
1886
|
-
var initContextInvariantConfidenceSnapshotSchema =
|
|
1887
|
-
confidence:
|
|
1888
|
-
evidence_refs:
|
|
1889
|
-
});
|
|
1890
|
-
var initContextSourceEvidenceSchema =
|
|
1891
|
-
file:
|
|
1892
|
-
lines:
|
|
1893
|
-
});
|
|
1894
|
-
var initContextInvariantSchema =
|
|
1895
|
-
type:
|
|
1896
|
-
rule:
|
|
1897
|
-
rationale:
|
|
2384
|
+
import { z as z14 } from "zod";
|
|
2385
|
+
var initContextFrameworkSchema = z14.object({
|
|
2386
|
+
kind: z14.string(),
|
|
2387
|
+
version: z14.string(),
|
|
2388
|
+
subkind: z14.string()
|
|
2389
|
+
});
|
|
2390
|
+
var initContextInvariantConfidenceSnapshotSchema = z14.object({
|
|
2391
|
+
confidence: z14.enum(["HIGH", "MEDIUM", "LOW"]),
|
|
2392
|
+
evidence_refs: z14.array(z14.string())
|
|
2393
|
+
});
|
|
2394
|
+
var initContextSourceEvidenceSchema = z14.object({
|
|
2395
|
+
file: z14.string(),
|
|
2396
|
+
lines: z14.string()
|
|
2397
|
+
});
|
|
2398
|
+
var initContextInvariantSchema = z14.object({
|
|
2399
|
+
type: z14.enum(["ban", "require", "protect"]),
|
|
2400
|
+
rule: z14.string(),
|
|
2401
|
+
rationale: z14.string().optional(),
|
|
1898
2402
|
confidence_snapshot: initContextInvariantConfidenceSnapshotSchema.optional(),
|
|
1899
|
-
source_evidence:
|
|
1900
|
-
});
|
|
1901
|
-
var initContextDomainGroupSchema =
|
|
1902
|
-
name:
|
|
1903
|
-
paths:
|
|
1904
|
-
summary:
|
|
1905
|
-
topology_type:
|
|
1906
|
-
target_path:
|
|
1907
|
-
});
|
|
1908
|
-
var initContextInterviewTrailEntrySchema =
|
|
1909
|
-
phase:
|
|
1910
|
-
question:
|
|
1911
|
-
answer:
|
|
1912
|
-
presentation:
|
|
1913
|
-
user_corrections:
|
|
1914
|
-
});
|
|
1915
|
-
var initContextSchema =
|
|
2403
|
+
source_evidence: z14.array(initContextSourceEvidenceSchema).optional()
|
|
2404
|
+
});
|
|
2405
|
+
var initContextDomainGroupSchema = z14.object({
|
|
2406
|
+
name: z14.string(),
|
|
2407
|
+
paths: z14.array(z14.string()),
|
|
2408
|
+
summary: z14.string().optional(),
|
|
2409
|
+
topology_type: z14.enum(["mirror", "cross-cutting"]).optional(),
|
|
2410
|
+
target_path: z14.string().optional()
|
|
2411
|
+
});
|
|
2412
|
+
var initContextInterviewTrailEntrySchema = z14.object({
|
|
2413
|
+
phase: z14.string(),
|
|
2414
|
+
question: z14.string(),
|
|
2415
|
+
answer: z14.string(),
|
|
2416
|
+
presentation: z14.string().optional(),
|
|
2417
|
+
user_corrections: z14.array(z14.string()).optional()
|
|
2418
|
+
});
|
|
2419
|
+
var initContextSchema = z14.object({
|
|
1916
2420
|
framework: initContextFrameworkSchema,
|
|
1917
|
-
architecture_patterns:
|
|
1918
|
-
invariants:
|
|
1919
|
-
domain_groups:
|
|
1920
|
-
interview_trail:
|
|
1921
|
-
forensic_ref:
|
|
2421
|
+
architecture_patterns: z14.array(z14.string()),
|
|
2422
|
+
invariants: z14.array(initContextInvariantSchema),
|
|
2423
|
+
domain_groups: z14.array(initContextDomainGroupSchema),
|
|
2424
|
+
interview_trail: z14.array(initContextInterviewTrailEntrySchema),
|
|
2425
|
+
forensic_ref: z14.string()
|
|
1922
2426
|
});
|
|
1923
2427
|
|
|
1924
2428
|
// src/schemas/events.ts
|
|
1925
|
-
import { z as
|
|
1926
|
-
var metaUpdatedEventSchema =
|
|
1927
|
-
type:
|
|
2429
|
+
import { z as z15 } from "zod";
|
|
2430
|
+
var metaUpdatedEventSchema = z15.object({
|
|
2431
|
+
type: z15.literal("meta:updated"),
|
|
1928
2432
|
payload: agentsMetaSchema
|
|
1929
2433
|
});
|
|
1930
|
-
var lockDriftEventSchema =
|
|
1931
|
-
type:
|
|
1932
|
-
payload:
|
|
1933
|
-
locked:
|
|
1934
|
-
drifted:
|
|
2434
|
+
var lockDriftEventSchema = z15.object({
|
|
2435
|
+
type: z15.literal("lock:drift"),
|
|
2436
|
+
payload: z15.object({
|
|
2437
|
+
locked: z15.array(humanLockEntrySchema),
|
|
2438
|
+
drifted: z15.array(humanLockEntrySchema)
|
|
1935
2439
|
})
|
|
1936
2440
|
});
|
|
1937
|
-
var lockApprovedEventSchema =
|
|
1938
|
-
type:
|
|
1939
|
-
payload:
|
|
1940
|
-
locked:
|
|
1941
|
-
approved:
|
|
2441
|
+
var lockApprovedEventSchema = z15.object({
|
|
2442
|
+
type: z15.literal("lock:approved"),
|
|
2443
|
+
payload: z15.object({
|
|
2444
|
+
locked: z15.array(humanLockEntrySchema),
|
|
2445
|
+
approved: z15.array(humanLockEntrySchema)
|
|
1942
2446
|
})
|
|
1943
2447
|
});
|
|
1944
|
-
var ledgerAppendedEventSchema =
|
|
1945
|
-
type:
|
|
2448
|
+
var ledgerAppendedEventSchema = z15.object({
|
|
2449
|
+
type: z15.literal("ledger:appended"),
|
|
1946
2450
|
payload: ledgerEntrySchema
|
|
1947
2451
|
});
|
|
1948
|
-
var driftDetectedEventSchema =
|
|
1949
|
-
type:
|
|
2452
|
+
var driftDetectedEventSchema = z15.object({
|
|
2453
|
+
type: z15.literal("drift:detected"),
|
|
1950
2454
|
payload: forensicReportSchema
|
|
1951
2455
|
});
|
|
1952
|
-
var fabricEventSchema =
|
|
2456
|
+
var fabricEventSchema = z15.discriminatedUnion("type", [
|
|
1953
2457
|
metaUpdatedEventSchema,
|
|
1954
2458
|
lockDriftEventSchema,
|
|
1955
2459
|
lockApprovedEventSchema,
|
|
@@ -1958,7 +2462,7 @@ var fabricEventSchema = z17.discriminatedUnion("type", [
|
|
|
1958
2462
|
]);
|
|
1959
2463
|
|
|
1960
2464
|
// src/schemas/event-ledger.ts
|
|
1961
|
-
import { z as
|
|
2465
|
+
import { z as z16 } from "zod";
|
|
1962
2466
|
|
|
1963
2467
|
// src/cite-line-parser.ts
|
|
1964
2468
|
var ID_RE = /^K[TP]-[A-Z]+-\d+$/;
|
|
@@ -1972,17 +2476,12 @@ function splitStorePrefix(token) {
|
|
|
1972
2476
|
return colon === -1 ? { store: null, id: token } : { store: token.slice(0, colon), id: token.slice(colon + 1) };
|
|
1973
2477
|
}
|
|
1974
2478
|
var CHAINED_FROM_ID_RE = /chained-from\s+(K[TP]-[A-Z]+-\d+)/i;
|
|
1975
|
-
var LEGACY_CITE_TAG_REMAP = {
|
|
1976
|
-
planned: "applied",
|
|
1977
|
-
recalled: "applied",
|
|
1978
|
-
"chained-from": "applied"
|
|
1979
|
-
};
|
|
1980
2479
|
function normalizeCiteTag(rawTag) {
|
|
1981
2480
|
const head = rawTag.trim().split(/[\s:]+/)[0].toLowerCase();
|
|
1982
2481
|
if (head === "applied" || head === "dismissed" || head === "none") {
|
|
1983
2482
|
return head;
|
|
1984
2483
|
}
|
|
1985
|
-
return
|
|
2484
|
+
return "none";
|
|
1986
2485
|
}
|
|
1987
2486
|
function parseTag(rawTag) {
|
|
1988
2487
|
if (!rawTag) return "none";
|
|
@@ -2070,146 +2569,131 @@ function parseCiteLine(raw) {
|
|
|
2070
2569
|
}
|
|
2071
2570
|
|
|
2072
2571
|
// src/schemas/event-ledger.ts
|
|
2073
|
-
var citeTagSchema =
|
|
2572
|
+
var citeTagSchema = z16.preprocess(
|
|
2074
2573
|
(value) => typeof value === "string" ? normalizeCiteTag(value) : value,
|
|
2075
|
-
|
|
2574
|
+
z16.enum(["applied", "dismissed", "none"])
|
|
2076
2575
|
);
|
|
2077
2576
|
var eventLedgerEnvelopeSchema = {
|
|
2078
|
-
kind:
|
|
2079
|
-
id:
|
|
2080
|
-
ts:
|
|
2081
|
-
schema_version:
|
|
2082
|
-
correlation_id:
|
|
2083
|
-
session_id:
|
|
2577
|
+
kind: z16.literal("fabric-event"),
|
|
2578
|
+
id: z16.string(),
|
|
2579
|
+
ts: z16.number().int().nonnegative(),
|
|
2580
|
+
schema_version: z16.literal(1),
|
|
2581
|
+
correlation_id: z16.string().optional(),
|
|
2582
|
+
session_id: z16.string().optional()
|
|
2084
2583
|
};
|
|
2085
|
-
var stringRecordSchema =
|
|
2086
|
-
var knowledgeContextPlannedEventSchema =
|
|
2584
|
+
var stringRecordSchema = z16.record(z16.string());
|
|
2585
|
+
var knowledgeContextPlannedEventSchema = z16.object({
|
|
2087
2586
|
...eventLedgerEnvelopeSchema,
|
|
2088
|
-
event_type:
|
|
2089
|
-
target_paths:
|
|
2090
|
-
required_stable_ids:
|
|
2091
|
-
ai_selectable_stable_ids:
|
|
2092
|
-
final_stable_ids:
|
|
2093
|
-
selection_token:
|
|
2094
|
-
client_hash:
|
|
2095
|
-
intent:
|
|
2096
|
-
known_tech:
|
|
2097
|
-
diagnostics:
|
|
2098
|
-
});
|
|
2099
|
-
var knowledgeSelectionEventSchema =
|
|
2587
|
+
event_type: z16.literal("knowledge_context_planned"),
|
|
2588
|
+
target_paths: z16.array(z16.string()),
|
|
2589
|
+
required_stable_ids: z16.array(z16.string()),
|
|
2590
|
+
ai_selectable_stable_ids: z16.array(z16.string()),
|
|
2591
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2592
|
+
selection_token: z16.string().optional(),
|
|
2593
|
+
client_hash: z16.string().optional(),
|
|
2594
|
+
intent: z16.string().optional(),
|
|
2595
|
+
known_tech: z16.array(z16.string()).optional(),
|
|
2596
|
+
diagnostics: z16.array(z16.unknown()).optional()
|
|
2597
|
+
});
|
|
2598
|
+
var knowledgeSelectionEventSchema = z16.object({
|
|
2100
2599
|
...eventLedgerEnvelopeSchema,
|
|
2101
|
-
event_type:
|
|
2102
|
-
selection_token:
|
|
2103
|
-
target_paths:
|
|
2104
|
-
required_stable_ids:
|
|
2105
|
-
ai_selectable_stable_ids:
|
|
2106
|
-
ai_selected_stable_ids:
|
|
2107
|
-
final_stable_ids:
|
|
2600
|
+
event_type: z16.literal("knowledge_selection"),
|
|
2601
|
+
selection_token: z16.string(),
|
|
2602
|
+
target_paths: z16.array(z16.string()),
|
|
2603
|
+
required_stable_ids: z16.array(z16.string()),
|
|
2604
|
+
ai_selectable_stable_ids: z16.array(z16.string()),
|
|
2605
|
+
ai_selected_stable_ids: z16.array(z16.string()),
|
|
2606
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2108
2607
|
ai_selection_reasons: stringRecordSchema,
|
|
2109
|
-
rejected_stable_ids:
|
|
2110
|
-
ignored_stable_ids:
|
|
2608
|
+
rejected_stable_ids: z16.array(z16.string()),
|
|
2609
|
+
ignored_stable_ids: z16.array(z16.string())
|
|
2111
2610
|
});
|
|
2112
|
-
var knowledgeSectionsFetchedEventSchema =
|
|
2611
|
+
var knowledgeSectionsFetchedEventSchema = z16.object({
|
|
2113
2612
|
...eventLedgerEnvelopeSchema,
|
|
2114
|
-
event_type:
|
|
2115
|
-
selection_token:
|
|
2116
|
-
target_paths:
|
|
2117
|
-
requested_sections:
|
|
2118
|
-
final_stable_ids:
|
|
2119
|
-
ai_selected_stable_ids:
|
|
2120
|
-
diagnostics:
|
|
2121
|
-
});
|
|
2122
|
-
var editIntentCheckedEventSchema =
|
|
2613
|
+
event_type: z16.literal("knowledge_sections_fetched"),
|
|
2614
|
+
selection_token: z16.string(),
|
|
2615
|
+
target_paths: z16.array(z16.string()).optional(),
|
|
2616
|
+
requested_sections: z16.array(z16.string()),
|
|
2617
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2618
|
+
ai_selected_stable_ids: z16.array(z16.string()),
|
|
2619
|
+
diagnostics: z16.array(z16.unknown()).optional()
|
|
2620
|
+
});
|
|
2621
|
+
var editIntentCheckedEventSchema = z16.object({
|
|
2123
2622
|
...eventLedgerEnvelopeSchema,
|
|
2124
|
-
event_type:
|
|
2125
|
-
path:
|
|
2126
|
-
compliant:
|
|
2127
|
-
intent:
|
|
2128
|
-
ledger_entry_id:
|
|
2623
|
+
event_type: z16.literal("edit_intent_checked"),
|
|
2624
|
+
path: z16.string(),
|
|
2625
|
+
compliant: z16.boolean(),
|
|
2626
|
+
intent: z16.string(),
|
|
2627
|
+
ledger_entry_id: z16.string(),
|
|
2129
2628
|
// rc.35 TASK-07 (P0-2): add "hook" — emitted by the PreToolUse narrow hook
|
|
2130
2629
|
// for every Edit/Write/MultiEdit fire so cite-coverage doctor metrics see
|
|
2131
2630
|
// actual edit signals (previously editsTouched was permanently 0 because
|
|
2132
2631
|
// no production caller of appendLedgerEntry existed).
|
|
2133
|
-
ledger_source:
|
|
2134
|
-
commit_sha:
|
|
2135
|
-
parent_sha:
|
|
2136
|
-
parent_ledger_entry_id:
|
|
2137
|
-
diff_stat:
|
|
2138
|
-
annotation:
|
|
2139
|
-
matched_rule_context_ts:
|
|
2140
|
-
window_ms:
|
|
2141
|
-
});
|
|
2142
|
-
var knowledgeDriftDetectedEventSchema =
|
|
2632
|
+
ledger_source: z16.enum(["ai", "human", "hook"]).optional(),
|
|
2633
|
+
commit_sha: z16.string().optional(),
|
|
2634
|
+
parent_sha: z16.string().optional(),
|
|
2635
|
+
parent_ledger_entry_id: z16.string().optional(),
|
|
2636
|
+
diff_stat: z16.string().optional(),
|
|
2637
|
+
annotation: z16.string().optional(),
|
|
2638
|
+
matched_rule_context_ts: z16.number().int().nonnegative().nullable(),
|
|
2639
|
+
window_ms: z16.number().int().nonnegative()
|
|
2640
|
+
});
|
|
2641
|
+
var knowledgeDriftDetectedEventSchema = z16.object({
|
|
2143
2642
|
...eventLedgerEnvelopeSchema,
|
|
2144
|
-
event_type:
|
|
2145
|
-
revision:
|
|
2146
|
-
drifted_stable_ids:
|
|
2147
|
-
missing_files:
|
|
2148
|
-
stale_files:
|
|
2149
|
-
details:
|
|
2150
|
-
|
|
2151
|
-
file:
|
|
2152
|
-
stable_id:
|
|
2153
|
-
expected_hash:
|
|
2154
|
-
actual_hash:
|
|
2643
|
+
event_type: z16.literal("knowledge_drift_detected"),
|
|
2644
|
+
revision: z16.string().optional(),
|
|
2645
|
+
drifted_stable_ids: z16.array(z16.string()),
|
|
2646
|
+
missing_files: z16.array(z16.string()),
|
|
2647
|
+
stale_files: z16.array(z16.string()),
|
|
2648
|
+
details: z16.array(
|
|
2649
|
+
z16.object({
|
|
2650
|
+
file: z16.string(),
|
|
2651
|
+
stable_id: z16.string(),
|
|
2652
|
+
expected_hash: z16.string(),
|
|
2653
|
+
actual_hash: z16.string().nullable()
|
|
2155
2654
|
})
|
|
2156
2655
|
).optional()
|
|
2157
2656
|
});
|
|
2158
|
-
var mcpEventLedgerEventSchema =
|
|
2657
|
+
var mcpEventLedgerEventSchema = z16.object({
|
|
2159
2658
|
...eventLedgerEnvelopeSchema,
|
|
2160
|
-
event_type:
|
|
2161
|
-
mcp_event_id:
|
|
2162
|
-
stream_id:
|
|
2163
|
-
message:
|
|
2659
|
+
event_type: z16.literal("mcp_event"),
|
|
2660
|
+
mcp_event_id: z16.string(),
|
|
2661
|
+
stream_id: z16.string(),
|
|
2662
|
+
message: z16.unknown()
|
|
2164
2663
|
});
|
|
2165
|
-
var reapplyCompletedEventSchema =
|
|
2664
|
+
var reapplyCompletedEventSchema = z16.object({
|
|
2166
2665
|
...eventLedgerEnvelopeSchema,
|
|
2167
|
-
event_type:
|
|
2168
|
-
preserved_ledger:
|
|
2169
|
-
preserved_meta:
|
|
2170
|
-
rules_count:
|
|
2666
|
+
event_type: z16.literal("reapply_completed"),
|
|
2667
|
+
preserved_ledger: z16.boolean(),
|
|
2668
|
+
preserved_meta: z16.boolean(),
|
|
2669
|
+
rules_count: z16.number().int().nonnegative()
|
|
2171
2670
|
});
|
|
2172
|
-
var installDiffAppliedEventSchema =
|
|
2671
|
+
var installDiffAppliedEventSchema = z16.object({
|
|
2173
2672
|
...eventLedgerEnvelopeSchema,
|
|
2174
|
-
event_type:
|
|
2175
|
-
applied:
|
|
2176
|
-
canonical:
|
|
2177
|
-
drifted:
|
|
2673
|
+
event_type: z16.literal("install_diff_applied"),
|
|
2674
|
+
applied: z16.array(z16.string()),
|
|
2675
|
+
canonical: z16.array(z16.string()),
|
|
2676
|
+
drifted: z16.array(z16.string())
|
|
2178
2677
|
});
|
|
2179
|
-
var eventLedgerTruncatedEventSchema =
|
|
2678
|
+
var eventLedgerTruncatedEventSchema = z16.object({
|
|
2180
2679
|
...eventLedgerEnvelopeSchema,
|
|
2181
|
-
event_type:
|
|
2182
|
-
byte_offset:
|
|
2183
|
-
byte_length:
|
|
2184
|
-
corrupted_path:
|
|
2680
|
+
event_type: z16.literal("event_ledger_truncated"),
|
|
2681
|
+
byte_offset: z16.number().int().nonnegative(),
|
|
2682
|
+
byte_length: z16.number().int().nonnegative(),
|
|
2683
|
+
corrupted_path: z16.string()
|
|
2185
2684
|
});
|
|
2186
|
-
var
|
|
2187
|
-
...eventLedgerEnvelopeSchema,
|
|
2188
|
-
event_type: z18.literal("mcp_config_migrated"),
|
|
2189
|
-
source: z18.literal("doctor_fix"),
|
|
2190
|
-
removed_from: z18.string()
|
|
2191
|
-
});
|
|
2192
|
-
var bootstrapMarkerMigratedEventSchema = z18.object({
|
|
2193
|
-
...eventLedgerEnvelopeSchema,
|
|
2194
|
-
event_type: z18.literal("bootstrap_marker_migrated"),
|
|
2195
|
-
path: z18.string(),
|
|
2196
|
-
migrated_count: z18.number().int().nonnegative(),
|
|
2197
|
-
legacy_marker: z18.literal("fabric:knowledge-base"),
|
|
2198
|
-
new_marker: z18.literal("fabric:bootstrap"),
|
|
2199
|
-
timestamp: z18.string()
|
|
2200
|
-
});
|
|
2201
|
-
var metaReconciledOnStartupEventSchema = z18.object({
|
|
2685
|
+
var metaReconciledOnStartupEventSchema = z16.object({
|
|
2202
2686
|
...eventLedgerEnvelopeSchema,
|
|
2203
|
-
event_type:
|
|
2204
|
-
reconciled_files:
|
|
2205
|
-
duration_ms:
|
|
2206
|
-
source:
|
|
2687
|
+
event_type: z16.literal("meta_reconciled_on_startup"),
|
|
2688
|
+
reconciled_files: z16.array(z16.string()),
|
|
2689
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2690
|
+
source: z16.literal("reconcileKnowledge")
|
|
2207
2691
|
});
|
|
2208
|
-
var metaReconciledEventSchema =
|
|
2692
|
+
var metaReconciledEventSchema = z16.object({
|
|
2209
2693
|
...eventLedgerEnvelopeSchema,
|
|
2210
|
-
event_type:
|
|
2211
|
-
reconciled_files:
|
|
2212
|
-
duration_ms:
|
|
2694
|
+
event_type: z16.literal("meta_reconciled"),
|
|
2695
|
+
reconciled_files: z16.array(z16.string()),
|
|
2696
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2213
2697
|
// v2.0.0-rc.23 TASK-005 (a-B): added `auto-heal-description` trigger so the
|
|
2214
2698
|
// read-path plan_context handler can drive a full reconcile when it detects
|
|
2215
2699
|
// any node carrying `description === undefined` (legacy meta drift that the
|
|
@@ -2223,7 +2707,7 @@ var metaReconciledEventSchema = z18.object({
|
|
|
2223
2707
|
// v2.0.0-rc.29 TASK-005 (BUG-G1): `auto-heal-after-drift` added so
|
|
2224
2708
|
// `ensureKnowledgeFresh` hot-path can chain a paired reconcile (closing the
|
|
2225
2709
|
// drift→heal gap) when the caller opts in via `autoHealOnDrift: true`.
|
|
2226
|
-
trigger:
|
|
2710
|
+
trigger: z16.enum([
|
|
2227
2711
|
"doctor",
|
|
2228
2712
|
"manual",
|
|
2229
2713
|
"auto-heal-description",
|
|
@@ -2231,195 +2715,207 @@ var metaReconciledEventSchema = z18.object({
|
|
|
2231
2715
|
"post-approve",
|
|
2232
2716
|
"post-modify"
|
|
2233
2717
|
]),
|
|
2234
|
-
source:
|
|
2718
|
+
source: z16.literal("reconcileKnowledge"),
|
|
2235
2719
|
// v2.0.0-rc.22 TASK-014 (Scope E): set when reconcileKnowledge forced a
|
|
2236
2720
|
// writeKnowledgeMeta on revision drift alone (no per-file content drift).
|
|
2237
2721
|
// Distinguishes top-level schema/revision repair from the standard per-file
|
|
2238
2722
|
// drift path. Optional so existing emitters stay unchanged.
|
|
2239
|
-
force_write_reason:
|
|
2723
|
+
force_write_reason: z16.enum(["revision_drift"]).optional()
|
|
2240
2724
|
});
|
|
2241
|
-
var claudeSkillPathMigratedEventSchema =
|
|
2725
|
+
var claudeSkillPathMigratedEventSchema = z16.object({
|
|
2242
2726
|
...eventLedgerEnvelopeSchema,
|
|
2243
|
-
event_type:
|
|
2244
|
-
from:
|
|
2245
|
-
to:
|
|
2727
|
+
event_type: z16.literal("claude_skill_path_migrated"),
|
|
2728
|
+
from: z16.string(),
|
|
2729
|
+
to: z16.string()
|
|
2246
2730
|
});
|
|
2247
|
-
var claudeHookPathMigratedEventSchema =
|
|
2731
|
+
var claudeHookPathMigratedEventSchema = z16.object({
|
|
2248
2732
|
...eventLedgerEnvelopeSchema,
|
|
2249
|
-
event_type:
|
|
2250
|
-
from:
|
|
2251
|
-
to:
|
|
2733
|
+
event_type: z16.literal("claude_hook_path_migrated"),
|
|
2734
|
+
from: z16.string(),
|
|
2735
|
+
to: z16.string()
|
|
2252
2736
|
});
|
|
2253
|
-
var codexSkillPathMigratedEventSchema =
|
|
2737
|
+
var codexSkillPathMigratedEventSchema = z16.object({
|
|
2254
2738
|
...eventLedgerEnvelopeSchema,
|
|
2255
|
-
event_type:
|
|
2256
|
-
from:
|
|
2257
|
-
to:
|
|
2739
|
+
event_type: z16.literal("codex_skill_path_migrated"),
|
|
2740
|
+
from: z16.string(),
|
|
2741
|
+
to: z16.string()
|
|
2258
2742
|
});
|
|
2259
|
-
var initScanCompletedEventSchema =
|
|
2743
|
+
var initScanCompletedEventSchema = z16.object({
|
|
2260
2744
|
...eventLedgerEnvelopeSchema,
|
|
2261
|
-
event_type:
|
|
2262
|
-
written_stable_ids:
|
|
2263
|
-
duration_ms:
|
|
2264
|
-
source:
|
|
2745
|
+
event_type: z16.literal("init_scan_completed"),
|
|
2746
|
+
written_stable_ids: z16.array(z16.string()),
|
|
2747
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2748
|
+
source: z16.enum(["init", "scan", "doctor_fix", "doctor-rescan"]).optional()
|
|
2265
2749
|
});
|
|
2266
|
-
var knowledgeProposedEventSchema =
|
|
2750
|
+
var knowledgeProposedEventSchema = z16.object({
|
|
2267
2751
|
...eventLedgerEnvelopeSchema,
|
|
2268
|
-
event_type:
|
|
2269
|
-
stable_id:
|
|
2270
|
-
timestamp:
|
|
2271
|
-
reason:
|
|
2752
|
+
event_type: z16.literal("knowledge_proposed"),
|
|
2753
|
+
stable_id: z16.string().optional(),
|
|
2754
|
+
timestamp: z16.string().datetime(),
|
|
2755
|
+
reason: z16.string().optional()
|
|
2272
2756
|
});
|
|
2273
|
-
var knowledgePromoteStartedEventSchema =
|
|
2757
|
+
var knowledgePromoteStartedEventSchema = z16.object({
|
|
2274
2758
|
...eventLedgerEnvelopeSchema,
|
|
2275
|
-
event_type:
|
|
2276
|
-
stable_id:
|
|
2277
|
-
timestamp:
|
|
2278
|
-
reason:
|
|
2759
|
+
event_type: z16.literal("knowledge_promote_started"),
|
|
2760
|
+
stable_id: z16.string().optional(),
|
|
2761
|
+
timestamp: z16.string().datetime(),
|
|
2762
|
+
reason: z16.string().optional()
|
|
2279
2763
|
});
|
|
2280
|
-
var knowledgePromotedEventSchema =
|
|
2764
|
+
var knowledgePromotedEventSchema = z16.object({
|
|
2281
2765
|
...eventLedgerEnvelopeSchema,
|
|
2282
|
-
event_type:
|
|
2283
|
-
stable_id:
|
|
2284
|
-
timestamp:
|
|
2285
|
-
reason:
|
|
2766
|
+
event_type: z16.literal("knowledge_promoted"),
|
|
2767
|
+
stable_id: z16.string().optional(),
|
|
2768
|
+
timestamp: z16.string().datetime(),
|
|
2769
|
+
reason: z16.string().optional()
|
|
2286
2770
|
});
|
|
2287
|
-
var knowledgePromoteFailedEventSchema =
|
|
2771
|
+
var knowledgePromoteFailedEventSchema = z16.object({
|
|
2288
2772
|
...eventLedgerEnvelopeSchema,
|
|
2289
|
-
event_type:
|
|
2290
|
-
stable_id:
|
|
2291
|
-
timestamp:
|
|
2292
|
-
reason:
|
|
2773
|
+
event_type: z16.literal("knowledge_promote_failed"),
|
|
2774
|
+
stable_id: z16.string().optional(),
|
|
2775
|
+
timestamp: z16.string().datetime(),
|
|
2776
|
+
reason: z16.string()
|
|
2293
2777
|
});
|
|
2294
|
-
var
|
|
2778
|
+
var knowledgeModifiedEventSchema = z16.object({
|
|
2295
2779
|
...eventLedgerEnvelopeSchema,
|
|
2296
|
-
event_type:
|
|
2297
|
-
stable_id:
|
|
2298
|
-
timestamp:
|
|
2299
|
-
|
|
2300
|
-
|
|
2301
|
-
|
|
2780
|
+
event_type: z16.literal("knowledge_modified"),
|
|
2781
|
+
stable_id: z16.string().optional(),
|
|
2782
|
+
timestamp: z16.string().datetime(),
|
|
2783
|
+
path: z16.string(),
|
|
2784
|
+
changed_fields: z16.array(z16.string()),
|
|
2785
|
+
before: z16.record(z16.unknown()),
|
|
2786
|
+
after: z16.record(z16.unknown()),
|
|
2787
|
+
reason: z16.string().optional()
|
|
2788
|
+
});
|
|
2789
|
+
var knowledgeLayerChangedEventSchema = z16.object({
|
|
2790
|
+
...eventLedgerEnvelopeSchema,
|
|
2791
|
+
event_type: z16.literal("knowledge_layer_changed"),
|
|
2792
|
+
stable_id: z16.string().optional(),
|
|
2793
|
+
timestamp: z16.string().datetime(),
|
|
2794
|
+
reason: z16.string().optional(),
|
|
2795
|
+
from_layer: z16.enum(["team", "personal"]),
|
|
2796
|
+
to_layer: z16.enum(["team", "personal"]),
|
|
2302
2797
|
// v2.0.0-rc.37 NEW-24: record the pre-flip stable_id so downstream consumers
|
|
2303
2798
|
// (fab_plan_context redirect surface, fab_get_knowledge_sections.redirect_to)
|
|
2304
2799
|
// can map a stale caller-held id back to the post-flip canonical id without
|
|
2305
2800
|
// requiring the caller to re-issue plan-context. Optional for forward-
|
|
2306
2801
|
// compatibility with rc ≤36 events that never carried this field.
|
|
2307
|
-
previous_stable_id:
|
|
2802
|
+
previous_stable_id: z16.string().optional()
|
|
2308
2803
|
});
|
|
2309
|
-
var knowledgeIdRedirectEventSchema =
|
|
2804
|
+
var knowledgeIdRedirectEventSchema = z16.object({
|
|
2310
2805
|
...eventLedgerEnvelopeSchema,
|
|
2311
|
-
event_type:
|
|
2312
|
-
timestamp:
|
|
2313
|
-
previous_stable_id:
|
|
2314
|
-
new_stable_id:
|
|
2315
|
-
reason:
|
|
2806
|
+
event_type: z16.literal("knowledge_id_redirect"),
|
|
2807
|
+
timestamp: z16.string().datetime(),
|
|
2808
|
+
previous_stable_id: z16.string(),
|
|
2809
|
+
new_stable_id: z16.string(),
|
|
2810
|
+
reason: z16.string().optional()
|
|
2316
2811
|
});
|
|
2317
|
-
var knowledgeSlugRenamedEventSchema =
|
|
2812
|
+
var knowledgeSlugRenamedEventSchema = z16.object({
|
|
2318
2813
|
...eventLedgerEnvelopeSchema,
|
|
2319
|
-
event_type:
|
|
2320
|
-
stable_id:
|
|
2321
|
-
timestamp:
|
|
2322
|
-
reason:
|
|
2323
|
-
from_slug:
|
|
2324
|
-
to_slug:
|
|
2325
|
-
});
|
|
2326
|
-
var knowledgeDemotedEventSchema =
|
|
2814
|
+
event_type: z16.literal("knowledge_slug_renamed"),
|
|
2815
|
+
stable_id: z16.string().optional(),
|
|
2816
|
+
timestamp: z16.string().datetime(),
|
|
2817
|
+
reason: z16.string().optional(),
|
|
2818
|
+
from_slug: z16.string(),
|
|
2819
|
+
to_slug: z16.string()
|
|
2820
|
+
});
|
|
2821
|
+
var knowledgeDemotedEventSchema = z16.object({
|
|
2327
2822
|
...eventLedgerEnvelopeSchema,
|
|
2328
|
-
event_type:
|
|
2329
|
-
stable_id:
|
|
2330
|
-
timestamp:
|
|
2331
|
-
reason:
|
|
2823
|
+
event_type: z16.literal("knowledge_demoted"),
|
|
2824
|
+
stable_id: z16.string().optional(),
|
|
2825
|
+
timestamp: z16.string().datetime(),
|
|
2826
|
+
reason: z16.string().optional()
|
|
2332
2827
|
});
|
|
2333
|
-
var knowledgeArchivedEventSchema =
|
|
2828
|
+
var knowledgeArchivedEventSchema = z16.object({
|
|
2334
2829
|
...eventLedgerEnvelopeSchema,
|
|
2335
|
-
event_type:
|
|
2336
|
-
stable_id:
|
|
2337
|
-
timestamp:
|
|
2338
|
-
reason:
|
|
2830
|
+
event_type: z16.literal("knowledge_archived"),
|
|
2831
|
+
stable_id: z16.string().optional(),
|
|
2832
|
+
timestamp: z16.string().datetime(),
|
|
2833
|
+
reason: z16.string().optional()
|
|
2339
2834
|
});
|
|
2340
|
-
var knowledgeArchiveAttemptedEventSchema =
|
|
2835
|
+
var knowledgeArchiveAttemptedEventSchema = z16.object({
|
|
2341
2836
|
...eventLedgerEnvelopeSchema,
|
|
2342
|
-
event_type:
|
|
2343
|
-
stable_id:
|
|
2344
|
-
timestamp:
|
|
2345
|
-
reason:
|
|
2837
|
+
event_type: z16.literal("knowledge_archive_attempted"),
|
|
2838
|
+
stable_id: z16.string().optional(),
|
|
2839
|
+
timestamp: z16.string().datetime(),
|
|
2840
|
+
reason: z16.string().optional()
|
|
2346
2841
|
});
|
|
2347
|
-
var knowledgeUnarchivedEventSchema =
|
|
2842
|
+
var knowledgeUnarchivedEventSchema = z16.object({
|
|
2348
2843
|
...eventLedgerEnvelopeSchema,
|
|
2349
|
-
event_type:
|
|
2350
|
-
stable_id:
|
|
2351
|
-
timestamp:
|
|
2352
|
-
reason:
|
|
2844
|
+
event_type: z16.literal("knowledge_unarchived"),
|
|
2845
|
+
stable_id: z16.string().optional(),
|
|
2846
|
+
timestamp: z16.string().datetime(),
|
|
2847
|
+
reason: z16.string().optional(),
|
|
2353
2848
|
// Pre-move archive path (e.g. ".fabric/.archive/decisions/KT-D-0007--single-cjs-hook.md").
|
|
2354
|
-
archive_path:
|
|
2355
|
-
// Post-move canonical path (e.g. "
|
|
2356
|
-
restored_to:
|
|
2849
|
+
archive_path: z16.string().optional(),
|
|
2850
|
+
// Post-move canonical path (e.g. "knowledge/decisions/KT-DEC-0007--single-cjs-hook.md" inside the resolved store).
|
|
2851
|
+
restored_to: z16.string().optional()
|
|
2357
2852
|
});
|
|
2358
|
-
var knowledgeDeferredEventSchema =
|
|
2853
|
+
var knowledgeDeferredEventSchema = z16.object({
|
|
2359
2854
|
...eventLedgerEnvelopeSchema,
|
|
2360
|
-
event_type:
|
|
2361
|
-
stable_id:
|
|
2362
|
-
|
|
2363
|
-
|
|
2364
|
-
|
|
2365
|
-
|
|
2366
|
-
|
|
2855
|
+
event_type: z16.literal("knowledge_deferred"),
|
|
2856
|
+
stable_id: z16.string().optional(),
|
|
2857
|
+
pending_path: z16.string().optional(),
|
|
2858
|
+
timestamp: z16.string().datetime(),
|
|
2859
|
+
reason: z16.string().optional(),
|
|
2860
|
+
until: z16.string().datetime().optional()
|
|
2861
|
+
});
|
|
2862
|
+
var knowledgeRejectedEventSchema = z16.object({
|
|
2367
2863
|
...eventLedgerEnvelopeSchema,
|
|
2368
|
-
event_type:
|
|
2369
|
-
stable_id:
|
|
2370
|
-
timestamp:
|
|
2371
|
-
reason:
|
|
2864
|
+
event_type: z16.literal("knowledge_rejected"),
|
|
2865
|
+
stable_id: z16.string().optional(),
|
|
2866
|
+
timestamp: z16.string().datetime(),
|
|
2867
|
+
reason: z16.string()
|
|
2372
2868
|
});
|
|
2373
|
-
var knowledgeConsumedEventSchema =
|
|
2869
|
+
var knowledgeConsumedEventSchema = z16.object({
|
|
2374
2870
|
...eventLedgerEnvelopeSchema,
|
|
2375
|
-
event_type:
|
|
2376
|
-
stable_id:
|
|
2377
|
-
consumed_at:
|
|
2378
|
-
client_hash:
|
|
2871
|
+
event_type: z16.literal("knowledge_consumed"),
|
|
2872
|
+
stable_id: z16.string(),
|
|
2873
|
+
consumed_at: z16.string().datetime(),
|
|
2874
|
+
client_hash: z16.string()
|
|
2379
2875
|
});
|
|
2380
|
-
var knowledgeScopeDegradedEventSchema =
|
|
2876
|
+
var knowledgeScopeDegradedEventSchema = z16.object({
|
|
2381
2877
|
...eventLedgerEnvelopeSchema,
|
|
2382
|
-
event_type:
|
|
2383
|
-
stable_id:
|
|
2384
|
-
timestamp:
|
|
2385
|
-
from_scope:
|
|
2386
|
-
to_scope:
|
|
2387
|
-
reason:
|
|
2388
|
-
});
|
|
2389
|
-
var doctorRunEventSchema =
|
|
2878
|
+
event_type: z16.literal("knowledge_scope_degraded"),
|
|
2879
|
+
stable_id: z16.string(),
|
|
2880
|
+
timestamp: z16.string().datetime(),
|
|
2881
|
+
from_scope: z16.enum(["narrow", "broad"]),
|
|
2882
|
+
to_scope: z16.enum(["narrow", "broad"]),
|
|
2883
|
+
reason: z16.string()
|
|
2884
|
+
});
|
|
2885
|
+
var doctorRunEventSchema = z16.object({
|
|
2390
2886
|
...eventLedgerEnvelopeSchema,
|
|
2391
|
-
event_type:
|
|
2392
|
-
mode:
|
|
2393
|
-
issues:
|
|
2394
|
-
mutations:
|
|
2395
|
-
timestamp:
|
|
2887
|
+
event_type: z16.literal("doctor_run"),
|
|
2888
|
+
mode: z16.enum(["lint", "fix-knowledge"]),
|
|
2889
|
+
issues: z16.number().int().nonnegative(),
|
|
2890
|
+
mutations: z16.number().int().nonnegative().optional(),
|
|
2891
|
+
timestamp: z16.string().datetime()
|
|
2396
2892
|
});
|
|
2397
|
-
var knowledgePathDangledEventSchema =
|
|
2893
|
+
var knowledgePathDangledEventSchema = z16.object({
|
|
2398
2894
|
...eventLedgerEnvelopeSchema,
|
|
2399
|
-
event_type:
|
|
2400
|
-
stable_id:
|
|
2401
|
-
removed_glob:
|
|
2895
|
+
event_type: z16.literal("knowledge_path_dangled"),
|
|
2896
|
+
stable_id: z16.string(),
|
|
2897
|
+
removed_glob: z16.string()
|
|
2402
2898
|
});
|
|
2403
|
-
var relevanceMigrationRunEventSchema =
|
|
2899
|
+
var relevanceMigrationRunEventSchema = z16.object({
|
|
2404
2900
|
...eventLedgerEnvelopeSchema,
|
|
2405
|
-
event_type:
|
|
2406
|
-
timestamp:
|
|
2407
|
-
scanned_count:
|
|
2408
|
-
touched_count:
|
|
2901
|
+
event_type: z16.literal("relevance_migration_run"),
|
|
2902
|
+
timestamp: z16.string().datetime(),
|
|
2903
|
+
scanned_count: z16.number().int().nonnegative(),
|
|
2904
|
+
touched_count: z16.number().int().nonnegative()
|
|
2409
2905
|
});
|
|
2410
|
-
var pendingAutoArchivedEventSchema =
|
|
2906
|
+
var pendingAutoArchivedEventSchema = z16.object({
|
|
2411
2907
|
...eventLedgerEnvelopeSchema,
|
|
2412
|
-
event_type:
|
|
2413
|
-
pending_path:
|
|
2414
|
-
archived_to:
|
|
2415
|
-
reason:
|
|
2908
|
+
event_type: z16.literal("pending_auto_archived"),
|
|
2909
|
+
pending_path: z16.string(),
|
|
2910
|
+
archived_to: z16.string(),
|
|
2911
|
+
reason: z16.string()
|
|
2416
2912
|
});
|
|
2417
|
-
var assistantTurnObservedEventSchema =
|
|
2913
|
+
var assistantTurnObservedEventSchema = z16.object({
|
|
2418
2914
|
...eventLedgerEnvelopeSchema,
|
|
2419
|
-
event_type:
|
|
2420
|
-
kb_line_raw:
|
|
2421
|
-
cite_ids:
|
|
2422
|
-
cite_tags:
|
|
2915
|
+
event_type: z16.literal("assistant_turn_observed"),
|
|
2916
|
+
kb_line_raw: z16.string().nullable(),
|
|
2917
|
+
cite_ids: z16.array(z16.string()).default([]),
|
|
2918
|
+
cite_tags: z16.array(citeTagSchema).default([]),
|
|
2423
2919
|
// v2.0.0-rc.24 TASK-01: per-cite contract commitments. Index-aligned with
|
|
2424
2920
|
// cite_ids/cite_tags (commitments[i] belongs to cite_ids[i]). Each slot
|
|
2425
2921
|
// carries `operators[]` (kind + glob target) or `skip_reason` when the cite
|
|
@@ -2427,160 +2923,201 @@ var assistantTurnObservedEventSchema = z18.object({
|
|
|
2427
2923
|
// empty array via `.default([])` and are excluded from contract-policy
|
|
2428
2924
|
// audits by the marker-gate (see cite_contract_policy_activated below).
|
|
2429
2925
|
// Mirrors the rc.20 cite_tags parallel-array evolution exactly.
|
|
2430
|
-
cite_commitments:
|
|
2431
|
-
|
|
2432
|
-
operators:
|
|
2433
|
-
|
|
2434
|
-
kind:
|
|
2435
|
-
target:
|
|
2926
|
+
cite_commitments: z16.array(
|
|
2927
|
+
z16.object({
|
|
2928
|
+
operators: z16.array(
|
|
2929
|
+
z16.object({
|
|
2930
|
+
kind: z16.enum(["edit", "not_edit", "require", "forbid"]),
|
|
2931
|
+
target: z16.string()
|
|
2436
2932
|
})
|
|
2437
2933
|
),
|
|
2438
|
-
skip_reason:
|
|
2934
|
+
skip_reason: z16.string().nullable()
|
|
2439
2935
|
})
|
|
2440
2936
|
).default([]),
|
|
2441
|
-
|
|
2442
|
-
|
|
2443
|
-
|
|
2444
|
-
|
|
2937
|
+
// lifecycle-refactor W3-T4 (§2 store 轴 / store-qualified 观测): per-cite store
|
|
2938
|
+
// qualifier, index-aligned with cite_ids. Mirrors the cite-line-parser's
|
|
2939
|
+
// `cite_stores` output (`<alias-or-uuid>:<id>` → the qualifier; a bare id →
|
|
2940
|
+
// null). Persists the store provenance the parser already extracts so
|
|
2941
|
+
// doctor --cite-coverage can break compliance down per store WITHOUT joining
|
|
2942
|
+
// against the store registry. Additive `.optional()` (NOT `.default([])`) so
|
|
2943
|
+
// existing inline event constructors stay valid without supplying it — pre-W3-T4
|
|
2944
|
+
// events parse with the field absent and bucket under the unqualified default.
|
|
2945
|
+
cite_stores: z16.array(z16.string().nullable()).optional(),
|
|
2946
|
+
client: z16.enum(["cc", "codex"]).optional(),
|
|
2947
|
+
turn_id: z16.string(),
|
|
2948
|
+
envelope_index: z16.number().int().nonnegative().optional(),
|
|
2949
|
+
timestamp: z16.string().datetime()
|
|
2950
|
+
});
|
|
2951
|
+
var citePolicyActivatedEventSchema = z16.object({
|
|
2952
|
+
...eventLedgerEnvelopeSchema,
|
|
2953
|
+
event_type: z16.literal("cite_policy_activated"),
|
|
2954
|
+
policy_version: z16.string(),
|
|
2955
|
+
timestamp: z16.string().datetime()
|
|
2445
2956
|
});
|
|
2446
|
-
var
|
|
2957
|
+
var citeContractPolicyActivatedEventSchema = z16.object({
|
|
2447
2958
|
...eventLedgerEnvelopeSchema,
|
|
2448
|
-
event_type:
|
|
2449
|
-
policy_version: z18.string(),
|
|
2450
|
-
timestamp: z18.string().datetime()
|
|
2959
|
+
event_type: z16.literal("cite_contract_policy_activated")
|
|
2451
2960
|
});
|
|
2452
|
-
var
|
|
2961
|
+
var eventsRotatedEventSchema = z16.object({
|
|
2453
2962
|
...eventLedgerEnvelopeSchema,
|
|
2454
|
-
event_type:
|
|
2963
|
+
event_type: z16.literal("events_rotated"),
|
|
2964
|
+
cutoff_ts: z16.string().datetime(),
|
|
2965
|
+
archived_count: z16.number().int().nonnegative(),
|
|
2966
|
+
kept_count: z16.number().int().nonnegative(),
|
|
2967
|
+
archive_path: z16.string()
|
|
2455
2968
|
});
|
|
2456
|
-
var
|
|
2969
|
+
var knowledgeMetaAutoHealedEventSchema = z16.object({
|
|
2457
2970
|
...eventLedgerEnvelopeSchema,
|
|
2458
|
-
event_type:
|
|
2459
|
-
|
|
2460
|
-
|
|
2461
|
-
|
|
2462
|
-
|
|
2971
|
+
event_type: z16.literal("knowledge_meta_auto_healed"),
|
|
2972
|
+
previous_revision_hash: z16.string(),
|
|
2973
|
+
revision_hash: z16.string(),
|
|
2974
|
+
trigger: z16.literal("read"),
|
|
2975
|
+
caller: z16.enum(["planContext", "getKnowledgeSections", "getKnowledge", "extractKnowledge"]).optional()
|
|
2463
2976
|
});
|
|
2464
|
-
var
|
|
2977
|
+
var serveLockClearedEventSchema = z16.object({
|
|
2465
2978
|
...eventLedgerEnvelopeSchema,
|
|
2466
|
-
event_type:
|
|
2467
|
-
|
|
2468
|
-
|
|
2469
|
-
|
|
2470
|
-
caller: z18.enum(["planContext", "getKnowledgeSections", "getKnowledge", "extractKnowledge"]).optional()
|
|
2979
|
+
event_type: z16.literal("serve_lock_cleared"),
|
|
2980
|
+
pid: z16.number().int().nonnegative(),
|
|
2981
|
+
age_ms: z16.number().int().nonnegative(),
|
|
2982
|
+
timestamp: z16.string().datetime()
|
|
2471
2983
|
});
|
|
2472
|
-
var
|
|
2984
|
+
var knowledgeEnrichedEventSchema = z16.object({
|
|
2473
2985
|
...eventLedgerEnvelopeSchema,
|
|
2474
|
-
event_type:
|
|
2475
|
-
|
|
2476
|
-
|
|
2477
|
-
|
|
2986
|
+
event_type: z16.literal("knowledge_enriched"),
|
|
2987
|
+
path: z16.string(),
|
|
2988
|
+
added_fields: z16.array(z16.enum(["intent_clues", "tech_stack", "impact", "must_read_if"])),
|
|
2989
|
+
mode: z16.enum(["auto", "preview", "readonly", "interactive"]),
|
|
2990
|
+
timestamp: z16.string().datetime()
|
|
2478
2991
|
});
|
|
2479
|
-
var
|
|
2992
|
+
var sessionArchiveAttemptedEventSchema = z16.object({
|
|
2480
2993
|
...eventLedgerEnvelopeSchema,
|
|
2481
|
-
event_type:
|
|
2482
|
-
|
|
2483
|
-
|
|
2484
|
-
|
|
2485
|
-
|
|
2994
|
+
event_type: z16.literal("session_archive_attempted"),
|
|
2995
|
+
outcome: z16.enum(["proposed", "viability_failed", "user_dismissed", "skipped_no_signal"]),
|
|
2996
|
+
covered_through_ts: z16.number().int().nonnegative(),
|
|
2997
|
+
candidates_proposed: z16.number().int().nonnegative().default(0),
|
|
2998
|
+
knowledge_proposed_ids: z16.array(z16.string()).default([])
|
|
2486
2999
|
});
|
|
2487
|
-
var
|
|
3000
|
+
var hookSurfaceEmittedEventSchema = z16.object({
|
|
3001
|
+
...eventLedgerEnvelopeSchema,
|
|
3002
|
+
event_type: z16.literal("hook_surface_emitted"),
|
|
3003
|
+
hook_name: z16.string(),
|
|
3004
|
+
client: z16.enum(["cc", "codex"]),
|
|
3005
|
+
target_channel: z16.string(),
|
|
3006
|
+
rendered_ids: z16.array(z16.string()),
|
|
3007
|
+
delivery_status: z16.enum(["delivered", "suppressed", "error"]),
|
|
3008
|
+
suppression_reason: z16.string().optional()
|
|
3009
|
+
});
|
|
3010
|
+
var hookSignalEmittedEventSchema = z16.object({
|
|
2488
3011
|
...eventLedgerEnvelopeSchema,
|
|
2489
|
-
event_type:
|
|
2490
|
-
|
|
2491
|
-
|
|
2492
|
-
|
|
2493
|
-
|
|
3012
|
+
event_type: z16.literal("hook_signal_emitted"),
|
|
3013
|
+
signal_type: z16.enum(["archive", "review", "maintenance", "other"]),
|
|
3014
|
+
threshold: z16.number(),
|
|
3015
|
+
actual_value: z16.number(),
|
|
3016
|
+
fired: z16.boolean()
|
|
2494
3017
|
});
|
|
2495
|
-
var
|
|
3018
|
+
var mcpStdioTraceEventSchema = z16.object({
|
|
2496
3019
|
...eventLedgerEnvelopeSchema,
|
|
2497
|
-
event_type:
|
|
2498
|
-
|
|
2499
|
-
|
|
2500
|
-
|
|
2501
|
-
|
|
2502
|
-
|
|
2503
|
-
|
|
2504
|
-
|
|
2505
|
-
|
|
3020
|
+
event_type: z16.literal("mcp_stdio_trace"),
|
|
3021
|
+
tool_name: z16.string(),
|
|
3022
|
+
request_id: z16.string(),
|
|
3023
|
+
duration_ms: z16.number().nonnegative(),
|
|
3024
|
+
status: z16.enum(["ok", "error"]),
|
|
3025
|
+
payload_bytes_in: z16.number().int().nonnegative(),
|
|
3026
|
+
payload_bytes_out: z16.number().int().nonnegative(),
|
|
3027
|
+
error_code: z16.string().optional()
|
|
3028
|
+
});
|
|
3029
|
+
var payloadGuardObservedEventSchema = z16.object({
|
|
2506
3030
|
...eventLedgerEnvelopeSchema,
|
|
2507
|
-
event_type:
|
|
2508
|
-
|
|
2509
|
-
|
|
2510
|
-
|
|
2511
|
-
|
|
3031
|
+
event_type: z16.literal("payload_guard_observed"),
|
|
3032
|
+
tool_name: z16.string(),
|
|
3033
|
+
path_count: z16.number().int().nonnegative(),
|
|
3034
|
+
tokens_estimated: z16.number().int().nonnegative(),
|
|
3035
|
+
truncated: z16.boolean(),
|
|
3036
|
+
cap: z16.number().int().positive()
|
|
3037
|
+
});
|
|
3038
|
+
var skillInvocationStartedEventSchema = z16.object({
|
|
3039
|
+
...eventLedgerEnvelopeSchema,
|
|
3040
|
+
event_type: z16.literal("skill_invocation_started"),
|
|
3041
|
+
skill_name: z16.string(),
|
|
3042
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3043
|
+
entry_point: z16.string()
|
|
2512
3044
|
});
|
|
2513
|
-
var
|
|
3045
|
+
var skillInvocationCompletedEventSchema = z16.object({
|
|
2514
3046
|
...eventLedgerEnvelopeSchema,
|
|
2515
|
-
event_type:
|
|
2516
|
-
|
|
2517
|
-
|
|
2518
|
-
|
|
2519
|
-
|
|
2520
|
-
|
|
2521
|
-
|
|
2522
|
-
|
|
2523
|
-
});
|
|
2524
|
-
var payloadGuardObservedEventSchema = z18.object({
|
|
3047
|
+
event_type: z16.literal("skill_invocation_completed"),
|
|
3048
|
+
skill_name: z16.string(),
|
|
3049
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3050
|
+
entry_point: z16.string(),
|
|
3051
|
+
outcome: z16.enum(["completed", "aborted", "error", "no_op"]),
|
|
3052
|
+
elapsed_ms: z16.number().nonnegative().optional()
|
|
3053
|
+
});
|
|
3054
|
+
var skillPhaseTransitionEventSchema = z16.object({
|
|
2525
3055
|
...eventLedgerEnvelopeSchema,
|
|
2526
|
-
event_type:
|
|
2527
|
-
|
|
2528
|
-
|
|
2529
|
-
|
|
2530
|
-
|
|
2531
|
-
|
|
2532
|
-
});
|
|
2533
|
-
var
|
|
3056
|
+
event_type: z16.literal("skill_phase_transition"),
|
|
3057
|
+
skill_name: z16.string(),
|
|
3058
|
+
phase: z16.string(),
|
|
3059
|
+
status: z16.enum(["entered", "completed", "skipped", "failed"]),
|
|
3060
|
+
checkpoint: z16.string().optional(),
|
|
3061
|
+
elapsed_ms: z16.number().nonnegative().optional()
|
|
3062
|
+
});
|
|
3063
|
+
var skillTriggerCandidateEventSchema = z16.object({
|
|
2534
3064
|
...eventLedgerEnvelopeSchema,
|
|
2535
|
-
event_type:
|
|
2536
|
-
skill_name:
|
|
2537
|
-
trigger_source:
|
|
2538
|
-
|
|
3065
|
+
event_type: z16.literal("skill_trigger_candidate"),
|
|
3066
|
+
skill_name: z16.string(),
|
|
3067
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3068
|
+
signal: z16.string(),
|
|
3069
|
+
invoked: z16.boolean()
|
|
2539
3070
|
});
|
|
2540
|
-
var
|
|
3071
|
+
var llmJudgeRunEventSchema = z16.object({
|
|
2541
3072
|
...eventLedgerEnvelopeSchema,
|
|
2542
|
-
event_type:
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
3073
|
+
event_type: z16.literal("llm_judge_run"),
|
|
3074
|
+
prompt: z16.string(),
|
|
3075
|
+
version: z16.string(),
|
|
3076
|
+
model: z16.string(),
|
|
3077
|
+
input_trace_id: z16.string(),
|
|
3078
|
+
score: z16.number(),
|
|
3079
|
+
rationale: z16.string()
|
|
3080
|
+
});
|
|
3081
|
+
var clientCapabilitySnapshotEventSchema = z16.object({
|
|
2550
3082
|
...eventLedgerEnvelopeSchema,
|
|
2551
|
-
event_type:
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
});
|
|
2558
|
-
var skillTriggerCandidateEventSchema = z18.object({
|
|
3083
|
+
event_type: z16.literal("client_capability_snapshot"),
|
|
3084
|
+
client: z16.enum(["cc", "codex"]),
|
|
3085
|
+
capabilities: z16.array(z16.string()),
|
|
3086
|
+
version: z16.string()
|
|
3087
|
+
});
|
|
3088
|
+
var sessionEndedEventSchema = z16.object({
|
|
2559
3089
|
...eventLedgerEnvelopeSchema,
|
|
2560
|
-
event_type:
|
|
2561
|
-
skill_name: z18.string(),
|
|
2562
|
-
trigger_source: z18.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
2563
|
-
signal: z18.string(),
|
|
2564
|
-
invoked: z18.boolean()
|
|
3090
|
+
event_type: z16.literal("session_ended")
|
|
2565
3091
|
});
|
|
2566
|
-
var
|
|
3092
|
+
var fileMutatedEventSchema = z16.object({
|
|
3093
|
+
...eventLedgerEnvelopeSchema,
|
|
3094
|
+
event_type: z16.literal("file_mutated"),
|
|
3095
|
+
path: z16.string(),
|
|
3096
|
+
tool_call_id: z16.string(),
|
|
3097
|
+
tool_name: z16.string().optional(),
|
|
3098
|
+
source_event_id: z16.string().optional(),
|
|
3099
|
+
store_id: z16.string().optional()
|
|
3100
|
+
});
|
|
3101
|
+
var knowledgeBodyReadEventSchema = z16.object({
|
|
2567
3102
|
...eventLedgerEnvelopeSchema,
|
|
2568
|
-
event_type:
|
|
2569
|
-
|
|
2570
|
-
|
|
2571
|
-
|
|
2572
|
-
|
|
2573
|
-
|
|
2574
|
-
|
|
2575
|
-
|
|
2576
|
-
var clientCapabilitySnapshotEventSchema = z18.object({
|
|
3103
|
+
event_type: z16.literal("knowledge_body_read"),
|
|
3104
|
+
stable_id: z16.string(),
|
|
3105
|
+
store: z16.string().optional(),
|
|
3106
|
+
path: z16.string(),
|
|
3107
|
+
tool_call_id: z16.string().optional(),
|
|
3108
|
+
tool_name: z16.string().optional()
|
|
3109
|
+
});
|
|
3110
|
+
var precompactObservedEventSchema = z16.object({
|
|
2577
3111
|
...eventLedgerEnvelopeSchema,
|
|
2578
|
-
event_type:
|
|
2579
|
-
client: z18.enum(["cc", "codex", "cursor"]),
|
|
2580
|
-
capabilities: z18.array(z18.string()),
|
|
2581
|
-
version: z18.string()
|
|
3112
|
+
event_type: z16.literal("precompact_observed")
|
|
2582
3113
|
});
|
|
2583
|
-
var
|
|
3114
|
+
var graphEdgeCandidateRequestedEventSchema = z16.object({
|
|
3115
|
+
...eventLedgerEnvelopeSchema,
|
|
3116
|
+
event_type: z16.literal("graph_edge_candidate_requested"),
|
|
3117
|
+
stable_id: z16.string(),
|
|
3118
|
+
store: z16.string().optional()
|
|
3119
|
+
});
|
|
3120
|
+
var eventLedgerEventSchema = z16.discriminatedUnion("event_type", [
|
|
2584
3121
|
knowledgeContextPlannedEventSchema,
|
|
2585
3122
|
knowledgeSelectionEventSchema,
|
|
2586
3123
|
knowledgeSectionsFetchedEventSchema,
|
|
@@ -2590,10 +3127,6 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2590
3127
|
reapplyCompletedEventSchema,
|
|
2591
3128
|
installDiffAppliedEventSchema,
|
|
2592
3129
|
eventLedgerTruncatedEventSchema,
|
|
2593
|
-
mcpConfigMigratedEventSchema,
|
|
2594
|
-
// v2.0.0-rc.19 TASK-004: bootstrap_marker_migrated — one-time fabric:knowledge-base
|
|
2595
|
-
// → fabric:bootstrap marker rewrite emitted per file by `fabric doctor --fix`.
|
|
2596
|
-
bootstrapMarkerMigratedEventSchema,
|
|
2597
3130
|
metaReconciledOnStartupEventSchema,
|
|
2598
3131
|
metaReconciledEventSchema,
|
|
2599
3132
|
claudeSkillPathMigratedEventSchema,
|
|
@@ -2605,6 +3138,7 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2605
3138
|
knowledgePromoteStartedEventSchema,
|
|
2606
3139
|
knowledgePromotedEventSchema,
|
|
2607
3140
|
knowledgePromoteFailedEventSchema,
|
|
3141
|
+
knowledgeModifiedEventSchema,
|
|
2608
3142
|
knowledgeLayerChangedEventSchema,
|
|
2609
3143
|
// v2.0.0-rc.37 NEW-24: dedicated old→new stable_id mapping event
|
|
2610
3144
|
knowledgeIdRedirectEventSchema,
|
|
@@ -2672,7 +3206,15 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2672
3206
|
skillPhaseTransitionEventSchema,
|
|
2673
3207
|
skillTriggerCandidateEventSchema,
|
|
2674
3208
|
llmJudgeRunEventSchema,
|
|
2675
|
-
clientCapabilitySnapshotEventSchema
|
|
3209
|
+
clientCapabilitySnapshotEventSchema,
|
|
3210
|
+
// lifecycle-refactor Wave 2 — dormant-hook activation markers.
|
|
3211
|
+
sessionEndedEventSchema,
|
|
3212
|
+
fileMutatedEventSchema,
|
|
3213
|
+
// KT-DEC-0030: knowledge_body_read — PostToolUse native-Read consumption marker
|
|
3214
|
+
// (replaces knowledge_consumed as the funnel's "body opened" signal).
|
|
3215
|
+
knowledgeBodyReadEventSchema,
|
|
3216
|
+
precompactObservedEventSchema,
|
|
3217
|
+
graphEdgeCandidateRequestedEventSchema
|
|
2676
3218
|
]);
|
|
2677
3219
|
|
|
2678
3220
|
// src/text-tokenize.ts
|
|
@@ -2703,51 +3245,16 @@ function tokenize(text) {
|
|
|
2703
3245
|
}
|
|
2704
3246
|
return tokens;
|
|
2705
3247
|
}
|
|
2706
|
-
|
|
2707
|
-
// src/retrieval-budget.ts
|
|
2708
|
-
var PROFILES = {
|
|
2709
|
-
conservative: {
|
|
2710
|
-
topK: 12,
|
|
2711
|
-
payloadWarnBytes: 8192,
|
|
2712
|
-
payloadHardBytes: 32768,
|
|
2713
|
-
injectionChars: 1e3
|
|
2714
|
-
},
|
|
2715
|
-
balanced: {
|
|
2716
|
-
topK: 24,
|
|
2717
|
-
payloadWarnBytes: 16384,
|
|
2718
|
-
payloadHardBytes: 65536,
|
|
2719
|
-
injectionChars: 2e3
|
|
2720
|
-
},
|
|
2721
|
-
generous: {
|
|
2722
|
-
topK: 48,
|
|
2723
|
-
payloadWarnBytes: 32768,
|
|
2724
|
-
payloadHardBytes: 131072,
|
|
2725
|
-
injectionChars: 4e3
|
|
2726
|
-
}
|
|
2727
|
-
};
|
|
2728
|
-
var DEFAULT_RETRIEVAL_BUDGET_PROFILE = "balanced";
|
|
2729
|
-
function resolveRetrievalBudget(overrides) {
|
|
2730
|
-
const base = PROFILES[overrides?.profile ?? DEFAULT_RETRIEVAL_BUDGET_PROFILE];
|
|
2731
|
-
return {
|
|
2732
|
-
topK: overrides?.topK ?? base.topK,
|
|
2733
|
-
payloadWarnBytes: overrides?.payloadWarnBytes ?? base.payloadWarnBytes,
|
|
2734
|
-
payloadHardBytes: overrides?.payloadHardBytes ?? base.payloadHardBytes,
|
|
2735
|
-
injectionChars: overrides?.injectionChars ?? base.injectionChars
|
|
2736
|
-
};
|
|
2737
|
-
}
|
|
2738
|
-
function retrievalBudgetProfile(profile) {
|
|
2739
|
-
return PROFILES[profile];
|
|
2740
|
-
}
|
|
2741
3248
|
export {
|
|
2742
3249
|
AGENTS_META_IDENTITY_SOURCES,
|
|
2743
|
-
AGENTS_META_LAYERS,
|
|
2744
3250
|
AGENTS_META_TOPOLOGY_TYPES,
|
|
2745
3251
|
AgentsMetaCountersSchema,
|
|
2746
|
-
|
|
3252
|
+
BOOTSTRAP_CANONICAL_BY_LOCALE,
|
|
3253
|
+
BOOTSTRAP_CANONICAL_EN,
|
|
3254
|
+
BOOTSTRAP_CANONICAL_ZH,
|
|
2747
3255
|
BOOTSTRAP_MARKER_BEGIN,
|
|
2748
3256
|
BOOTSTRAP_MARKER_END,
|
|
2749
3257
|
BOOTSTRAP_REGEX,
|
|
2750
|
-
DEFAULT_RETRIEVAL_BUDGET_PROFILE,
|
|
2751
3258
|
FabExtractKnowledgeInputSchema,
|
|
2752
3259
|
FabExtractKnowledgeInputShape,
|
|
2753
3260
|
FabExtractKnowledgeOutputSchema,
|
|
@@ -2763,9 +3270,6 @@ export {
|
|
|
2763
3270
|
KNOWN_SCOPE_PREFIXES,
|
|
2764
3271
|
KnowledgeEntryFrontmatterSchema,
|
|
2765
3272
|
KnowledgeTypeSchema,
|
|
2766
|
-
LEGACY_KB_MARKER_BEGIN,
|
|
2767
|
-
LEGACY_KB_MARKER_END,
|
|
2768
|
-
LEGACY_KB_REGEX,
|
|
2769
3273
|
LayerSchema,
|
|
2770
3274
|
MCP_STORE_AWARE_CONTRACTS,
|
|
2771
3275
|
MCP_STORE_AWARE_TOOLS,
|
|
@@ -2777,30 +3281,34 @@ export {
|
|
|
2777
3281
|
PERSONAL_SCOPE,
|
|
2778
3282
|
PERSONAL_STORE_SENTINEL,
|
|
2779
3283
|
PROJECT_ROOT_SIGNALS,
|
|
2780
|
-
|
|
3284
|
+
PROPOSED_REASON_DESCRIPTIONS_BY_LOCALE,
|
|
2781
3285
|
PROTECTED_TOKENS,
|
|
2782
3286
|
ProposedReasonSchema,
|
|
2783
3287
|
REDACTION_PLACEHOLDER_PREFIX,
|
|
2784
|
-
ResolverNotImplementedError,
|
|
2785
3288
|
SCOPE_COORDINATE_PATTERN,
|
|
2786
3289
|
STORES_ROOT_DIR,
|
|
3290
|
+
STORE_ALIAS_PATTERN,
|
|
2787
3291
|
STORE_GITIGNORE,
|
|
2788
3292
|
STORE_KNOWLEDGE_TYPE_DIRS,
|
|
2789
3293
|
STORE_LAYOUT,
|
|
3294
|
+
STORE_MOUNT_GROUPS,
|
|
3295
|
+
STORE_MOUNT_NAME_PATTERN,
|
|
2790
3296
|
STORE_PENDING_DIR,
|
|
3297
|
+
STORE_PROJECT_ID_PATTERN,
|
|
2791
3298
|
STORE_RESOLVER_WARNING_CODES,
|
|
2792
3299
|
STORE_UUID_PATTERN,
|
|
2793
3300
|
StableIdSchema,
|
|
2794
3301
|
UID_SEGMENT_PATTERN,
|
|
2795
3302
|
addMountedStore,
|
|
3303
|
+
addStoreProject,
|
|
2796
3304
|
agentsIdentitySourceSchema,
|
|
2797
|
-
agentsLayerSchema,
|
|
2798
3305
|
agentsMetaNodeSchema,
|
|
2799
3306
|
agentsMetaSchema,
|
|
2800
3307
|
agentsTopologyTypeSchema,
|
|
2801
3308
|
aggregatePendingAcrossStores,
|
|
2802
3309
|
aiLedgerEntrySchema,
|
|
2803
3310
|
allocateKnowledgeId,
|
|
3311
|
+
allocateStoreKnowledgeId,
|
|
2804
3312
|
annotateIntentRequestSchema,
|
|
2805
3313
|
archiveScanAnnotations,
|
|
2806
3314
|
archiveScanInputSchema,
|
|
@@ -2809,10 +3317,10 @@ export {
|
|
|
2809
3317
|
auditModeSchema,
|
|
2810
3318
|
bindRequiredStore,
|
|
2811
3319
|
bindingsSnapshotPath,
|
|
2812
|
-
bootstrapMarkerMigratedEventSchema,
|
|
2813
3320
|
buildDebugBundle,
|
|
2814
3321
|
buildFailureTrace,
|
|
2815
3322
|
buildScanRecommendations,
|
|
3323
|
+
buildStoreResolveInput,
|
|
2816
3324
|
candidateFileEntrySchema,
|
|
2817
3325
|
citeContractMetricsSchema,
|
|
2818
3326
|
citeContractPolicyActivatedEventSchema,
|
|
@@ -2831,11 +3339,12 @@ export {
|
|
|
2831
3339
|
defaultLayerFilterSchema,
|
|
2832
3340
|
defaultMessages,
|
|
2833
3341
|
deriveAgentsMetaIdentitySource,
|
|
2834
|
-
deriveAgentsMetaLayer,
|
|
2835
3342
|
deriveAgentsMetaStableId,
|
|
2836
3343
|
deriveAgentsMetaTopologyType,
|
|
3344
|
+
deriveMountLabel,
|
|
2837
3345
|
detachMountedStore,
|
|
2838
3346
|
detectNodeLocale,
|
|
3347
|
+
disambiguateAlias,
|
|
2839
3348
|
doctorRunEventSchema,
|
|
2840
3349
|
driftDetectedEventSchema,
|
|
2841
3350
|
editIntentCheckedEventSchema,
|
|
@@ -2850,6 +3359,7 @@ export {
|
|
|
2850
3359
|
fabricConfigSchema,
|
|
2851
3360
|
fabricEventSchema,
|
|
2852
3361
|
fabricLanguageSchema,
|
|
3362
|
+
fileMutatedEventSchema,
|
|
2853
3363
|
findMountedStore,
|
|
2854
3364
|
findStoreExecutableViolations,
|
|
2855
3365
|
forensicAssertionCoverageSchema,
|
|
@@ -2866,8 +3376,10 @@ export {
|
|
|
2866
3376
|
formatKnowledgeId,
|
|
2867
3377
|
getPanelFieldByKey,
|
|
2868
3378
|
getPanelFields,
|
|
3379
|
+
globalConfigPath,
|
|
2869
3380
|
globalConfigSchema,
|
|
2870
3381
|
globalRefSchema,
|
|
3382
|
+
graphEdgeCandidateRequestedEventSchema,
|
|
2871
3383
|
hasSecrets,
|
|
2872
3384
|
historyStateQuerySchema,
|
|
2873
3385
|
hookSignalEmittedEventSchema,
|
|
@@ -2892,6 +3404,7 @@ export {
|
|
|
2892
3404
|
isPersonalScope,
|
|
2893
3405
|
knowledgeArchiveAttemptedEventSchema,
|
|
2894
3406
|
knowledgeArchivedEventSchema,
|
|
3407
|
+
knowledgeBodyReadEventSchema,
|
|
2895
3408
|
knowledgeConsumedEventSchema,
|
|
2896
3409
|
knowledgeContextPlannedEventSchema,
|
|
2897
3410
|
knowledgeDeferredEventSchema,
|
|
@@ -2901,6 +3414,7 @@ export {
|
|
|
2901
3414
|
knowledgeIdRedirectEventSchema,
|
|
2902
3415
|
knowledgeLayerChangedEventSchema,
|
|
2903
3416
|
knowledgeMetaAutoHealedEventSchema,
|
|
3417
|
+
knowledgeModifiedEventSchema,
|
|
2904
3418
|
knowledgePathDangledEventSchema,
|
|
2905
3419
|
knowledgePromoteFailedEventSchema,
|
|
2906
3420
|
knowledgePromoteStartedEventSchema,
|
|
@@ -2926,10 +3440,12 @@ export {
|
|
|
2926
3440
|
lintCrossStoreReferences,
|
|
2927
3441
|
listStoreKnowledge,
|
|
2928
3442
|
llmJudgeRunEventSchema,
|
|
3443
|
+
loadGlobalConfig,
|
|
3444
|
+
loadProjectConfig,
|
|
2929
3445
|
localKnowledgeIdSchema,
|
|
2930
3446
|
lockApprovedEventSchema,
|
|
2931
3447
|
lockDriftEventSchema,
|
|
2932
|
-
|
|
3448
|
+
matchBootstrapCanonicalLocale,
|
|
2933
3449
|
mcpEventLedgerEventSchema,
|
|
2934
3450
|
mcpPayloadLimitsSchema,
|
|
2935
3451
|
mcpStdioTraceEventSchema,
|
|
@@ -2939,6 +3455,8 @@ export {
|
|
|
2939
3455
|
mountedStoreSchema,
|
|
2940
3456
|
normalizeCiteTag,
|
|
2941
3457
|
normalizeLocale,
|
|
3458
|
+
nudgeModeSchema,
|
|
3459
|
+
observeConfigSchema,
|
|
2942
3460
|
onboardSlotSchema,
|
|
2943
3461
|
parityCapabilitySchema,
|
|
2944
3462
|
parityClientExpectationSchema,
|
|
@@ -2956,6 +3474,8 @@ export {
|
|
|
2956
3474
|
planContextInputSchema,
|
|
2957
3475
|
planContextOutputSchema,
|
|
2958
3476
|
planContextTopKSchema,
|
|
3477
|
+
precompactObservedEventSchema,
|
|
3478
|
+
projectConfigPath,
|
|
2959
3479
|
projectRootGoldenCaseSchema,
|
|
2960
3480
|
projectRootGoldenFileSchema,
|
|
2961
3481
|
projectRootResolutionSchema,
|
|
@@ -2966,23 +3486,32 @@ export {
|
|
|
2966
3486
|
readSetEntrySchema,
|
|
2967
3487
|
readSetGoldenCaseSchema,
|
|
2968
3488
|
readSetGoldenFileSchema,
|
|
3489
|
+
readStoreCounters,
|
|
2969
3490
|
readStoreIdentity,
|
|
3491
|
+
readStoreIdentityAsync,
|
|
3492
|
+
readStoreProjects,
|
|
2970
3493
|
reapplyCompletedEventSchema,
|
|
2971
3494
|
recallAnnotations,
|
|
2972
3495
|
recallInputSchema,
|
|
2973
3496
|
recallOutputSchema,
|
|
2974
3497
|
recognizeStoreDir,
|
|
3498
|
+
reconcileStoreCounters,
|
|
3499
|
+
redactPii,
|
|
2975
3500
|
redactSecrets,
|
|
2976
3501
|
relevanceMigrationRunEventSchema,
|
|
2977
3502
|
requiredStoreEntrySchema,
|
|
3503
|
+
resolveBootstrapCanonical,
|
|
2978
3504
|
resolveCandidates,
|
|
2979
3505
|
resolveFabricLocale,
|
|
2980
|
-
|
|
3506
|
+
resolveGlobalLocale,
|
|
3507
|
+
resolveGlobalRoot,
|
|
2981
3508
|
resolveStoreQualifiedId,
|
|
3509
|
+
resolveWorkspaceBindingId,
|
|
2982
3510
|
resolvedBindingsSnapshotSchema,
|
|
2983
|
-
retrievalBudgetProfile,
|
|
2984
3511
|
ruleDescriptionIndexItemSchema,
|
|
2985
3512
|
ruleDescriptionSchema,
|
|
3513
|
+
saveGlobalConfig,
|
|
3514
|
+
saveProjectConfig,
|
|
2986
3515
|
scanForSecrets,
|
|
2987
3516
|
scopeCoordinateSchema,
|
|
2988
3517
|
scopeRoot,
|
|
@@ -2990,16 +3519,26 @@ export {
|
|
|
2990
3519
|
selectionTokenTtlMsSchema,
|
|
2991
3520
|
serveLockClearedEventSchema,
|
|
2992
3521
|
sessionArchiveAttemptedEventSchema,
|
|
3522
|
+
sessionEndedEventSchema,
|
|
2993
3523
|
skillInvocationCompletedEventSchema,
|
|
2994
3524
|
skillInvocationStartedEventSchema,
|
|
2995
3525
|
skillPhaseTransitionEventSchema,
|
|
2996
3526
|
skillTriggerCandidateEventSchema,
|
|
3527
|
+
storeAliasSchema,
|
|
2997
3528
|
storeAwareEntrySchema,
|
|
3529
|
+
storeCountersPath,
|
|
2998
3530
|
storeCountersSchema,
|
|
3531
|
+
storeHasProject,
|
|
2999
3532
|
storeIdentitySchema,
|
|
3000
3533
|
storeKnowledgeTypeDir,
|
|
3534
|
+
storeMountGroup,
|
|
3535
|
+
storeMountNameSchema,
|
|
3536
|
+
storeMountSubPath,
|
|
3537
|
+
storeProjectSchema,
|
|
3538
|
+
storeProjectsFileSchema,
|
|
3001
3539
|
storeReadSetSchema,
|
|
3002
3540
|
storeRelativePath,
|
|
3541
|
+
storeRelativePathForMount,
|
|
3003
3542
|
storeResolveInputSchema,
|
|
3004
3543
|
storeResolverWarningCodeSchema,
|
|
3005
3544
|
storeResolverWarningSchema,
|
|
@@ -3009,6 +3548,7 @@ export {
|
|
|
3009
3548
|
uidSchema,
|
|
3010
3549
|
withDerivedAgentsMetaNodeDefaults,
|
|
3011
3550
|
writeBindingsSnapshot,
|
|
3551
|
+
writeRouteSchema,
|
|
3012
3552
|
writeTargetSchema,
|
|
3013
3553
|
writtenToStoreSchema,
|
|
3014
3554
|
zhCNMessages
|