@fenglimg/fabric-shared 2.2.0-rc.3 → 2.2.0-rc.8
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-355LUDLW.js → chunk-5AKCRBKJ.js} +474 -418
- package/dist/{chunk-QSD4PN4W.js → chunk-B2N3I7UX.js} +410 -532
- package/dist/chunk-BDJQIOQO.js +206 -0
- package/dist/chunk-C7WZPYZE.js +129 -0
- package/dist/{chunk-VW5QGPIN.js → chunk-O6GIHZF3.js} +10 -0
- package/dist/errors/index.d.ts +9 -1
- package/dist/errors/index.js +7 -3
- package/dist/i18n/index.d.ts +29 -23
- package/dist/i18n/index.js +7 -3
- package/dist/{index-Dm4IJWwB.d.ts → index-SrmixArL.d.ts} +87 -28
- package/dist/index.d.ts +571 -551
- package/dist/index.js +1368 -965
- package/dist/node/atomic-write.js +6 -121
- 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 +163 -113
- 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-AFT7DB4P.js +0 -103
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-B2N3I7UX.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,7 @@ 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),
|
|
604
574
|
// v2.1 ⑤ cite-redesign (P5): recall-based cite-accounting hook config. The
|
|
605
575
|
// rc.34 cite_evict_interval turn-counter above is superseded by the
|
|
606
576
|
// PreToolUse(Edit/Write) recall-aware nudge in cite-policy-evict.cjs; the old
|
|
@@ -610,21 +580,21 @@ var fabricConfigSchema = z6.object({
|
|
|
610
580
|
// the cite_evict_interval=0 opt-out convention). `cite_recall_window_minutes`
|
|
611
581
|
// bounds how far back an in-session fab_recall counts as "informing" the edit
|
|
612
582
|
// (default 30; 0 = unbounded).
|
|
613
|
-
cite_recall_nudge:
|
|
614
|
-
cite_recall_window_minutes:
|
|
583
|
+
cite_recall_nudge: z5.boolean().optional().default(true),
|
|
584
|
+
cite_recall_window_minutes: z5.number().int().min(0).optional().default(30),
|
|
615
585
|
// F2: glob exemptions for the cite nudge (cite-policy-evict.cjs). Edit paths
|
|
616
586
|
// matching any glob skip the "改前先 fab_recall" nudge — meta/orchestration
|
|
617
587
|
// files (e.g. `.workflow/` scratchpads) are not source the cite policy
|
|
618
588
|
// governs. MERGED with the hook's built-in [".workflow/**"] default; an
|
|
619
589
|
// omitted/empty value keeps just that default. `*` = within a path segment,
|
|
620
590
|
// `**` = across segments.
|
|
621
|
-
cite_nudge_ignore_globs:
|
|
591
|
+
cite_nudge_ignore_globs: z5.array(z5.string()).optional(),
|
|
622
592
|
// v2.1 ④ conflict-detection (P4): bm25 content-similarity threshold (0..1)
|
|
623
593
|
// for the knowledge-conflict lint (`fabric doctor --lint-conflicts`). A
|
|
624
594
|
// same-(type,layer) pair whose normalized bm25 similarity reaches this floor
|
|
625
595
|
// is surfaced as a candidate (possible duplicate OR conflict). Conservative
|
|
626
596
|
// default 0.5 — raise to reduce noise, lower to catch looser pairs.
|
|
627
|
-
conflict_lint_similarity_threshold:
|
|
597
|
+
conflict_lint_similarity_threshold: z5.number().min(0).max(1).optional().default(0.5),
|
|
628
598
|
// v2.0.0-rc.22 Scope A T3: sliding-window retention (in days) for the
|
|
629
599
|
// event ledger rotation primitive (`rotateEventLedgerIfNeeded`). Lines
|
|
630
600
|
// whose `ts` is older than `now - fabric_event_retention_days * 86_400_000`
|
|
@@ -637,7 +607,7 @@ var fabricConfigSchema = z6.object({
|
|
|
637
607
|
// Mirrors cite-policy precedent of locking enum-style numeric tunables
|
|
638
608
|
// to a small literal set (vs free `.positive()`) to prevent fat-finger
|
|
639
609
|
// misconfig.
|
|
640
|
-
fabric_event_retention_days:
|
|
610
|
+
fabric_event_retention_days: z5.union([z5.literal(7), z5.literal(30), z5.literal(90)]).optional(),
|
|
641
611
|
// v2.0.0-rc.23 TASK-014 (F8c): onboard slot opt-out list. Tracks slot
|
|
642
612
|
// names the user explicitly dismissed during fabric-archive's first-run
|
|
643
613
|
// onboard phase (or via `fabric config dismiss-slot <slot>`). Dismissed
|
|
@@ -654,7 +624,7 @@ var fabricConfigSchema = z6.object({
|
|
|
654
624
|
//
|
|
655
625
|
// Default `[]` keeps the field optional on existing configs — fresh
|
|
656
626
|
// installs land with no opt-outs.
|
|
657
|
-
onboard_slots_opted_out:
|
|
627
|
+
onboard_slots_opted_out: z5.array(z5.string()).optional().default([]),
|
|
658
628
|
// v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the broad SessionStart hint
|
|
659
629
|
// banner emitted by knowledge-hint-broad.cjs. After plan-context-hint returns
|
|
660
630
|
// its full broad-scoped index, the hook slices the entries to this many
|
|
@@ -666,20 +636,29 @@ var fabricConfigSchema = z6.object({
|
|
|
666
636
|
// Range 1..50; values above 20 effectively disable the cap because the
|
|
667
637
|
// TRUNCATION_THRESHOLD=12 grouped-render kicks in. Mirrors the rc.7 T7 +
|
|
668
638
|
// archive_max_* pattern of externalizing previously-hardcoded thresholds.
|
|
669
|
-
hint_broad_top_k:
|
|
639
|
+
hint_broad_top_k: z5.number().int().min(1).max(50).optional().default(8),
|
|
670
640
|
// v2.2 HK2-degrade (W2-T2): char budget for the rendered SessionStart broad-menu
|
|
671
641
|
// body — the final rung of the degradation ladder after the hint_broad_top_k
|
|
672
642
|
// count slice. Once the rendered entry/group lines exceed this, the tail
|
|
673
643
|
// collapses to a single "N more omitted" marker so a large corpus cannot blow
|
|
674
644
|
// the agent's working memory. Default 2000 (~one screenful); 0 disables the
|
|
675
645
|
// budget. Read by knowledge-hint-broad.cjs via readConfigNumber. Range 0..20000.
|
|
676
|
-
hint_broad_budget_chars:
|
|
646
|
+
hint_broad_budget_chars: z5.number().int().min(0).max(2e4).optional().default(2e3),
|
|
647
|
+
// W4-1 (KT-DEC-0028 / KT-MOD-0001): scale backstop for the FULL broad index.
|
|
648
|
+
// After W2-1 retired the hint_broad_top_k hard cap, the broad banner shows
|
|
649
|
+
// every broad entry (completeness); this is the only guard — once a store's
|
|
650
|
+
// rendered broad index exceeds this many lines the overflow tail folds into a
|
|
651
|
+
// single drift marker. The doctor `broad-index-drift` lint (W4-2) warns at 80%
|
|
652
|
+
// of this value per store so the corpus can be pruned (fabric-audit) BEFORE the
|
|
653
|
+
// banner silently truncates. Default 50; range 20..500 (read inline by
|
|
654
|
+
// knowledge-hint-broad.cjs#readBroadIndexBackstop with the same bounds).
|
|
655
|
+
broad_index_backstop: z5.number().int().min(20).max(500).optional().default(50),
|
|
677
656
|
// v2.0.0-rc.37 NEW-16: durable per-signal dismiss for the fabric-hint Stop
|
|
678
657
|
// hook nudges. Any signal type listed here is suppressed at emit time across
|
|
679
658
|
// all sessions (the session-scoped sibling lives in a .fabric/.cache sidecar
|
|
680
659
|
// written on request). Mirrors the cite_evict_interval=0 opt-out convention —
|
|
681
660
|
// a knob for an existing surface, not a new feature. Unknown types ignored.
|
|
682
|
-
hint_dismiss_signals:
|
|
661
|
+
hint_dismiss_signals: z5.array(z5.enum(["archive", "review", "import", "maintenance"])).optional(),
|
|
683
662
|
// v2.1 ADJ-NEWN-4: user-override escape hatches for the two strong behavioral
|
|
684
663
|
// policies (cite-before-edit + self-archive). The strong policies can make an
|
|
685
664
|
// agent feel like a "stubborn parrot" (D2 user-in-control red line); these
|
|
@@ -691,15 +670,15 @@ var fabricConfigSchema = z6.object({
|
|
|
691
670
|
// surface, mirroring the cite_evict_interval=0 / hint_dismiss_signals opt-out
|
|
692
671
|
// convention, NOT a new feature. Wave3 J32 will quantify the friction these
|
|
693
672
|
// relieve; until then they ship as inert-safe opt-outs.
|
|
694
|
-
cite_policy_enabled:
|
|
695
|
-
self_archive_policy_enabled:
|
|
673
|
+
cite_policy_enabled: z5.boolean().optional().default(true),
|
|
674
|
+
self_archive_policy_enabled: z5.boolean().optional().default(true),
|
|
696
675
|
// v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the narrow PreToolUse hint
|
|
697
676
|
// emitted by knowledge-hint-narrow.cjs. After filtering to entries whose
|
|
698
677
|
// `relevance_scope === "narrow"` (rc.27 TASK-005 audit §2.5 fix), the hook
|
|
699
678
|
// slices to this many before the E3 emit-gate / renderSummary pipeline.
|
|
700
679
|
// Default 5 keeps each per-Edit hint terse — five lines max so the agent's
|
|
701
680
|
// working memory is not displaced by an unwieldy banner. Range 1..20.
|
|
702
|
-
hint_narrow_top_k:
|
|
681
|
+
hint_narrow_top_k: z5.number().int().min(1).max(20).optional().default(5),
|
|
703
682
|
// v2.0.0-rc.33 W2-1 (P0-9): per-file dedup window (in PreToolUse turns) for
|
|
704
683
|
// the narrow hint. Same (file_path, stable_id) tuple stays silent for this
|
|
705
684
|
// many turns even when the E3 cross-session cache would otherwise re-emit.
|
|
@@ -709,7 +688,7 @@ var fabricConfigSchema = z6.object({
|
|
|
709
688
|
// Storage: .fabric/.cache/narrow-dedup-window.json — distinct from session-
|
|
710
689
|
// hints cache so a window-only suppression does not poison cross-session
|
|
711
690
|
// dedupe semantics.
|
|
712
|
-
hint_narrow_dedup_window_turns:
|
|
691
|
+
hint_narrow_dedup_window_turns: z5.number().int().min(1).max(50).optional().default(5),
|
|
713
692
|
// v2.0.0-rc.33 W2-5 (P1-8): cooldown between broad SessionStart hint emits,
|
|
714
693
|
// in hours. Distinct from the archive_hint_cooldown_hours that gates the
|
|
715
694
|
// fabric-hint Stop hook — knowledge-hint-broad re-fires on every
|
|
@@ -718,14 +697,14 @@ var fabricConfigSchema = z6.object({
|
|
|
718
697
|
// menu at most once per hour"; 0 means "no cooldown, current behavior."
|
|
719
698
|
// Range 0..168 (one week). Stored alongside fabric-hint's cooldown cache
|
|
720
699
|
// under a distinct knowledge-hint-broad key.
|
|
721
|
-
hint_broad_cooldown_hours:
|
|
700
|
+
hint_broad_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
|
|
722
701
|
// v2.0.0-rc.33 W2-5 (P1-8): cooldown for the narrow PreToolUse hint.
|
|
723
702
|
// Same shape as hint_broad_cooldown_hours but applies to per-Edit hint
|
|
724
703
|
// re-emission across the cooldown window — independent of E3 session-
|
|
725
704
|
// hints dedupe. Default 0 preserves rc.32 behavior; set to e.g. 1 to
|
|
726
705
|
// throttle hint frequency during rapid-fire editing sprints. Range
|
|
727
706
|
// 0..168 (one week).
|
|
728
|
-
hint_narrow_cooldown_hours:
|
|
707
|
+
hint_narrow_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
|
|
729
708
|
// v2.0.0-rc.33 W4-B3 (T5 P2): per-maturity inactivity thresholds (days)
|
|
730
709
|
// driving orphan_demote. Hardcoded at proven=90/verified=30/draft=14 in
|
|
731
710
|
// rc.32; chatty workspaces want them tighter, slow ones want them looser.
|
|
@@ -733,26 +712,19 @@ var fabricConfigSchema = z6.object({
|
|
|
733
712
|
// chosen so a typo can't accidentally disable the lint (min 1).
|
|
734
713
|
//
|
|
735
714
|
// v2.2 W3-T5 (F-MATURITY-ENDORSED): the canonical maturity enum is
|
|
736
|
-
// draft/verified/proven (KT-DEC-0005)
|
|
737
|
-
//
|
|
738
|
-
//
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
orphan_demote_proven_days: z6.number().int().min(1).max(3650).optional(),
|
|
743
|
-
orphan_demote_verified_days: z6.number().int().min(1).max(3650).optional(),
|
|
744
|
-
orphan_demote_draft_days: z6.number().int().min(1).max(3650).optional(),
|
|
745
|
-
// Legacy aliases (deprecated; map to proven/verified). Kept so existing
|
|
746
|
-
// configs do not silently lose their tuning.
|
|
747
|
-
orphan_demote_stable_days: z6.number().int().min(1).max(3650).optional(),
|
|
748
|
-
orphan_demote_endorsed_days: z6.number().int().min(1).max(3650).optional(),
|
|
715
|
+
// draft/verified/proven (KT-DEC-0005). These threshold keys use the canonical
|
|
716
|
+
// vocabulary; the loader maps proven→stable / verified→endorsed onto the
|
|
717
|
+
// doctor's internal orphan_demote ladder.
|
|
718
|
+
orphan_demote_proven_days: z5.number().int().min(1).max(3650).optional(),
|
|
719
|
+
orphan_demote_verified_days: z5.number().int().min(1).max(3650).optional(),
|
|
720
|
+
orphan_demote_draft_days: z5.number().int().min(1).max(3650).optional(),
|
|
749
721
|
// v2.0.0-rc.33 W4-A3 (T4 P2): per-entry summary truncation length used by
|
|
750
722
|
// knowledge-hint-{broad,narrow}.cjs. Hard-coded at 80 chars in rc.32 — too
|
|
751
723
|
// short for entries with parameterized summaries (e.g. "Use bcrypt with
|
|
752
724
|
// cost=12 for password hashing"), too long for terse pitfalls. Range 40..240;
|
|
753
725
|
// default 80 preserves rc.32 behavior. Both hooks read the same key so the
|
|
754
726
|
// banner styling stays consistent across SessionStart + PreToolUse.
|
|
755
|
-
hint_summary_max_len:
|
|
727
|
+
hint_summary_max_len: z5.number().int().min(40).max(240).optional().default(80),
|
|
756
728
|
// v2.0.0-rc.33 W2-6 (P0-7 + P0-8): when true, knowledge-hint hooks emit
|
|
757
729
|
// their banners as `hookSpecificOutput.additionalContext` JSON on stdout
|
|
758
730
|
// (per Claude Code PreToolUse hook contract — see
|
|
@@ -762,7 +734,7 @@ var fabricConfigSchema = z6.object({
|
|
|
762
734
|
// coverage focus (rc.32 baseline 3.1% → primary cause: reminders never
|
|
763
735
|
// entered model context). Set false to revert to legacy stderr-only mode
|
|
764
736
|
// for hosts that don't honor the JSON contract.
|
|
765
|
-
hint_reminder_to_context:
|
|
737
|
+
hint_reminder_to_context: z5.boolean().optional().default(true),
|
|
766
738
|
// v2.0.0-rc.29 TASK-008 (BUG-F3): selection-token TTL override. The
|
|
767
739
|
// `fab_plan_context` MCP tool hands clients a `selection_token` whose default
|
|
768
740
|
// 5-minute lifetime (`SELECTION_TOKEN_TTL_MS` at
|
|
@@ -790,19 +762,27 @@ var fabricConfigSchema = z6.object({
|
|
|
790
762
|
// up together. Per-field knobs (plan_context_top_k, mcpPayloadLimits.*,
|
|
791
763
|
// hint_broad_budget_chars) still override the profile when set. See
|
|
792
764
|
// retrieval-budget.ts (resolveRetrievalBudget) for the resolution order.
|
|
793
|
-
retrieval_budget_profile:
|
|
765
|
+
retrieval_budget_profile: z5.enum(["conservative", "balanced", "generous"]).optional(),
|
|
766
|
+
// grill-report C-009 / R2-R5 mitigation knob: per-field override for the
|
|
767
|
+
// fab_recall body-tier budget (bytes). Absent → derived from the profile
|
|
768
|
+
// (balanced = 4096). Bump it if the lean default drops too many bodies and the
|
|
769
|
+
// AI is not following up with fab_get_knowledge_sections (R2 observation).
|
|
770
|
+
// Range 256B..1MB — below 256B even one body never fits; above 1MB defeats the
|
|
771
|
+
// whole lean-recall purpose. Overrides the profile when set, like the other
|
|
772
|
+
// per-field knobs.
|
|
773
|
+
recall_body_budget_bytes: z5.number().int().min(256).max(1048576).optional(),
|
|
794
774
|
// v2.2 C2-vector (W2-T7): OPTIONAL dense-embedding semantic retrieval, layered
|
|
795
775
|
// as a recall supplement after BM25. Default OFF (`--no-embed` is the baseline);
|
|
796
776
|
// requires the operator to install the optional `fastembed` package — absent →
|
|
797
777
|
// text-only fallback. Never grows the default install footprint.
|
|
798
|
-
embed_enabled:
|
|
778
|
+
embed_enabled: z5.boolean().optional().default(false),
|
|
799
779
|
// Weight applied to the 0..1 cosine similarity before it joins the additive
|
|
800
780
|
// score. Capped at 49 — strictly BELOW BM25_WEIGHT (50) — so a perfect vector
|
|
801
781
|
// match (weight × 1) can never outscore a single strong BM25 term match. This
|
|
802
782
|
// ENFORCES the "vectors supplement, never override lexical relevance"
|
|
803
783
|
// invariant in the schema rather than leaving it to a comment (W2-REVIEW codex
|
|
804
784
|
// MED-4). Range 0..49; default 30.
|
|
805
|
-
embed_weight:
|
|
785
|
+
embed_weight: z5.number().int().min(0).max(49).optional().default(30),
|
|
806
786
|
// v2.1 ③ vector-chinese-model (P3): which fastembed model to load. The prior
|
|
807
787
|
// code pinned fastembed's English default (bge-small-en-v1.5) — wrong for the
|
|
808
788
|
// Chinese-heavy zh-CN-hybrid KB. Values are the fastembed@2.x EmbeddingModel
|
|
@@ -812,7 +792,7 @@ var fabricConfigSchema = z6.object({
|
|
|
812
792
|
// available for full multilingual recall at a ~1GB download + slower CPU cost.
|
|
813
793
|
// (V1 research: fastembed@2.1.0 has NO multilingual-e5-SMALL — the originally
|
|
814
794
|
// planned pin — so bge-small-zh is the light Chinese choice.)
|
|
815
|
-
embed_model:
|
|
795
|
+
embed_model: z5.enum([
|
|
816
796
|
"fast-bge-small-zh-v1.5",
|
|
817
797
|
"fast-multilingual-e5-large",
|
|
818
798
|
"fast-bge-small-en-v1.5",
|
|
@@ -824,8 +804,8 @@ var fabricConfigSchema = z6.object({
|
|
|
824
804
|
});
|
|
825
805
|
|
|
826
806
|
// src/schemas/fabric-config-introspect.ts
|
|
827
|
-
import { z as
|
|
828
|
-
var positiveIntSchema =
|
|
807
|
+
import { z as z6 } from "zod";
|
|
808
|
+
var positiveIntSchema = z6.coerce.number().int().positive();
|
|
829
809
|
function makePositiveIntField(key, defaultValue) {
|
|
830
810
|
return {
|
|
831
811
|
key,
|
|
@@ -881,6 +861,28 @@ function makeEnumField(key, group, enumValues, defaultValue) {
|
|
|
881
861
|
}
|
|
882
862
|
};
|
|
883
863
|
}
|
|
864
|
+
function makeBooleanField(key, defaultValue) {
|
|
865
|
+
return {
|
|
866
|
+
key,
|
|
867
|
+
group: "D_behavior",
|
|
868
|
+
widget: "select",
|
|
869
|
+
label_i18n_key: `cli.config.fields.${key}.label`,
|
|
870
|
+
description_i18n_key: `cli.config.fields.${key}.description`,
|
|
871
|
+
default: String(defaultValue),
|
|
872
|
+
enum_values: ["true", "false"],
|
|
873
|
+
validate(raw) {
|
|
874
|
+
const trimmed = raw.trim();
|
|
875
|
+
if (trimmed === "true") return { ok: true, value: true };
|
|
876
|
+
if (trimmed === "false") return { ok: true, value: false };
|
|
877
|
+
return { ok: false, error: "Must be one of: true, false." };
|
|
878
|
+
},
|
|
879
|
+
format_for_display(value) {
|
|
880
|
+
if (typeof value === "boolean") return String(value);
|
|
881
|
+
if (value === void 0 || value === null) return String(defaultValue);
|
|
882
|
+
return String(value);
|
|
883
|
+
}
|
|
884
|
+
};
|
|
885
|
+
}
|
|
884
886
|
var SCHEMA_DEFAULTS = fabricConfigSchema.parse({});
|
|
885
887
|
function pickNumberDefault(key) {
|
|
886
888
|
const v = SCHEMA_DEFAULTS[key];
|
|
@@ -909,12 +911,13 @@ function getPanelFieldByKey(key) {
|
|
|
909
911
|
}
|
|
910
912
|
var PANEL_FIELDS = [
|
|
911
913
|
// --- Group A: Locale (2) ---
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
914
|
+
// grill-6fixes (D1): `fabric_language` is no longer a project-config field —
|
|
915
|
+
// it is the single machine-wide tone in `~/.fabric/fabric-global.json`. The
|
|
916
|
+
// panel still surfaces it (the `fabric config` language entry), but config.ts
|
|
917
|
+
// special-cases this key to read/write the GLOBAL config instead of the
|
|
918
|
+
// project file. Default is a literal "en" since there is no project-schema
|
|
919
|
+
// default to derive from.
|
|
920
|
+
makeEnumField("fabric_language", "A_locale", fabricLanguageSchema.options, "en"),
|
|
918
921
|
makeEnumField(
|
|
919
922
|
"default_layer_filter",
|
|
920
923
|
"A_locale",
|
|
@@ -958,39 +961,23 @@ var PANEL_FIELDS = [
|
|
|
958
961
|
"C_audit",
|
|
959
962
|
auditModeSchema.options,
|
|
960
963
|
AUDIT_MODE_PANEL_DEFAULT
|
|
961
|
-
)
|
|
964
|
+
),
|
|
965
|
+
// --- Group D: Behavior / features (2) ---
|
|
966
|
+
// nudge_mode — the master switch for the human-visible nudge experience
|
|
967
|
+
// (the most user-facing runtime knob, previously JSON-only). embed_enabled —
|
|
968
|
+
// vector semantic recall, panel-editable now that config lives in `.fabric`
|
|
969
|
+
// (A1); enabling also needs the host-side `fabric install --enable-embed`.
|
|
970
|
+
makeEnumField("nudge_mode", "D_behavior", nudgeModeSchema.options, "normal"),
|
|
971
|
+
makeBooleanField("embed_enabled", false)
|
|
962
972
|
];
|
|
963
973
|
|
|
964
|
-
// src/schemas/scope.ts
|
|
965
|
-
import { z as z8 } from "zod";
|
|
966
|
-
var PERSONAL_SCOPE = "personal";
|
|
967
|
-
var KNOWN_SCOPE_PREFIXES = ["personal", "team", "project", "org"];
|
|
968
|
-
var SCOPE_COORDINATE_PATTERN = /^[a-z0-9_-]+(:[a-z0-9_-]+)*$/u;
|
|
969
|
-
var scopeCoordinateSchema = z8.string().min(1).regex(
|
|
970
|
-
SCOPE_COORDINATE_PATTERN,
|
|
971
|
-
"scope coordinate must be ':'-joined lowercase [a-z0-9_-] segments"
|
|
972
|
-
);
|
|
973
|
-
function scopeRoot(coordinate) {
|
|
974
|
-
const colon = coordinate.indexOf(":");
|
|
975
|
-
return colon === -1 ? coordinate : coordinate.slice(0, colon);
|
|
976
|
-
}
|
|
977
|
-
function isPersonalScope(coordinate) {
|
|
978
|
-
return scopeRoot(coordinate) === PERSONAL_SCOPE;
|
|
979
|
-
}
|
|
980
|
-
var entryScopeMetadataSchema = z8.object({
|
|
981
|
-
semantic_scope: scopeCoordinateSchema,
|
|
982
|
-
// Store alias or UUID. Validated as a non-empty string here; the resolver
|
|
983
|
-
// (P0.6) maps alias→UUID and verifies the store is in the read-set.
|
|
984
|
-
visibility_store: z8.string().min(1)
|
|
985
|
-
}).strict();
|
|
986
|
-
|
|
987
974
|
// src/schemas/store-stable-id.ts
|
|
988
|
-
import { z as
|
|
975
|
+
import { z as z7 } from "zod";
|
|
989
976
|
var localKnowledgeIdSchema = StableIdSchema;
|
|
990
977
|
var UID_SEGMENT_PATTERN = /^[a-z0-9-]+$/u;
|
|
991
|
-
var uidSchema =
|
|
978
|
+
var uidSchema = z7.string().min(1).regex(UID_SEGMENT_PATTERN, "uid must be lowercase [a-z0-9-] segments");
|
|
992
979
|
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;
|
|
993
|
-
var globalRefSchema =
|
|
980
|
+
var globalRefSchema = z7.string().regex(GLOBAL_REF_PATTERN, "global_ref must be <store_uuid>[:<uid>]:<local_id>");
|
|
994
981
|
function formatGlobalRef(parts) {
|
|
995
982
|
const { store_uuid, uid, local_id } = parts;
|
|
996
983
|
return uid === void 0 ? `${store_uuid}:${local_id}` : `${store_uuid}:${uid}:${local_id}`;
|
|
@@ -1015,14 +1002,14 @@ function parseGlobalRef(ref) {
|
|
|
1015
1002
|
local_id
|
|
1016
1003
|
};
|
|
1017
1004
|
}
|
|
1018
|
-
var storeKnowledgeTypeCountersSchema =
|
|
1019
|
-
MOD:
|
|
1020
|
-
DEC:
|
|
1021
|
-
GLD:
|
|
1022
|
-
PIT:
|
|
1023
|
-
PRO:
|
|
1005
|
+
var storeKnowledgeTypeCountersSchema = z7.object({
|
|
1006
|
+
MOD: z7.number().int().nonnegative().default(0),
|
|
1007
|
+
DEC: z7.number().int().nonnegative().default(0),
|
|
1008
|
+
GLD: z7.number().int().nonnegative().default(0),
|
|
1009
|
+
PIT: z7.number().int().nonnegative().default(0),
|
|
1010
|
+
PRO: z7.number().int().nonnegative().default(0)
|
|
1024
1011
|
}).default({ MOD: 0, DEC: 0, GLD: 0, PIT: 0, PRO: 0 });
|
|
1025
|
-
var storeCountersSchema =
|
|
1012
|
+
var storeCountersSchema = z7.object({
|
|
1026
1013
|
KP: storeKnowledgeTypeCountersSchema,
|
|
1027
1014
|
KT: storeKnowledgeTypeCountersSchema
|
|
1028
1015
|
}).default({
|
|
@@ -1031,64 +1018,63 @@ var storeCountersSchema = z9.object({
|
|
|
1031
1018
|
});
|
|
1032
1019
|
|
|
1033
1020
|
// src/schemas/parity-matrix.ts
|
|
1034
|
-
import { z as
|
|
1035
|
-
var PARITY_CLIENTS = ["claudeCode", "codexCLI"
|
|
1036
|
-
var parityClientSchema =
|
|
1021
|
+
import { z as z8 } from "zod";
|
|
1022
|
+
var PARITY_CLIENTS = ["claudeCode", "codexCLI"];
|
|
1023
|
+
var parityClientSchema = z8.enum(PARITY_CLIENTS);
|
|
1037
1024
|
var PARITY_SURFACES = ["skill", "hook", "mcp", "render"];
|
|
1038
|
-
var paritySurfaceSchema =
|
|
1039
|
-
var parityClientExpectationSchema =
|
|
1040
|
-
supported:
|
|
1041
|
-
mechanism:
|
|
1042
|
-
notes:
|
|
1025
|
+
var paritySurfaceSchema = z8.enum(PARITY_SURFACES);
|
|
1026
|
+
var parityClientExpectationSchema = z8.object({
|
|
1027
|
+
supported: z8.boolean(),
|
|
1028
|
+
mechanism: z8.string().optional(),
|
|
1029
|
+
notes: z8.string().optional()
|
|
1043
1030
|
}).strict();
|
|
1044
|
-
var parityCapabilitySchema =
|
|
1045
|
-
id:
|
|
1031
|
+
var parityCapabilitySchema = z8.object({
|
|
1032
|
+
id: z8.string().min(1),
|
|
1046
1033
|
surface: paritySurfaceSchema,
|
|
1047
|
-
description:
|
|
1048
|
-
clients:
|
|
1034
|
+
description: z8.string().min(1),
|
|
1035
|
+
clients: z8.object({
|
|
1049
1036
|
claudeCode: parityClientExpectationSchema,
|
|
1050
|
-
codexCLI: parityClientExpectationSchema
|
|
1051
|
-
cursor: parityClientExpectationSchema
|
|
1037
|
+
codexCLI: parityClientExpectationSchema
|
|
1052
1038
|
}).strict()
|
|
1053
1039
|
}).strict();
|
|
1054
|
-
var parityMatrixSchema =
|
|
1040
|
+
var parityMatrixSchema = z8.object({
|
|
1055
1041
|
// Schema/version tag of the matrix document itself.
|
|
1056
|
-
version:
|
|
1042
|
+
version: z8.string().min(1),
|
|
1057
1043
|
// Free-form note on what release/milestone this matrix targets.
|
|
1058
|
-
generated_for:
|
|
1059
|
-
capabilities:
|
|
1044
|
+
generated_for: z8.string().min(1),
|
|
1045
|
+
capabilities: z8.array(parityCapabilitySchema).min(1)
|
|
1060
1046
|
}).strict();
|
|
1061
1047
|
|
|
1062
1048
|
// src/resolver/contracts.ts
|
|
1063
|
-
import { z as
|
|
1049
|
+
import { z as z9 } from "zod";
|
|
1064
1050
|
var PROJECT_ROOT_SIGNALS = ["env", "marker", "cwd", "repo"];
|
|
1065
|
-
var projectRootSignalSchema =
|
|
1066
|
-
var projectRootSignalsSchema =
|
|
1051
|
+
var projectRootSignalSchema = z9.enum(PROJECT_ROOT_SIGNALS);
|
|
1052
|
+
var projectRootSignalsSchema = z9.object({
|
|
1067
1053
|
// FABRIC_PROJECT_ROOT, if set.
|
|
1068
|
-
env:
|
|
1054
|
+
env: z9.string().optional(),
|
|
1069
1055
|
// Nearest directory AT-OR-ABOVE cwd holding `.fabric/fabric-config.json`,
|
|
1070
1056
|
// if any (the upward marker search result; may equal cwd).
|
|
1071
|
-
markerDir:
|
|
1057
|
+
markerDir: z9.string().optional(),
|
|
1072
1058
|
// Always present — the process cwd.
|
|
1073
|
-
cwd:
|
|
1059
|
+
cwd: z9.string().min(1),
|
|
1074
1060
|
// git repo root, if inside a repo.
|
|
1075
|
-
repoRoot:
|
|
1061
|
+
repoRoot: z9.string().optional(),
|
|
1076
1062
|
// The `project_id` read from the winning root's fabric-config.json during
|
|
1077
1063
|
// (fs) signal collection. The pure resolver echoes it — it cannot invent a
|
|
1078
1064
|
// UUID. Worktrees of one repo share the committed config, hence the same
|
|
1079
1065
|
// project_id (S45 merge). Absent when no .fabric config exists at the root
|
|
1080
1066
|
// yet (fresh repo-fallback) → resolution still yields the root with a null
|
|
1081
1067
|
// projectId so the caller can mint+persist one at install time.
|
|
1082
|
-
discoveredProjectId:
|
|
1068
|
+
discoveredProjectId: z9.string().optional()
|
|
1083
1069
|
}).strict();
|
|
1084
|
-
var projectRootResolutionSchema =
|
|
1070
|
+
var projectRootResolutionSchema = z9.object({
|
|
1085
1071
|
// Absolute project root directory.
|
|
1086
|
-
projectRoot:
|
|
1072
|
+
projectRoot: z9.string().min(1),
|
|
1087
1073
|
// Stable project identity. One repo = one .fabric = one project_id (S32);
|
|
1088
1074
|
// git worktrees of the same repo resolve to the SAME project_id (S45).
|
|
1089
1075
|
// Null when the resolved root has no fabric-config.json yet (fresh
|
|
1090
1076
|
// repo-fallback) — the caller mints + persists a UUID at install time.
|
|
1091
|
-
projectId:
|
|
1077
|
+
projectId: z9.string().min(1).nullable(),
|
|
1092
1078
|
// Which signal won.
|
|
1093
1079
|
signalUsed: projectRootSignalSchema
|
|
1094
1080
|
}).strict();
|
|
@@ -1097,91 +1083,96 @@ var STORE_RESOLVER_WARNING_CODES = [
|
|
|
1097
1083
|
// required_stores entry has no matching mounted store (S51)
|
|
1098
1084
|
"local_only_no_remote",
|
|
1099
1085
|
// mounted but local-only (R5#5 nudge, non-fatal)
|
|
1100
|
-
"alias_unresolved"
|
|
1086
|
+
"alias_unresolved",
|
|
1101
1087
|
// referenced alias maps to no mounted store
|
|
1088
|
+
"missing_write_route"
|
|
1089
|
+
// multi/shared write requires an explicit route
|
|
1102
1090
|
];
|
|
1103
|
-
var storeResolverWarningCodeSchema =
|
|
1104
|
-
var storeResolverWarningSchema =
|
|
1091
|
+
var storeResolverWarningCodeSchema = z9.enum(STORE_RESOLVER_WARNING_CODES);
|
|
1092
|
+
var storeResolverWarningSchema = z9.object({
|
|
1105
1093
|
code: storeResolverWarningCodeSchema,
|
|
1106
1094
|
// The alias/UUID/id the warning concerns.
|
|
1107
|
-
ref:
|
|
1108
|
-
message:
|
|
1095
|
+
ref: z9.string().min(1),
|
|
1096
|
+
message: z9.string().min(1)
|
|
1109
1097
|
}).strict();
|
|
1110
|
-
var readSetEntrySchema =
|
|
1111
|
-
store_uuid:
|
|
1112
|
-
alias:
|
|
1113
|
-
remote:
|
|
1098
|
+
var readSetEntrySchema = z9.object({
|
|
1099
|
+
store_uuid: z9.string().min(1),
|
|
1100
|
+
alias: z9.string().min(1),
|
|
1101
|
+
remote: z9.string().min(1).optional(),
|
|
1114
1102
|
// Whether this store accepts writes from the current context. Personal
|
|
1115
1103
|
// store is writable; shared stores writable iff mounted with write intent.
|
|
1116
|
-
writable:
|
|
1104
|
+
writable: z9.boolean()
|
|
1117
1105
|
}).strict();
|
|
1118
|
-
var storeReadSetSchema =
|
|
1119
|
-
stores:
|
|
1120
|
-
warnings:
|
|
1106
|
+
var storeReadSetSchema = z9.object({
|
|
1107
|
+
stores: z9.array(readSetEntrySchema),
|
|
1108
|
+
warnings: z9.array(storeResolverWarningSchema)
|
|
1121
1109
|
}).strict();
|
|
1122
|
-
var writeTargetSchema =
|
|
1123
|
-
store_uuid:
|
|
1124
|
-
alias:
|
|
1110
|
+
var writeTargetSchema = z9.object({
|
|
1111
|
+
store_uuid: z9.string().min(1),
|
|
1112
|
+
alias: z9.string().min(1)
|
|
1125
1113
|
}).strict();
|
|
1126
|
-
var storeResolveInputSchema =
|
|
1114
|
+
var storeResolveInputSchema = z9.object({
|
|
1127
1115
|
// Machine identity (S33) — namespaces personal ids; identifies personal store.
|
|
1128
|
-
uid:
|
|
1116
|
+
uid: z9.string().min(1),
|
|
1129
1117
|
// Stores mounted on this machine (from global config).
|
|
1130
|
-
mountedStores:
|
|
1131
|
-
|
|
1132
|
-
store_uuid:
|
|
1133
|
-
alias:
|
|
1134
|
-
|
|
1135
|
-
|
|
1118
|
+
mountedStores: z9.array(
|
|
1119
|
+
z9.object({
|
|
1120
|
+
store_uuid: z9.string().min(1),
|
|
1121
|
+
alias: z9.string().min(1),
|
|
1122
|
+
mount_name: z9.string().min(1).optional(),
|
|
1123
|
+
remote: z9.string().min(1).optional(),
|
|
1124
|
+
writable: z9.boolean().default(true),
|
|
1136
1125
|
// Marks the implicit personal store.
|
|
1137
|
-
personal:
|
|
1126
|
+
personal: z9.boolean().default(false)
|
|
1138
1127
|
}).strict()
|
|
1139
1128
|
),
|
|
1140
1129
|
// The project's declared required_stores (ids/aliases + optional remote).
|
|
1141
|
-
requiredStores:
|
|
1142
|
-
|
|
1143
|
-
id:
|
|
1144
|
-
suggested_remote:
|
|
1130
|
+
requiredStores: z9.array(
|
|
1131
|
+
z9.object({
|
|
1132
|
+
id: z9.string().min(1),
|
|
1133
|
+
suggested_remote: z9.string().min(1).optional()
|
|
1145
1134
|
}).strict()
|
|
1146
1135
|
),
|
|
1147
1136
|
// Alias selected as the active write store for non-personal scopes, if any.
|
|
1148
|
-
activeWriteAlias:
|
|
1137
|
+
activeWriteAlias: z9.string().min(1).optional(),
|
|
1138
|
+
// Scope-aware write routes. Exact scope wins first, then longest prefix route.
|
|
1139
|
+
writeRoutes: z9.array(
|
|
1140
|
+
z9.object({
|
|
1141
|
+
scope: z9.string().min(1),
|
|
1142
|
+
store: z9.string().min(1)
|
|
1143
|
+
}).strict()
|
|
1144
|
+
).optional().default([]),
|
|
1145
|
+
defaultWriteAlias: z9.string().min(1).optional()
|
|
1149
1146
|
}).strict();
|
|
1150
|
-
var projectRootGoldenCaseSchema =
|
|
1151
|
-
name:
|
|
1152
|
-
note:
|
|
1147
|
+
var projectRootGoldenCaseSchema = z9.object({
|
|
1148
|
+
name: z9.string().min(1),
|
|
1149
|
+
note: z9.string().optional(),
|
|
1153
1150
|
signals: projectRootSignalsSchema,
|
|
1154
1151
|
// null expected = resolver should return no root for these signals.
|
|
1155
1152
|
expected: projectRootResolutionSchema.nullable()
|
|
1156
1153
|
}).strict();
|
|
1157
|
-
var projectRootGoldenFileSchema =
|
|
1158
|
-
contract:
|
|
1159
|
-
cases:
|
|
1154
|
+
var projectRootGoldenFileSchema = z9.object({
|
|
1155
|
+
contract: z9.literal("project-root.golden"),
|
|
1156
|
+
cases: z9.array(projectRootGoldenCaseSchema).min(1)
|
|
1160
1157
|
}).strict();
|
|
1161
|
-
var readSetGoldenCaseSchema =
|
|
1162
|
-
name:
|
|
1163
|
-
note:
|
|
1158
|
+
var readSetGoldenCaseSchema = z9.object({
|
|
1159
|
+
name: z9.string().min(1),
|
|
1160
|
+
note: z9.string().optional(),
|
|
1164
1161
|
input: storeResolveInputSchema,
|
|
1165
1162
|
// Scope under test for the write-target expectation.
|
|
1166
|
-
writeScope:
|
|
1167
|
-
expected:
|
|
1163
|
+
writeScope: z9.string().min(1),
|
|
1164
|
+
expected: z9.object({
|
|
1168
1165
|
readSet: storeReadSetSchema,
|
|
1169
1166
|
writeTarget: writeTargetSchema.nullable(),
|
|
1170
|
-
writeWarnings:
|
|
1167
|
+
writeWarnings: z9.array(storeResolverWarningSchema)
|
|
1171
1168
|
}).strict()
|
|
1172
1169
|
}).strict();
|
|
1173
|
-
var readSetGoldenFileSchema =
|
|
1174
|
-
contract:
|
|
1175
|
-
cases:
|
|
1170
|
+
var readSetGoldenFileSchema = z9.object({
|
|
1171
|
+
contract: z9.literal("read-set.golden"),
|
|
1172
|
+
cases: z9.array(readSetGoldenCaseSchema).min(1)
|
|
1176
1173
|
}).strict();
|
|
1177
1174
|
|
|
1178
1175
|
// src/resolver/project-root-resolver.ts
|
|
1179
|
-
var ResolverNotImplementedError = class extends Error {
|
|
1180
|
-
constructor(what) {
|
|
1181
|
-
super(`${what} is not implemented yet (TDD target)`);
|
|
1182
|
-
this.name = "ResolverNotImplementedError";
|
|
1183
|
-
}
|
|
1184
|
-
};
|
|
1185
1176
|
function createProjectRootResolver() {
|
|
1186
1177
|
return {
|
|
1187
1178
|
resolve(signals) {
|
|
@@ -1219,12 +1210,65 @@ function personalEntry(input) {
|
|
|
1219
1210
|
}
|
|
1220
1211
|
return entry;
|
|
1221
1212
|
}
|
|
1213
|
+
function readSetEntryFromMounted(store) {
|
|
1214
|
+
const entry = {
|
|
1215
|
+
store_uuid: store.store_uuid,
|
|
1216
|
+
alias: store.alias,
|
|
1217
|
+
writable: store.writable
|
|
1218
|
+
};
|
|
1219
|
+
if (store.remote !== void 0) {
|
|
1220
|
+
entry.remote = store.remote;
|
|
1221
|
+
return { entry };
|
|
1222
|
+
}
|
|
1223
|
+
return {
|
|
1224
|
+
entry,
|
|
1225
|
+
warning: {
|
|
1226
|
+
code: "local_only_no_remote",
|
|
1227
|
+
ref: store.alias,
|
|
1228
|
+
message: `store '${store.alias}' is local-only; add a git remote to back it up (\`fabric store ... \` / doctor nudge)`
|
|
1229
|
+
}
|
|
1230
|
+
};
|
|
1231
|
+
}
|
|
1232
|
+
function isMountedPersonal(input, storeUuid) {
|
|
1233
|
+
return input.mountedStores.some((store) => store.store_uuid === storeUuid && store.personal);
|
|
1234
|
+
}
|
|
1235
|
+
function routeMatches(routeScope, scope) {
|
|
1236
|
+
return scope === routeScope || scope.startsWith(`${routeScope}:`);
|
|
1237
|
+
}
|
|
1238
|
+
function resolveRouteAlias(input, scope) {
|
|
1239
|
+
const routes = input.writeRoutes ?? [];
|
|
1240
|
+
const exact = routes.find((route) => route.scope === scope);
|
|
1241
|
+
if (exact !== void 0) {
|
|
1242
|
+
return exact.store;
|
|
1243
|
+
}
|
|
1244
|
+
const prefix = routes.filter((route) => routeMatches(route.scope, scope)).sort((a, b) => b.scope.length - a.scope.length)[0];
|
|
1245
|
+
return prefix?.store ?? input.defaultWriteAlias ?? input.activeWriteAlias;
|
|
1246
|
+
}
|
|
1247
|
+
function hasMultipleSharedStores(input, readSet) {
|
|
1248
|
+
return readSet.stores.filter((store) => !isMountedPersonal(input, store.store_uuid)).length > 1;
|
|
1249
|
+
}
|
|
1250
|
+
function hasExplicitRouteOrDefault(input, scope) {
|
|
1251
|
+
const routes = input.writeRoutes ?? [];
|
|
1252
|
+
return routes.some((route) => routeMatches(route.scope, scope)) || input.defaultWriteAlias !== void 0;
|
|
1253
|
+
}
|
|
1222
1254
|
function createStoreResolver() {
|
|
1223
1255
|
return {
|
|
1224
1256
|
resolveReadSet(input) {
|
|
1225
1257
|
const stores = [];
|
|
1226
1258
|
const warnings = [];
|
|
1259
|
+
const seenStoreUuids = /* @__PURE__ */ new Set();
|
|
1227
1260
|
for (const req of input.requiredStores) {
|
|
1261
|
+
if (req.suggested_remote === "$personal") {
|
|
1262
|
+
const personal2 = findPersonal(input);
|
|
1263
|
+
if (personal2 === void 0) {
|
|
1264
|
+
warnings.push({
|
|
1265
|
+
code: "missing_store",
|
|
1266
|
+
ref: req.id,
|
|
1267
|
+
message: `required store '${req.id}' is not mounted; run \`fabric store add\` (suggested remote: $personal)`
|
|
1268
|
+
});
|
|
1269
|
+
}
|
|
1270
|
+
continue;
|
|
1271
|
+
}
|
|
1228
1272
|
const matched = input.mountedStores.find(
|
|
1229
1273
|
(m) => !m.personal && (m.alias === req.id || m.store_uuid === req.id)
|
|
1230
1274
|
);
|
|
@@ -1237,25 +1281,20 @@ function createStoreResolver() {
|
|
|
1237
1281
|
});
|
|
1238
1282
|
continue;
|
|
1239
1283
|
}
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
entry.remote = matched.remote;
|
|
1247
|
-
} else {
|
|
1248
|
-
warnings.push({
|
|
1249
|
-
code: "local_only_no_remote",
|
|
1250
|
-
ref: matched.alias,
|
|
1251
|
-
message: `store '${matched.alias}' is local-only; add a git remote to back it up (\`fabric store ... \` / doctor nudge)`
|
|
1252
|
-
});
|
|
1284
|
+
if (seenStoreUuids.has(matched.store_uuid)) {
|
|
1285
|
+
continue;
|
|
1286
|
+
}
|
|
1287
|
+
const { entry, warning } = readSetEntryFromMounted(matched);
|
|
1288
|
+
if (warning !== void 0) {
|
|
1289
|
+
warnings.push(warning);
|
|
1253
1290
|
}
|
|
1254
1291
|
stores.push(entry);
|
|
1292
|
+
seenStoreUuids.add(matched.store_uuid);
|
|
1255
1293
|
}
|
|
1256
1294
|
const personal = personalEntry(input);
|
|
1257
|
-
if (personal !== void 0) {
|
|
1295
|
+
if (personal !== void 0 && !seenStoreUuids.has(personal.store_uuid)) {
|
|
1258
1296
|
stores.push(personal);
|
|
1297
|
+
seenStoreUuids.add(personal.store_uuid);
|
|
1259
1298
|
}
|
|
1260
1299
|
return { stores, warnings };
|
|
1261
1300
|
},
|
|
@@ -1276,15 +1315,31 @@ function createStoreResolver() {
|
|
|
1276
1315
|
}
|
|
1277
1316
|
return { target: { store_uuid: p.store_uuid, alias: p.alias }, warnings: [] };
|
|
1278
1317
|
}
|
|
1279
|
-
const
|
|
1318
|
+
const readSet = this.resolveReadSet(input);
|
|
1319
|
+
if (hasMultipleSharedStores(input, readSet) && !hasExplicitRouteOrDefault(input, scope)) {
|
|
1320
|
+
return {
|
|
1321
|
+
target: null,
|
|
1322
|
+
warnings: [
|
|
1323
|
+
{
|
|
1324
|
+
code: "missing_write_route",
|
|
1325
|
+
ref: scope,
|
|
1326
|
+
message: `scope '${scope}' has no explicit write route; set \`fabric store route-write ${scope} <alias>\``
|
|
1327
|
+
}
|
|
1328
|
+
]
|
|
1329
|
+
};
|
|
1330
|
+
}
|
|
1331
|
+
const routeAlias = resolveRouteAlias(input, scope);
|
|
1332
|
+
const active = routeAlias === void 0 ? void 0 : readSet.stores.find(
|
|
1333
|
+
(store) => store.writable && !isMountedPersonal(input, store.store_uuid) && (store.alias === routeAlias || store.store_uuid === routeAlias)
|
|
1334
|
+
);
|
|
1280
1335
|
if (active === void 0) {
|
|
1281
1336
|
return {
|
|
1282
1337
|
target: null,
|
|
1283
1338
|
warnings: [
|
|
1284
1339
|
{
|
|
1285
1340
|
code: "alias_unresolved",
|
|
1286
|
-
ref:
|
|
1287
|
-
message: `no writable store for scope '${scope}'; set
|
|
1341
|
+
ref: routeAlias ?? scope,
|
|
1342
|
+
message: `no writable store for scope '${scope}'; set a write route or default write store`
|
|
1288
1343
|
}
|
|
1289
1344
|
]
|
|
1290
1345
|
};
|
|
@@ -1298,7 +1353,8 @@ function createStoreResolver() {
|
|
|
1298
1353
|
}
|
|
1299
1354
|
|
|
1300
1355
|
// src/resolver/store-disk-reader.ts
|
|
1301
|
-
import { existsSync, readdirSync, readFileSync
|
|
1356
|
+
import { existsSync, lstatSync, readdirSync, readFileSync } from "fs";
|
|
1357
|
+
import { readFile } from "fs/promises";
|
|
1302
1358
|
import { join } from "path";
|
|
1303
1359
|
function readStoreIdentity(absDir) {
|
|
1304
1360
|
const identityFile = join(absDir, STORE_LAYOUT.identityFile);
|
|
@@ -1314,6 +1370,17 @@ function readStoreIdentity(absDir) {
|
|
|
1314
1370
|
const parsed = storeIdentitySchema.safeParse(raw);
|
|
1315
1371
|
return parsed.success ? parsed.data : null;
|
|
1316
1372
|
}
|
|
1373
|
+
async function readStoreIdentityAsync(absDir) {
|
|
1374
|
+
const identityFile = join(absDir, STORE_LAYOUT.identityFile);
|
|
1375
|
+
let raw;
|
|
1376
|
+
try {
|
|
1377
|
+
raw = JSON.parse(await readFile(identityFile, "utf8"));
|
|
1378
|
+
} catch {
|
|
1379
|
+
return null;
|
|
1380
|
+
}
|
|
1381
|
+
const parsed = storeIdentitySchema.safeParse(raw);
|
|
1382
|
+
return parsed.success ? parsed.data : null;
|
|
1383
|
+
}
|
|
1317
1384
|
function recognizeStoreDir(absDir) {
|
|
1318
1385
|
return readStoreIdentity(absDir) !== null;
|
|
1319
1386
|
}
|
|
@@ -1373,10 +1440,14 @@ function findStoreExecutableViolations(absDir, options = {}) {
|
|
|
1373
1440
|
const relPath = rel === "" ? entry : `${rel}/${entry}`;
|
|
1374
1441
|
let stat;
|
|
1375
1442
|
try {
|
|
1376
|
-
stat =
|
|
1443
|
+
stat = lstatSync(abs);
|
|
1377
1444
|
} catch {
|
|
1378
1445
|
continue;
|
|
1379
1446
|
}
|
|
1447
|
+
if (stat.isSymbolicLink()) {
|
|
1448
|
+
violations.push(relPath);
|
|
1449
|
+
continue;
|
|
1450
|
+
}
|
|
1380
1451
|
if (stat.isDirectory()) {
|
|
1381
1452
|
walk(abs, relPath, depth + 1);
|
|
1382
1453
|
continue;
|
|
@@ -1490,9 +1561,10 @@ function resolveCandidates(candidates, options = {}) {
|
|
|
1490
1561
|
}
|
|
1491
1562
|
|
|
1492
1563
|
// src/store/core.ts
|
|
1493
|
-
import {
|
|
1494
|
-
import {
|
|
1564
|
+
import { execFile } from "child_process";
|
|
1565
|
+
import { access, mkdir, readdir, readFile as readFile2, writeFile } from "fs/promises";
|
|
1495
1566
|
import { join as join2 } from "path";
|
|
1567
|
+
import { promisify } from "util";
|
|
1496
1568
|
var STORE_PENDING_DIR = "pending";
|
|
1497
1569
|
var STORE_GITIGNORE = [
|
|
1498
1570
|
"# v2.1 store \u2014 volatile / derived data is never committed",
|
|
@@ -1501,103 +1573,225 @@ var STORE_GITIGNORE = [
|
|
|
1501
1573
|
".cache/",
|
|
1502
1574
|
""
|
|
1503
1575
|
].join("\n");
|
|
1504
|
-
|
|
1505
|
-
|
|
1576
|
+
var execFileAsync = promisify(execFile);
|
|
1577
|
+
async function git(cwd, args) {
|
|
1578
|
+
await execFileAsync("git", args, { cwd });
|
|
1506
1579
|
}
|
|
1507
|
-
function initStore(absDir, identity, options = {}) {
|
|
1580
|
+
async function initStore(absDir, identity, options = {}) {
|
|
1508
1581
|
const parsed = storeIdentitySchema.parse(identity);
|
|
1509
1582
|
const identityFile = join2(absDir, STORE_LAYOUT.identityFile);
|
|
1510
|
-
|
|
1583
|
+
try {
|
|
1584
|
+
await access(identityFile);
|
|
1511
1585
|
throw new Error(`store already initialized at ${absDir} (store.json exists)`);
|
|
1586
|
+
} catch (err) {
|
|
1587
|
+
if (err instanceof Error && err.message.includes("already initialized")) {
|
|
1588
|
+
throw err;
|
|
1589
|
+
}
|
|
1512
1590
|
}
|
|
1513
1591
|
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1592
|
+
const typeDir = join2(absDir, STORE_LAYOUT.knowledgeDir, type);
|
|
1593
|
+
await mkdir(typeDir, { recursive: true });
|
|
1594
|
+
await writeFile(join2(typeDir, ".gitkeep"), "", "utf8");
|
|
1595
|
+
}
|
|
1596
|
+
await mkdir(join2(absDir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR), { recursive: true });
|
|
1597
|
+
await mkdir(join2(absDir, STORE_LAYOUT.bindingsDir), { recursive: true });
|
|
1598
|
+
await mkdir(join2(absDir, STORE_LAYOUT.stateDir), { recursive: true });
|
|
1599
|
+
await writeFile(identityFile, `${JSON.stringify(parsed, null, 2)}
|
|
1520
1600
|
`, "utf8");
|
|
1521
|
-
|
|
1601
|
+
await writeFile(join2(absDir, ".gitignore"), STORE_GITIGNORE, "utf8");
|
|
1522
1602
|
if (options.git !== false) {
|
|
1523
|
-
git(absDir, ["init", "-b", "main"]);
|
|
1603
|
+
await git(absDir, ["init", "-b", "main"]);
|
|
1524
1604
|
}
|
|
1525
|
-
const readBack =
|
|
1605
|
+
const readBack = await readStoreIdentityAsync(absDir);
|
|
1526
1606
|
if (readBack === null) {
|
|
1527
1607
|
throw new Error(`store init wrote an unrecognizable store.json at ${absDir}`);
|
|
1528
1608
|
}
|
|
1529
1609
|
return readBack;
|
|
1530
1610
|
}
|
|
1531
|
-
function listMarkdown(dir) {
|
|
1532
|
-
|
|
1611
|
+
async function listMarkdown(dir) {
|
|
1612
|
+
let entries;
|
|
1613
|
+
try {
|
|
1614
|
+
entries = await readdir(dir);
|
|
1615
|
+
} catch {
|
|
1533
1616
|
return [];
|
|
1534
1617
|
}
|
|
1535
|
-
return
|
|
1618
|
+
return entries.filter((name) => name.endsWith(".md")).sort().map((name) => join2(dir, name));
|
|
1536
1619
|
}
|
|
1537
|
-
function listStoreKnowledge(store) {
|
|
1620
|
+
async function listStoreKnowledge(store) {
|
|
1538
1621
|
const refs = [];
|
|
1539
1622
|
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1540
|
-
for (const file of listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, type))) {
|
|
1623
|
+
for (const file of await listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, type))) {
|
|
1541
1624
|
refs.push({ store_uuid: store.store_uuid, alias: store.alias, type, file });
|
|
1542
1625
|
}
|
|
1543
1626
|
}
|
|
1544
1627
|
return refs;
|
|
1545
1628
|
}
|
|
1546
|
-
function readKnowledgeAcrossStores(stores) {
|
|
1547
|
-
|
|
1629
|
+
async function readKnowledgeAcrossStores(stores) {
|
|
1630
|
+
const lists = await Promise.all(stores.map((store) => listStoreKnowledge(store)));
|
|
1631
|
+
return lists.flat();
|
|
1632
|
+
}
|
|
1633
|
+
function storeProjectsPath(storeDir) {
|
|
1634
|
+
return join2(storeDir, STORE_LAYOUT.projectsFile);
|
|
1548
1635
|
}
|
|
1549
|
-
function
|
|
1550
|
-
|
|
1551
|
-
|
|
1636
|
+
async function readStoreProjects(storeDir) {
|
|
1637
|
+
const path = storeProjectsPath(storeDir);
|
|
1638
|
+
let raw;
|
|
1639
|
+
try {
|
|
1640
|
+
raw = JSON.parse(await readFile2(path, "utf8"));
|
|
1641
|
+
} catch {
|
|
1642
|
+
return [];
|
|
1643
|
+
}
|
|
1644
|
+
const parsed = storeProjectsFileSchema.safeParse(raw);
|
|
1645
|
+
return parsed.success ? parsed.data.projects : [];
|
|
1646
|
+
}
|
|
1647
|
+
async function storeHasProject(storeDir, id) {
|
|
1648
|
+
return (await readStoreProjects(storeDir)).some((p) => p.id === id);
|
|
1649
|
+
}
|
|
1650
|
+
async function addStoreProject(storeDir, project) {
|
|
1651
|
+
const parsed = storeProjectSchema.parse(project);
|
|
1652
|
+
const existing = await readStoreProjects(storeDir);
|
|
1653
|
+
if (existing.some((p) => p.id === parsed.id)) {
|
|
1654
|
+
throw new Error(`project '${parsed.id}' already exists in store at ${storeDir}`);
|
|
1655
|
+
}
|
|
1656
|
+
const next = [...existing, parsed];
|
|
1657
|
+
const validated = storeProjectsFileSchema.parse({ projects: next });
|
|
1658
|
+
await writeFile(storeProjectsPath(storeDir), `${JSON.stringify(validated, null, 2)}
|
|
1659
|
+
`, "utf8");
|
|
1660
|
+
return validated.projects;
|
|
1661
|
+
}
|
|
1662
|
+
async function aggregatePendingAcrossStores(stores) {
|
|
1663
|
+
const lists = await Promise.all(stores.map(
|
|
1664
|
+
async (store) => (await listMarkdown(join2(store.dir, STORE_LAYOUT.knowledgeDir, STORE_PENDING_DIR))).map((file) => ({
|
|
1552
1665
|
store_uuid: store.store_uuid,
|
|
1553
1666
|
alias: store.alias,
|
|
1554
1667
|
type: STORE_PENDING_DIR,
|
|
1555
1668
|
file
|
|
1556
1669
|
}))
|
|
1557
|
-
);
|
|
1670
|
+
));
|
|
1671
|
+
return lists.flat();
|
|
1558
1672
|
}
|
|
1559
1673
|
|
|
1560
|
-
// src/store/
|
|
1561
|
-
import { existsSync as
|
|
1562
|
-
import { homedir } from "os";
|
|
1674
|
+
// src/store/store-counters.ts
|
|
1675
|
+
import { existsSync as existsSync2, readFileSync as readFileSync2, readdirSync as readdirSync2, writeFileSync } from "fs";
|
|
1563
1676
|
import { join as join3 } from "path";
|
|
1564
|
-
function
|
|
1565
|
-
return join3(
|
|
1677
|
+
function storeCountersPath(storeDir) {
|
|
1678
|
+
return join3(storeDir, STORE_LAYOUT.countersFile);
|
|
1679
|
+
}
|
|
1680
|
+
function readStoreCounters(storeDir) {
|
|
1681
|
+
let raw;
|
|
1682
|
+
try {
|
|
1683
|
+
raw = readFileSync2(storeCountersPath(storeDir), "utf8");
|
|
1684
|
+
} catch {
|
|
1685
|
+
return defaultAgentsMetaCounters();
|
|
1686
|
+
}
|
|
1687
|
+
let parsed;
|
|
1688
|
+
try {
|
|
1689
|
+
parsed = JSON.parse(raw);
|
|
1690
|
+
} catch {
|
|
1691
|
+
return defaultAgentsMetaCounters();
|
|
1692
|
+
}
|
|
1693
|
+
const result = AgentsMetaCountersSchema.safeParse(parsed);
|
|
1694
|
+
return result.success ? result.data : defaultAgentsMetaCounters();
|
|
1566
1695
|
}
|
|
1567
|
-
function
|
|
1568
|
-
|
|
1696
|
+
function preserveCorruptCounters(path, raw) {
|
|
1697
|
+
const corruptedPath = `${path}.corrupted.${Date.now()}`;
|
|
1698
|
+
writeFileSync(corruptedPath, raw, "utf8");
|
|
1699
|
+
return corruptedPath;
|
|
1569
1700
|
}
|
|
1570
|
-
function
|
|
1571
|
-
const path =
|
|
1572
|
-
if (!
|
|
1701
|
+
function readStoreCountersForAllocation(storeDir) {
|
|
1702
|
+
const path = storeCountersPath(storeDir);
|
|
1703
|
+
if (!existsSync2(path)) {
|
|
1704
|
+
return defaultAgentsMetaCounters();
|
|
1705
|
+
}
|
|
1706
|
+
const raw = readFileSync2(path, "utf8");
|
|
1707
|
+
let parsed;
|
|
1708
|
+
try {
|
|
1709
|
+
parsed = JSON.parse(raw);
|
|
1710
|
+
} catch (error) {
|
|
1711
|
+
const corruptedPath = preserveCorruptCounters(path, raw);
|
|
1712
|
+
throw new Error(
|
|
1713
|
+
`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)}`
|
|
1714
|
+
);
|
|
1715
|
+
}
|
|
1716
|
+
const result = AgentsMetaCountersSchema.safeParse(parsed);
|
|
1717
|
+
if (!result.success) {
|
|
1718
|
+
const corruptedPath = preserveCorruptCounters(path, raw);
|
|
1719
|
+
throw new Error(
|
|
1720
|
+
`store counters.json is schema-invalid; forensic copy saved to ${corruptedPath}. Run doctor --fix or reconcileStoreCounters before allocating a new stable_id.`
|
|
1721
|
+
);
|
|
1722
|
+
}
|
|
1723
|
+
return result.data;
|
|
1724
|
+
}
|
|
1725
|
+
async function allocateStoreKnowledgeId(layer, type, storeDir) {
|
|
1726
|
+
const countersPath = storeCountersPath(storeDir);
|
|
1727
|
+
return withFileLock(`${countersPath}.lock`, async () => {
|
|
1728
|
+
const counters = readStoreCountersForAllocation(storeDir);
|
|
1729
|
+
const { id, nextCounters } = allocateKnowledgeId(layer, type, counters);
|
|
1730
|
+
await atomicWriteJson(countersPath, nextCounters, { indent: 2 });
|
|
1731
|
+
return id;
|
|
1732
|
+
});
|
|
1733
|
+
}
|
|
1734
|
+
function readEntryId(file) {
|
|
1735
|
+
let content;
|
|
1736
|
+
try {
|
|
1737
|
+
content = readFileSync2(file, "utf8");
|
|
1738
|
+
} catch {
|
|
1573
1739
|
return null;
|
|
1574
1740
|
}
|
|
1575
|
-
|
|
1741
|
+
const match = content.match(/^id:\s*(\S+)\s*$/mu);
|
|
1742
|
+
if (match) {
|
|
1743
|
+
return match[1] ?? null;
|
|
1744
|
+
}
|
|
1745
|
+
const stem = file.slice(file.lastIndexOf("/") + 1).replace(/\.md$/u, "");
|
|
1746
|
+
const idPart = stem.split("--")[0];
|
|
1747
|
+
return idPart.length > 0 ? idPart : null;
|
|
1576
1748
|
}
|
|
1577
|
-
function
|
|
1578
|
-
const
|
|
1579
|
-
|
|
1580
|
-
|
|
1749
|
+
function reconcileStoreCounters(storeDir) {
|
|
1750
|
+
const current = readStoreCounters(storeDir);
|
|
1751
|
+
const next = {
|
|
1752
|
+
KP: { ...current.KP },
|
|
1753
|
+
KT: { ...current.KT }
|
|
1754
|
+
};
|
|
1755
|
+
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
1756
|
+
const dir = join3(storeDir, STORE_LAYOUT.knowledgeDir, type);
|
|
1757
|
+
if (!existsSync2(dir)) {
|
|
1758
|
+
continue;
|
|
1759
|
+
}
|
|
1760
|
+
for (const name of readdirSync2(dir)) {
|
|
1761
|
+
if (!name.endsWith(".md")) {
|
|
1762
|
+
continue;
|
|
1763
|
+
}
|
|
1764
|
+
const parsed = parseKnowledgeId(readEntryId(join3(dir, name)) ?? "");
|
|
1765
|
+
if (parsed === null) {
|
|
1766
|
+
continue;
|
|
1767
|
+
}
|
|
1768
|
+
const layerKey = parsed.layer === "personal" ? "KP" : "KT";
|
|
1769
|
+
const typeCode = KNOWLEDGE_TYPE_CODES[parsed.type];
|
|
1770
|
+
next[layerKey][typeCode] = Math.max(next[layerKey][typeCode], parsed.counter);
|
|
1771
|
+
}
|
|
1772
|
+
}
|
|
1773
|
+
writeFileSync(storeCountersPath(storeDir), `${JSON.stringify(next, null, 2)}
|
|
1581
1774
|
`, "utf8");
|
|
1775
|
+
return next;
|
|
1582
1776
|
}
|
|
1583
1777
|
|
|
1584
1778
|
// src/store/project-config-io.ts
|
|
1585
|
-
import { existsSync as
|
|
1779
|
+
import { existsSync as existsSync3, mkdirSync, readFileSync as readFileSync3, writeFileSync as writeFileSync2 } from "fs";
|
|
1586
1780
|
import { join as join4 } from "path";
|
|
1587
1781
|
function projectConfigPath(projectRoot) {
|
|
1588
1782
|
return join4(projectRoot, ".fabric", "fabric-config.json");
|
|
1589
1783
|
}
|
|
1590
1784
|
function loadProjectConfig(projectRoot) {
|
|
1591
1785
|
const path = projectConfigPath(projectRoot);
|
|
1592
|
-
if (!
|
|
1786
|
+
if (!existsSync3(path)) {
|
|
1593
1787
|
return null;
|
|
1594
1788
|
}
|
|
1595
1789
|
return fabricConfigSchema.parse(JSON.parse(readFileSync3(path, "utf8")));
|
|
1596
1790
|
}
|
|
1597
1791
|
function saveProjectConfig(config, projectRoot) {
|
|
1598
1792
|
const validated = fabricConfigSchema.parse(config);
|
|
1599
|
-
|
|
1600
|
-
|
|
1793
|
+
mkdirSync(join4(projectRoot, ".fabric"), { recursive: true });
|
|
1794
|
+
writeFileSync2(projectConfigPath(projectRoot), `${JSON.stringify(validated, null, 2)}
|
|
1601
1795
|
`, "utf8");
|
|
1602
1796
|
}
|
|
1603
1797
|
|
|
@@ -1613,6 +1807,7 @@ function buildStoreResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
|
1613
1807
|
mountedStores: global.stores.map((s) => ({
|
|
1614
1808
|
store_uuid: s.store_uuid,
|
|
1615
1809
|
alias: s.alias,
|
|
1810
|
+
...s.mount_name === void 0 ? {} : { mount_name: s.mount_name },
|
|
1616
1811
|
...s.remote === void 0 ? {} : { remote: s.remote },
|
|
1617
1812
|
writable: s.writable ?? true,
|
|
1618
1813
|
personal: s.personal ?? false
|
|
@@ -1623,22 +1818,43 @@ function buildStoreResolveInput(projectRoot, globalRoot = resolveGlobalRoot()) {
|
|
|
1623
1818
|
...r.suggested_remote === void 0 ? {} : { suggested_remote: r.suggested_remote }
|
|
1624
1819
|
})
|
|
1625
1820
|
),
|
|
1626
|
-
...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store }
|
|
1821
|
+
...project?.active_write_store === void 0 ? {} : { activeWriteAlias: project.active_write_store },
|
|
1822
|
+
writeRoutes: project?.write_routes ?? [],
|
|
1823
|
+
...project?.default_write_store === void 0 ? {} : { defaultWriteAlias: project.default_write_store }
|
|
1627
1824
|
};
|
|
1628
1825
|
}
|
|
1629
1826
|
|
|
1630
1827
|
// src/store/secret-scan.ts
|
|
1631
|
-
var
|
|
1632
|
-
{ rule: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b
|
|
1633
|
-
{ rule: "private-key-block", re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY
|
|
1634
|
-
{ rule: "openai-api-key", re: /\bsk-[A-Za-z0-9]{20,}\b
|
|
1635
|
-
{ rule: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b
|
|
1636
|
-
{ rule: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b
|
|
1828
|
+
var CREDENTIAL_RULES = [
|
|
1829
|
+
{ rule: "aws-access-key-id", re: /\bAKIA[0-9A-Z]{16}\b/, category: "credential" },
|
|
1830
|
+
{ rule: "private-key-block", re: /-----BEGIN (?:RSA |EC |OPENSSH |DSA |PGP )?PRIVATE KEY-----/, category: "credential" },
|
|
1831
|
+
{ rule: "openai-api-key", re: /\bsk-[A-Za-z0-9]{20,}\b/, category: "credential" },
|
|
1832
|
+
{ rule: "github-token", re: /\bgh[pousr]_[A-Za-z0-9]{20,}\b/, category: "credential" },
|
|
1833
|
+
{ rule: "slack-token", re: /\bxox[baprs]-[A-Za-z0-9-]{10,}\b/, category: "credential" },
|
|
1637
1834
|
{
|
|
1638
1835
|
rule: "credential-assignment",
|
|
1639
|
-
re: /(?:password|passwd|secret|api[_-]?key|access[_-]?token|token)\s*[:=]\s*['"][^'"\s]{8,}[
|
|
1836
|
+
re: /(?:password|passwd|secret|api[_-]?key|access[_-]?token|token)\s*[:=]\s*(?:"[^'"\s]{8,}"|'[^'"\s]{8,}'|[A-Za-z0-9_./+=:@-]{8,})/i,
|
|
1837
|
+
category: "credential"
|
|
1838
|
+
}
|
|
1839
|
+
];
|
|
1840
|
+
var PII_RULES = [
|
|
1841
|
+
{
|
|
1842
|
+
rule: "email-address",
|
|
1843
|
+
re: /\b[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\b/i,
|
|
1844
|
+
category: "pii"
|
|
1845
|
+
},
|
|
1846
|
+
{
|
|
1847
|
+
rule: "ipv4-address",
|
|
1848
|
+
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/,
|
|
1849
|
+
category: "pii"
|
|
1850
|
+
},
|
|
1851
|
+
{
|
|
1852
|
+
rule: "phone-number",
|
|
1853
|
+
re: /(?<!\d)(?:\+?1[-.\s]?)?(?:\(?\d{3}\)?[-.\s]?)\d{3}[-.\s]?\d{4}(?!\d)/,
|
|
1854
|
+
category: "pii"
|
|
1640
1855
|
}
|
|
1641
1856
|
];
|
|
1857
|
+
var SECRET_RULES = [...CREDENTIAL_RULES, ...PII_RULES];
|
|
1642
1858
|
function scanForSecrets(content) {
|
|
1643
1859
|
const findings = [];
|
|
1644
1860
|
const lines = content.split(/\r?\n/u);
|
|
@@ -1652,12 +1868,26 @@ function scanForSecrets(content) {
|
|
|
1652
1868
|
return findings;
|
|
1653
1869
|
}
|
|
1654
1870
|
function hasSecrets(content) {
|
|
1655
|
-
|
|
1871
|
+
const lines = content.split(/\r?\n/u);
|
|
1872
|
+
for (const line of lines) {
|
|
1873
|
+
for (const { re } of CREDENTIAL_RULES) {
|
|
1874
|
+
if (re.test(line)) {
|
|
1875
|
+
return true;
|
|
1876
|
+
}
|
|
1877
|
+
}
|
|
1878
|
+
}
|
|
1879
|
+
return false;
|
|
1656
1880
|
}
|
|
1657
1881
|
var REDACTION_PLACEHOLDER_PREFIX = "[REDACTED:";
|
|
1658
1882
|
function redactSecrets(content) {
|
|
1883
|
+
return redactByRules(content, SECRET_RULES);
|
|
1884
|
+
}
|
|
1885
|
+
function redactPii(content) {
|
|
1886
|
+
return redactByRules(content, PII_RULES);
|
|
1887
|
+
}
|
|
1888
|
+
function redactByRules(content, rules) {
|
|
1659
1889
|
let out = content;
|
|
1660
|
-
for (const { rule, re } of
|
|
1890
|
+
for (const { rule, re } of rules) {
|
|
1661
1891
|
const flags = re.flags.includes("i") ? "gi" : "g";
|
|
1662
1892
|
out = out.replace(new RegExp(re.source, flags), `${REDACTION_PLACEHOLDER_PREFIX}${rule}]`);
|
|
1663
1893
|
}
|
|
@@ -1780,10 +2010,10 @@ function buildDebugBundle(input) {
|
|
|
1780
2010
|
}
|
|
1781
2011
|
|
|
1782
2012
|
// src/schemas/provenance.ts
|
|
1783
|
-
import { z as
|
|
1784
|
-
var knowledgeProvenanceSchema =
|
|
2013
|
+
import { z as z10 } from "zod";
|
|
2014
|
+
var knowledgeProvenanceSchema = z10.object({
|
|
1785
2015
|
store_uuid: storeUuidSchema,
|
|
1786
|
-
alias:
|
|
2016
|
+
alias: z10.string().min(1),
|
|
1787
2017
|
local_id: localKnowledgeIdSchema,
|
|
1788
2018
|
global_ref: globalRefSchema,
|
|
1789
2019
|
// Optional scope coordinate of the entry (resolution axis); present when the
|
|
@@ -1792,7 +2022,7 @@ var knowledgeProvenanceSchema = z12.object({
|
|
|
1792
2022
|
}).strict();
|
|
1793
2023
|
|
|
1794
2024
|
// src/schemas/mcp-store-contracts.ts
|
|
1795
|
-
import { z as
|
|
2025
|
+
import { z as z11 } from "zod";
|
|
1796
2026
|
var MCP_STORE_AWARE_TOOLS = [
|
|
1797
2027
|
"fab_recall",
|
|
1798
2028
|
"fab_plan_context",
|
|
@@ -1801,14 +2031,14 @@ var MCP_STORE_AWARE_TOOLS = [
|
|
|
1801
2031
|
"fab_extract_knowledge",
|
|
1802
2032
|
"fab_review"
|
|
1803
2033
|
];
|
|
1804
|
-
var storeAwareEntrySchema =
|
|
1805
|
-
stable_id:
|
|
2034
|
+
var storeAwareEntrySchema = z11.object({
|
|
2035
|
+
stable_id: z11.string(),
|
|
1806
2036
|
global_ref: globalRefSchema,
|
|
1807
2037
|
provenance: knowledgeProvenanceSchema
|
|
1808
2038
|
}).strict();
|
|
1809
|
-
var writtenToStoreSchema =
|
|
2039
|
+
var writtenToStoreSchema = z11.object({
|
|
1810
2040
|
store_uuid: storeUuidSchema,
|
|
1811
|
-
alias:
|
|
2041
|
+
alias: z11.string().min(1)
|
|
1812
2042
|
}).strict();
|
|
1813
2043
|
var MCP_STORE_AWARE_CONTRACTS = {
|
|
1814
2044
|
fab_recall: { tool: "fab_recall", surfacesEntries: true, echoesWrittenStore: false },
|
|
@@ -1821,7 +2051,7 @@ var MCP_STORE_AWARE_CONTRACTS = {
|
|
|
1821
2051
|
fab_archive_scan: {
|
|
1822
2052
|
tool: "fab_archive_scan",
|
|
1823
2053
|
surfacesEntries: false,
|
|
1824
|
-
echoesWrittenStore:
|
|
2054
|
+
echoesWrittenStore: false
|
|
1825
2055
|
},
|
|
1826
2056
|
fab_extract_knowledge: {
|
|
1827
2057
|
tool: "fab_extract_knowledge",
|
|
@@ -1832,60 +2062,180 @@ var MCP_STORE_AWARE_CONTRACTS = {
|
|
|
1832
2062
|
};
|
|
1833
2063
|
|
|
1834
2064
|
// src/schemas/bindings-snapshot.ts
|
|
1835
|
-
import { z as
|
|
1836
|
-
var resolvedBindingsSnapshotSchema =
|
|
2065
|
+
import { z as z12 } from "zod";
|
|
2066
|
+
var resolvedBindingsSnapshotSchema = z12.object({
|
|
1837
2067
|
// Schema version of the snapshot document.
|
|
1838
|
-
version:
|
|
2068
|
+
version: z12.literal(1),
|
|
1839
2069
|
// The project this snapshot is bound to (S13).
|
|
1840
|
-
project_id:
|
|
2070
|
+
project_id: z12.string().min(1),
|
|
2071
|
+
// The local runtime binding key. Defaults to project_id for standard repos;
|
|
2072
|
+
// worktrees may isolate state by setting fabric-config.workspace_binding_id.
|
|
2073
|
+
workspace_binding_id: z12.string().min(1),
|
|
1841
2074
|
// ISO-8601 generation timestamp (provenance / staleness signal for doctor).
|
|
1842
|
-
generated_at:
|
|
2075
|
+
generated_at: z12.string().min(1),
|
|
1843
2076
|
// Pre-resolved read-set (required_stores ∪ implicit personal + warnings).
|
|
1844
2077
|
read_set: storeReadSetSchema,
|
|
1845
2078
|
// Pre-resolved active write target for non-personal scopes (null if none).
|
|
1846
|
-
write_target: writeTargetSchema.nullable()
|
|
2079
|
+
write_target: writeTargetSchema.nullable(),
|
|
2080
|
+
// Pre-computed store-backed knowledge counts, snapshotted at write time.
|
|
2081
|
+
// PROVENANCE ONLY: these are store-global counts cached in a per-workspace
|
|
2082
|
+
// file, so they go stale whenever store content changes out-of-band (a `git
|
|
2083
|
+
// pull` in the store repo, a sync run from a *different* bound workspace,
|
|
2084
|
+
// etc.) — the snapshot is only regenerated by install/sync/store-ops in the
|
|
2085
|
+
// workspace that runs them (KT-PIT-0017). Hooks MUST NOT trust these numbers
|
|
2086
|
+
// for nudges; they recount live from `knowledge_store_dirs`. Retained for
|
|
2087
|
+
// doctor provenance + backward-compatible fallback when dirs are absent.
|
|
2088
|
+
knowledge_stats: z12.object({
|
|
2089
|
+
pending_count: z12.number().int().nonnegative(),
|
|
2090
|
+
canonical_count: z12.number().int().nonnegative(),
|
|
2091
|
+
oldest_pending_mtime_ms: z12.number().nonnegative().nullable()
|
|
2092
|
+
}).strict().optional(),
|
|
2093
|
+
// Resolved absolute store ROOT dirs the knowledge_stats were derived from.
|
|
2094
|
+
// STABLE across content sync — they only change when mounts/bindings change,
|
|
2095
|
+
// which DOES regenerate the snapshot. Hooks walk `<dir>/knowledge/<type>` +
|
|
2096
|
+
// `<dir>/knowledge/pending` LIVE off these roots so nudge counts are always
|
|
2097
|
+
// fresh regardless of how store content changed (the underseed / review-
|
|
2098
|
+
// backlog false-positive root cure; pairs with knowledge_stats above).
|
|
2099
|
+
knowledge_store_dirs: z12.array(z12.string().min(1)).optional()
|
|
1847
2100
|
}).strict();
|
|
1848
2101
|
|
|
1849
2102
|
// src/store/bindings.ts
|
|
1850
|
-
import {
|
|
2103
|
+
import {
|
|
2104
|
+
existsSync as existsSync4,
|
|
2105
|
+
mkdirSync as mkdirSync2,
|
|
2106
|
+
readdirSync as readdirSync3,
|
|
2107
|
+
readFileSync as readFileSync4,
|
|
2108
|
+
renameSync,
|
|
2109
|
+
statSync,
|
|
2110
|
+
unlinkSync,
|
|
2111
|
+
writeFileSync as writeFileSync3
|
|
2112
|
+
} from "fs";
|
|
1851
2113
|
import { join as join5, resolve, sep } from "path";
|
|
1852
|
-
var
|
|
1853
|
-
function
|
|
1854
|
-
if (!
|
|
2114
|
+
var SAFE_BINDING_ID = /^[A-Za-z0-9._-]+$/;
|
|
2115
|
+
function assertSafeBindingId(bindingId) {
|
|
2116
|
+
if (!SAFE_BINDING_ID.test(bindingId) || bindingId.includes("..")) {
|
|
1855
2117
|
throw new Error(
|
|
1856
|
-
`bindingsSnapshotPath: refusing unsafe
|
|
2118
|
+
`bindingsSnapshotPath: refusing unsafe workspace_binding_id ${JSON.stringify(bindingId)} (must match ${SAFE_BINDING_ID} and contain no "..")`
|
|
1857
2119
|
);
|
|
1858
2120
|
}
|
|
1859
2121
|
}
|
|
1860
|
-
function bindingsSnapshotPath(globalRoot,
|
|
1861
|
-
|
|
2122
|
+
function bindingsSnapshotPath(globalRoot, bindingId) {
|
|
2123
|
+
assertSafeBindingId(bindingId);
|
|
1862
2124
|
const bindingsDir = resolve(join5(globalRoot, GLOBAL_STATE_DIR, GLOBAL_BINDINGS_DIR));
|
|
1863
|
-
const path = resolve(join5(bindingsDir, `${
|
|
2125
|
+
const path = resolve(join5(bindingsDir, `${bindingId}_resolved.json`));
|
|
1864
2126
|
if (path !== bindingsDir && !path.startsWith(bindingsDir + sep)) {
|
|
1865
|
-
throw new Error(`bindingsSnapshotPath: resolved path escapes bindings dir for ${JSON.stringify(
|
|
2127
|
+
throw new Error(`bindingsSnapshotPath: resolved path escapes bindings dir for ${JSON.stringify(bindingId)}`);
|
|
1866
2128
|
}
|
|
1867
2129
|
return path;
|
|
1868
2130
|
}
|
|
2131
|
+
function resolveWorkspaceBindingId(config) {
|
|
2132
|
+
return config.workspace_binding_id ?? config.project_id;
|
|
2133
|
+
}
|
|
2134
|
+
function countMarkdownFiles(dir) {
|
|
2135
|
+
let count = 0;
|
|
2136
|
+
let oldestMtimeMs = null;
|
|
2137
|
+
if (!existsSync4(dir)) {
|
|
2138
|
+
return { count, oldestMtimeMs };
|
|
2139
|
+
}
|
|
2140
|
+
let entries;
|
|
2141
|
+
try {
|
|
2142
|
+
entries = readdirSync3(dir, { withFileTypes: true });
|
|
2143
|
+
} catch {
|
|
2144
|
+
return { count, oldestMtimeMs };
|
|
2145
|
+
}
|
|
2146
|
+
for (const entry of entries) {
|
|
2147
|
+
const fullPath = join5(dir, entry.name);
|
|
2148
|
+
if (entry.isDirectory()) {
|
|
2149
|
+
const nested = countMarkdownFiles(fullPath);
|
|
2150
|
+
count += nested.count;
|
|
2151
|
+
if (nested.oldestMtimeMs !== null && (oldestMtimeMs === null || nested.oldestMtimeMs < oldestMtimeMs)) {
|
|
2152
|
+
oldestMtimeMs = nested.oldestMtimeMs;
|
|
2153
|
+
}
|
|
2154
|
+
continue;
|
|
2155
|
+
}
|
|
2156
|
+
if (!entry.isFile() || !entry.name.endsWith(".md")) {
|
|
2157
|
+
continue;
|
|
2158
|
+
}
|
|
2159
|
+
let mtimeMs;
|
|
2160
|
+
try {
|
|
2161
|
+
mtimeMs = statSync(fullPath).mtimeMs;
|
|
2162
|
+
} catch {
|
|
2163
|
+
continue;
|
|
2164
|
+
}
|
|
2165
|
+
count += 1;
|
|
2166
|
+
if (oldestMtimeMs === null || mtimeMs < oldestMtimeMs) {
|
|
2167
|
+
oldestMtimeMs = mtimeMs;
|
|
2168
|
+
}
|
|
2169
|
+
}
|
|
2170
|
+
return { count, oldestMtimeMs };
|
|
2171
|
+
}
|
|
2172
|
+
function collectKnowledgeStats(globalRoot, resolveInput, readSet) {
|
|
2173
|
+
let pendingCount = 0;
|
|
2174
|
+
let oldestPendingMtimeMs = null;
|
|
2175
|
+
let canonicalCount = 0;
|
|
2176
|
+
const storeDirs = [];
|
|
2177
|
+
for (const store of readSet.stores) {
|
|
2178
|
+
const mounted = resolveInput.mountedStores.find((entry) => entry.store_uuid === store.store_uuid) ?? {
|
|
2179
|
+
store_uuid: store.store_uuid
|
|
2180
|
+
};
|
|
2181
|
+
const storeDir = join5(globalRoot, storeRelativePathForMount(mounted));
|
|
2182
|
+
storeDirs.push(storeDir);
|
|
2183
|
+
for (const type of STORE_KNOWLEDGE_TYPE_DIRS) {
|
|
2184
|
+
const canonical = countMarkdownFiles(join5(storeDir, STORE_LAYOUT.knowledgeDir, type));
|
|
2185
|
+
canonicalCount += canonical.count;
|
|
2186
|
+
}
|
|
2187
|
+
const pending = countMarkdownFiles(join5(storeDir, STORE_LAYOUT.knowledgeDir, "pending"));
|
|
2188
|
+
pendingCount += pending.count;
|
|
2189
|
+
if (pending.oldestMtimeMs !== null && (oldestPendingMtimeMs === null || pending.oldestMtimeMs < oldestPendingMtimeMs)) {
|
|
2190
|
+
oldestPendingMtimeMs = pending.oldestMtimeMs;
|
|
2191
|
+
}
|
|
2192
|
+
}
|
|
2193
|
+
return {
|
|
2194
|
+
stats: {
|
|
2195
|
+
pending_count: pendingCount,
|
|
2196
|
+
canonical_count: canonicalCount,
|
|
2197
|
+
oldest_pending_mtime_ms: oldestPendingMtimeMs
|
|
2198
|
+
},
|
|
2199
|
+
storeDirs
|
|
2200
|
+
};
|
|
2201
|
+
}
|
|
2202
|
+
function atomicWriteJsonSync(path, value) {
|
|
2203
|
+
const tmpPath = `${path}.${process.pid}.${Date.now()}.tmp`;
|
|
2204
|
+
try {
|
|
2205
|
+
writeFileSync3(tmpPath, `${JSON.stringify(value, null, 2)}
|
|
2206
|
+
`, "utf8");
|
|
2207
|
+
renameSync(tmpPath, path);
|
|
2208
|
+
} catch (error) {
|
|
2209
|
+
try {
|
|
2210
|
+
unlinkSync(tmpPath);
|
|
2211
|
+
} catch {
|
|
2212
|
+
}
|
|
2213
|
+
throw error;
|
|
2214
|
+
}
|
|
2215
|
+
}
|
|
1869
2216
|
function writeBindingsSnapshot(options) {
|
|
1870
2217
|
const resolver = createStoreResolver();
|
|
1871
2218
|
const read_set = resolver.resolveReadSet(options.resolveInput);
|
|
1872
2219
|
const { target } = resolver.resolveWriteTarget(options.resolveInput, options.writeScope);
|
|
2220
|
+
const { stats, storeDirs } = collectKnowledgeStats(options.globalRoot, options.resolveInput, read_set);
|
|
1873
2221
|
const snapshot = resolvedBindingsSnapshotSchema.parse({
|
|
1874
2222
|
version: 1,
|
|
1875
2223
|
project_id: options.projectId,
|
|
2224
|
+
workspace_binding_id: options.workspaceBindingId ?? options.projectId,
|
|
1876
2225
|
generated_at: options.now,
|
|
1877
2226
|
read_set,
|
|
1878
|
-
write_target: target
|
|
2227
|
+
write_target: target,
|
|
2228
|
+
knowledge_stats: stats,
|
|
2229
|
+
knowledge_store_dirs: storeDirs
|
|
1879
2230
|
});
|
|
1880
|
-
const path = bindingsSnapshotPath(options.globalRoot,
|
|
1881
|
-
|
|
1882
|
-
|
|
1883
|
-
`, "utf8");
|
|
2231
|
+
const path = bindingsSnapshotPath(options.globalRoot, snapshot.workspace_binding_id);
|
|
2232
|
+
mkdirSync2(join5(path, ".."), { recursive: true });
|
|
2233
|
+
atomicWriteJsonSync(path, snapshot);
|
|
1884
2234
|
return snapshot;
|
|
1885
2235
|
}
|
|
1886
2236
|
function readBindingsSnapshot(globalRoot, projectId) {
|
|
1887
2237
|
const path = bindingsSnapshotPath(globalRoot, projectId);
|
|
1888
|
-
if (!
|
|
2238
|
+
if (!existsSync4(path)) {
|
|
1889
2239
|
return null;
|
|
1890
2240
|
}
|
|
1891
2241
|
try {
|
|
@@ -1898,7 +2248,9 @@ function readBindingsSnapshot(globalRoot, projectId) {
|
|
|
1898
2248
|
|
|
1899
2249
|
// src/store/store-lifecycle.ts
|
|
1900
2250
|
function findMountedStore(config, aliasOrUuid) {
|
|
1901
|
-
return config.stores.find(
|
|
2251
|
+
return config.stores.find(
|
|
2252
|
+
(s) => s.alias === aliasOrUuid || s.store_uuid === aliasOrUuid || s.mount_name === aliasOrUuid
|
|
2253
|
+
);
|
|
1902
2254
|
}
|
|
1903
2255
|
function addMountedStore(config, store) {
|
|
1904
2256
|
const aliasClash = config.stores.find(
|
|
@@ -1909,12 +2261,32 @@ function addMountedStore(config, store) {
|
|
|
1909
2261
|
`alias '${store.alias}' already mounts store ${aliasClash.store_uuid}; choose another alias`
|
|
1910
2262
|
);
|
|
1911
2263
|
}
|
|
2264
|
+
const mountNameClash = store.mount_name === void 0 ? void 0 : config.stores.find(
|
|
2265
|
+
(s) => s.mount_name === store.mount_name && s.store_uuid !== store.store_uuid
|
|
2266
|
+
);
|
|
2267
|
+
if (mountNameClash !== void 0) {
|
|
2268
|
+
throw new Error(
|
|
2269
|
+
`mount_name '${store.mount_name}' already maps to store ${mountNameClash.store_uuid}; choose another mount_name`
|
|
2270
|
+
);
|
|
2271
|
+
}
|
|
1912
2272
|
const sanitized = store.remote === void 0 ? store : { ...store, remote: scrubRemoteUrl(store.remote) };
|
|
1913
2273
|
store = sanitized;
|
|
1914
2274
|
const existing = config.stores.find((s) => s.store_uuid === store.store_uuid);
|
|
1915
2275
|
const stores = existing === void 0 ? [...config.stores, store] : config.stores.map((s) => s.store_uuid === store.store_uuid ? store : s);
|
|
1916
2276
|
return { ...config, stores };
|
|
1917
2277
|
}
|
|
2278
|
+
function disambiguateAlias(existingAliases, desired) {
|
|
2279
|
+
const taken = new Set(existingAliases);
|
|
2280
|
+
if (!taken.has(desired)) {
|
|
2281
|
+
return desired;
|
|
2282
|
+
}
|
|
2283
|
+
for (let n = 2; ; n += 1) {
|
|
2284
|
+
const candidate = `${desired}-${n}`;
|
|
2285
|
+
if (!taken.has(candidate)) {
|
|
2286
|
+
return candidate;
|
|
2287
|
+
}
|
|
2288
|
+
}
|
|
2289
|
+
}
|
|
1918
2290
|
function detachMountedStore(config, alias) {
|
|
1919
2291
|
const detached = config.stores.find((s) => s.alias === alias) ?? null;
|
|
1920
2292
|
if (detached === null) {
|
|
@@ -1943,155 +2315,155 @@ function explainStore(config, alias) {
|
|
|
1943
2315
|
}
|
|
1944
2316
|
|
|
1945
2317
|
// src/schemas/forensic-report.ts
|
|
1946
|
-
import { z as
|
|
1947
|
-
var forensicCodeSampleSchema =
|
|
1948
|
-
path:
|
|
1949
|
-
lines:
|
|
1950
|
-
snippet:
|
|
1951
|
-
pattern_hint:
|
|
1952
|
-
});
|
|
1953
|
-
var forensicEvidenceAnchorSchema =
|
|
1954
|
-
file:
|
|
1955
|
-
line:
|
|
1956
|
-
snippet:
|
|
1957
|
-
});
|
|
1958
|
-
var forensicAssertionCoverageSchema =
|
|
1959
|
-
ratio:
|
|
1960
|
-
total:
|
|
1961
|
-
matched:
|
|
1962
|
-
co_occurring_patterns:
|
|
1963
|
-
});
|
|
1964
|
-
var forensicAssertionSchema =
|
|
1965
|
-
type:
|
|
1966
|
-
statement:
|
|
1967
|
-
confidence:
|
|
1968
|
-
evidence:
|
|
2318
|
+
import { z as z13 } from "zod";
|
|
2319
|
+
var forensicCodeSampleSchema = z13.object({
|
|
2320
|
+
path: z13.string(),
|
|
2321
|
+
lines: z13.string(),
|
|
2322
|
+
snippet: z13.string(),
|
|
2323
|
+
pattern_hint: z13.string()
|
|
2324
|
+
});
|
|
2325
|
+
var forensicEvidenceAnchorSchema = z13.object({
|
|
2326
|
+
file: z13.string(),
|
|
2327
|
+
line: z13.string(),
|
|
2328
|
+
snippet: z13.string()
|
|
2329
|
+
});
|
|
2330
|
+
var forensicAssertionCoverageSchema = z13.object({
|
|
2331
|
+
ratio: z13.number().min(0).max(1),
|
|
2332
|
+
total: z13.number().int().nonnegative(),
|
|
2333
|
+
matched: z13.number().int().nonnegative(),
|
|
2334
|
+
co_occurring_patterns: z13.array(z13.string())
|
|
2335
|
+
});
|
|
2336
|
+
var forensicAssertionSchema = z13.object({
|
|
2337
|
+
type: z13.enum(["framework", "pattern", "invariant", "domain"]),
|
|
2338
|
+
statement: z13.string(),
|
|
2339
|
+
confidence: z13.enum(["HIGH", "MEDIUM", "LOW"]),
|
|
2340
|
+
evidence: z13.array(forensicEvidenceAnchorSchema),
|
|
1969
2341
|
coverage: forensicAssertionCoverageSchema,
|
|
1970
|
-
proposed_rule:
|
|
1971
|
-
alternatives:
|
|
1972
|
-
});
|
|
1973
|
-
var forensicTopologySchema =
|
|
1974
|
-
total_files:
|
|
1975
|
-
by_ext:
|
|
1976
|
-
key_dirs:
|
|
1977
|
-
max_depth:
|
|
1978
|
-
});
|
|
1979
|
-
var forensicEntryPointSchema =
|
|
1980
|
-
path:
|
|
1981
|
-
reason:
|
|
1982
|
-
size_bytes:
|
|
1983
|
-
});
|
|
1984
|
-
var forensicFrameworkSchema =
|
|
1985
|
-
kind:
|
|
1986
|
-
version:
|
|
1987
|
-
subkind:
|
|
1988
|
-
evidence:
|
|
1989
|
-
});
|
|
1990
|
-
var forensicReadmeSchema =
|
|
1991
|
-
quality:
|
|
1992
|
-
line_count:
|
|
1993
|
-
has_contributing:
|
|
1994
|
-
});
|
|
1995
|
-
var candidateFileEntrySchema =
|
|
1996
|
-
path:
|
|
1997
|
-
family:
|
|
1998
|
-
rationale:
|
|
1999
|
-
});
|
|
2000
|
-
var forensicSamplingBudgetSchema =
|
|
2001
|
-
max_files:
|
|
2002
|
-
max_lines_per_file:
|
|
2003
|
-
});
|
|
2004
|
-
var forensicReportSchema =
|
|
2005
|
-
version:
|
|
2006
|
-
generated_at:
|
|
2007
|
-
generated_by:
|
|
2008
|
-
target:
|
|
2009
|
-
project_name:
|
|
2342
|
+
proposed_rule: z13.string().optional(),
|
|
2343
|
+
alternatives: z13.array(z13.string()).optional()
|
|
2344
|
+
});
|
|
2345
|
+
var forensicTopologySchema = z13.object({
|
|
2346
|
+
total_files: z13.number().int().nonnegative(),
|
|
2347
|
+
by_ext: z13.record(z13.number().int().nonnegative()),
|
|
2348
|
+
key_dirs: z13.array(z13.string()),
|
|
2349
|
+
max_depth: z13.number().int().nonnegative()
|
|
2350
|
+
});
|
|
2351
|
+
var forensicEntryPointSchema = z13.object({
|
|
2352
|
+
path: z13.string(),
|
|
2353
|
+
reason: z13.string(),
|
|
2354
|
+
size_bytes: z13.number().int().nonnegative().optional()
|
|
2355
|
+
});
|
|
2356
|
+
var forensicFrameworkSchema = z13.object({
|
|
2357
|
+
kind: z13.string(),
|
|
2358
|
+
version: z13.string(),
|
|
2359
|
+
subkind: z13.string(),
|
|
2360
|
+
evidence: z13.array(z13.string())
|
|
2361
|
+
});
|
|
2362
|
+
var forensicReadmeSchema = z13.object({
|
|
2363
|
+
quality: z13.enum(["missing", "stub", "ok"]),
|
|
2364
|
+
line_count: z13.number().int().nonnegative(),
|
|
2365
|
+
has_contributing: z13.boolean()
|
|
2366
|
+
});
|
|
2367
|
+
var candidateFileEntrySchema = z13.object({
|
|
2368
|
+
path: z13.string(),
|
|
2369
|
+
family: z13.enum(["entry", "component", "config", "test", "domain"]),
|
|
2370
|
+
rationale: z13.string()
|
|
2371
|
+
});
|
|
2372
|
+
var forensicSamplingBudgetSchema = z13.object({
|
|
2373
|
+
max_files: z13.literal(15),
|
|
2374
|
+
max_lines_per_file: z13.literal(100)
|
|
2375
|
+
});
|
|
2376
|
+
var forensicReportSchema = z13.object({
|
|
2377
|
+
version: z13.string(),
|
|
2378
|
+
generated_at: z13.string(),
|
|
2379
|
+
generated_by: z13.string(),
|
|
2380
|
+
target: z13.string(),
|
|
2381
|
+
project_name: z13.string(),
|
|
2010
2382
|
framework: forensicFrameworkSchema,
|
|
2011
2383
|
topology: forensicTopologySchema,
|
|
2012
|
-
entry_points:
|
|
2013
|
-
code_samples:
|
|
2014
|
-
assertions:
|
|
2015
|
-
candidate_files:
|
|
2384
|
+
entry_points: z13.array(forensicEntryPointSchema),
|
|
2385
|
+
code_samples: z13.array(forensicCodeSampleSchema),
|
|
2386
|
+
assertions: z13.array(forensicAssertionSchema),
|
|
2387
|
+
candidate_files: z13.array(candidateFileEntrySchema),
|
|
2016
2388
|
sampling_budget: forensicSamplingBudgetSchema,
|
|
2017
2389
|
readme: forensicReadmeSchema,
|
|
2018
|
-
recommendations_for_skill:
|
|
2390
|
+
recommendations_for_skill: z13.array(z13.string()).optional()
|
|
2019
2391
|
});
|
|
2020
2392
|
|
|
2021
2393
|
// src/schemas/init-context.ts
|
|
2022
|
-
import { z as
|
|
2023
|
-
var initContextFrameworkSchema =
|
|
2024
|
-
kind:
|
|
2025
|
-
version:
|
|
2026
|
-
subkind:
|
|
2027
|
-
});
|
|
2028
|
-
var initContextInvariantConfidenceSnapshotSchema =
|
|
2029
|
-
confidence:
|
|
2030
|
-
evidence_refs:
|
|
2031
|
-
});
|
|
2032
|
-
var initContextSourceEvidenceSchema =
|
|
2033
|
-
file:
|
|
2034
|
-
lines:
|
|
2035
|
-
});
|
|
2036
|
-
var initContextInvariantSchema =
|
|
2037
|
-
type:
|
|
2038
|
-
rule:
|
|
2039
|
-
rationale:
|
|
2394
|
+
import { z as z14 } from "zod";
|
|
2395
|
+
var initContextFrameworkSchema = z14.object({
|
|
2396
|
+
kind: z14.string(),
|
|
2397
|
+
version: z14.string(),
|
|
2398
|
+
subkind: z14.string()
|
|
2399
|
+
});
|
|
2400
|
+
var initContextInvariantConfidenceSnapshotSchema = z14.object({
|
|
2401
|
+
confidence: z14.enum(["HIGH", "MEDIUM", "LOW"]),
|
|
2402
|
+
evidence_refs: z14.array(z14.string())
|
|
2403
|
+
});
|
|
2404
|
+
var initContextSourceEvidenceSchema = z14.object({
|
|
2405
|
+
file: z14.string(),
|
|
2406
|
+
lines: z14.string()
|
|
2407
|
+
});
|
|
2408
|
+
var initContextInvariantSchema = z14.object({
|
|
2409
|
+
type: z14.enum(["ban", "require", "protect"]),
|
|
2410
|
+
rule: z14.string(),
|
|
2411
|
+
rationale: z14.string().optional(),
|
|
2040
2412
|
confidence_snapshot: initContextInvariantConfidenceSnapshotSchema.optional(),
|
|
2041
|
-
source_evidence:
|
|
2042
|
-
});
|
|
2043
|
-
var initContextDomainGroupSchema =
|
|
2044
|
-
name:
|
|
2045
|
-
paths:
|
|
2046
|
-
summary:
|
|
2047
|
-
topology_type:
|
|
2048
|
-
target_path:
|
|
2049
|
-
});
|
|
2050
|
-
var initContextInterviewTrailEntrySchema =
|
|
2051
|
-
phase:
|
|
2052
|
-
question:
|
|
2053
|
-
answer:
|
|
2054
|
-
presentation:
|
|
2055
|
-
user_corrections:
|
|
2056
|
-
});
|
|
2057
|
-
var initContextSchema =
|
|
2413
|
+
source_evidence: z14.array(initContextSourceEvidenceSchema).optional()
|
|
2414
|
+
});
|
|
2415
|
+
var initContextDomainGroupSchema = z14.object({
|
|
2416
|
+
name: z14.string(),
|
|
2417
|
+
paths: z14.array(z14.string()),
|
|
2418
|
+
summary: z14.string().optional(),
|
|
2419
|
+
topology_type: z14.enum(["mirror", "cross-cutting"]).optional(),
|
|
2420
|
+
target_path: z14.string().optional()
|
|
2421
|
+
});
|
|
2422
|
+
var initContextInterviewTrailEntrySchema = z14.object({
|
|
2423
|
+
phase: z14.string(),
|
|
2424
|
+
question: z14.string(),
|
|
2425
|
+
answer: z14.string(),
|
|
2426
|
+
presentation: z14.string().optional(),
|
|
2427
|
+
user_corrections: z14.array(z14.string()).optional()
|
|
2428
|
+
});
|
|
2429
|
+
var initContextSchema = z14.object({
|
|
2058
2430
|
framework: initContextFrameworkSchema,
|
|
2059
|
-
architecture_patterns:
|
|
2060
|
-
invariants:
|
|
2061
|
-
domain_groups:
|
|
2062
|
-
interview_trail:
|
|
2063
|
-
forensic_ref:
|
|
2431
|
+
architecture_patterns: z14.array(z14.string()),
|
|
2432
|
+
invariants: z14.array(initContextInvariantSchema),
|
|
2433
|
+
domain_groups: z14.array(initContextDomainGroupSchema),
|
|
2434
|
+
interview_trail: z14.array(initContextInterviewTrailEntrySchema),
|
|
2435
|
+
forensic_ref: z14.string()
|
|
2064
2436
|
});
|
|
2065
2437
|
|
|
2066
2438
|
// src/schemas/events.ts
|
|
2067
|
-
import { z as
|
|
2068
|
-
var metaUpdatedEventSchema =
|
|
2069
|
-
type:
|
|
2439
|
+
import { z as z15 } from "zod";
|
|
2440
|
+
var metaUpdatedEventSchema = z15.object({
|
|
2441
|
+
type: z15.literal("meta:updated"),
|
|
2070
2442
|
payload: agentsMetaSchema
|
|
2071
2443
|
});
|
|
2072
|
-
var lockDriftEventSchema =
|
|
2073
|
-
type:
|
|
2074
|
-
payload:
|
|
2075
|
-
locked:
|
|
2076
|
-
drifted:
|
|
2444
|
+
var lockDriftEventSchema = z15.object({
|
|
2445
|
+
type: z15.literal("lock:drift"),
|
|
2446
|
+
payload: z15.object({
|
|
2447
|
+
locked: z15.array(humanLockEntrySchema),
|
|
2448
|
+
drifted: z15.array(humanLockEntrySchema)
|
|
2077
2449
|
})
|
|
2078
2450
|
});
|
|
2079
|
-
var lockApprovedEventSchema =
|
|
2080
|
-
type:
|
|
2081
|
-
payload:
|
|
2082
|
-
locked:
|
|
2083
|
-
approved:
|
|
2451
|
+
var lockApprovedEventSchema = z15.object({
|
|
2452
|
+
type: z15.literal("lock:approved"),
|
|
2453
|
+
payload: z15.object({
|
|
2454
|
+
locked: z15.array(humanLockEntrySchema),
|
|
2455
|
+
approved: z15.array(humanLockEntrySchema)
|
|
2084
2456
|
})
|
|
2085
2457
|
});
|
|
2086
|
-
var ledgerAppendedEventSchema =
|
|
2087
|
-
type:
|
|
2458
|
+
var ledgerAppendedEventSchema = z15.object({
|
|
2459
|
+
type: z15.literal("ledger:appended"),
|
|
2088
2460
|
payload: ledgerEntrySchema
|
|
2089
2461
|
});
|
|
2090
|
-
var driftDetectedEventSchema =
|
|
2091
|
-
type:
|
|
2462
|
+
var driftDetectedEventSchema = z15.object({
|
|
2463
|
+
type: z15.literal("drift:detected"),
|
|
2092
2464
|
payload: forensicReportSchema
|
|
2093
2465
|
});
|
|
2094
|
-
var fabricEventSchema =
|
|
2466
|
+
var fabricEventSchema = z15.discriminatedUnion("type", [
|
|
2095
2467
|
metaUpdatedEventSchema,
|
|
2096
2468
|
lockDriftEventSchema,
|
|
2097
2469
|
lockApprovedEventSchema,
|
|
@@ -2100,7 +2472,7 @@ var fabricEventSchema = z17.discriminatedUnion("type", [
|
|
|
2100
2472
|
]);
|
|
2101
2473
|
|
|
2102
2474
|
// src/schemas/event-ledger.ts
|
|
2103
|
-
import { z as
|
|
2475
|
+
import { z as z16 } from "zod";
|
|
2104
2476
|
|
|
2105
2477
|
// src/cite-line-parser.ts
|
|
2106
2478
|
var ID_RE = /^K[TP]-[A-Z]+-\d+$/;
|
|
@@ -2114,17 +2486,12 @@ function splitStorePrefix(token) {
|
|
|
2114
2486
|
return colon === -1 ? { store: null, id: token } : { store: token.slice(0, colon), id: token.slice(colon + 1) };
|
|
2115
2487
|
}
|
|
2116
2488
|
var CHAINED_FROM_ID_RE = /chained-from\s+(K[TP]-[A-Z]+-\d+)/i;
|
|
2117
|
-
var LEGACY_CITE_TAG_REMAP = {
|
|
2118
|
-
planned: "applied",
|
|
2119
|
-
recalled: "applied",
|
|
2120
|
-
"chained-from": "applied"
|
|
2121
|
-
};
|
|
2122
2489
|
function normalizeCiteTag(rawTag) {
|
|
2123
2490
|
const head = rawTag.trim().split(/[\s:]+/)[0].toLowerCase();
|
|
2124
2491
|
if (head === "applied" || head === "dismissed" || head === "none") {
|
|
2125
2492
|
return head;
|
|
2126
2493
|
}
|
|
2127
|
-
return
|
|
2494
|
+
return "none";
|
|
2128
2495
|
}
|
|
2129
2496
|
function parseTag(rawTag) {
|
|
2130
2497
|
if (!rawTag) return "none";
|
|
@@ -2212,146 +2579,131 @@ function parseCiteLine(raw) {
|
|
|
2212
2579
|
}
|
|
2213
2580
|
|
|
2214
2581
|
// src/schemas/event-ledger.ts
|
|
2215
|
-
var citeTagSchema =
|
|
2582
|
+
var citeTagSchema = z16.preprocess(
|
|
2216
2583
|
(value) => typeof value === "string" ? normalizeCiteTag(value) : value,
|
|
2217
|
-
|
|
2584
|
+
z16.enum(["applied", "dismissed", "none"])
|
|
2218
2585
|
);
|
|
2219
2586
|
var eventLedgerEnvelopeSchema = {
|
|
2220
|
-
kind:
|
|
2221
|
-
id:
|
|
2222
|
-
ts:
|
|
2223
|
-
schema_version:
|
|
2224
|
-
correlation_id:
|
|
2225
|
-
session_id:
|
|
2587
|
+
kind: z16.literal("fabric-event"),
|
|
2588
|
+
id: z16.string(),
|
|
2589
|
+
ts: z16.number().int().nonnegative(),
|
|
2590
|
+
schema_version: z16.literal(1),
|
|
2591
|
+
correlation_id: z16.string().optional(),
|
|
2592
|
+
session_id: z16.string().optional()
|
|
2226
2593
|
};
|
|
2227
|
-
var stringRecordSchema =
|
|
2228
|
-
var knowledgeContextPlannedEventSchema =
|
|
2594
|
+
var stringRecordSchema = z16.record(z16.string());
|
|
2595
|
+
var knowledgeContextPlannedEventSchema = z16.object({
|
|
2229
2596
|
...eventLedgerEnvelopeSchema,
|
|
2230
|
-
event_type:
|
|
2231
|
-
target_paths:
|
|
2232
|
-
required_stable_ids:
|
|
2233
|
-
ai_selectable_stable_ids:
|
|
2234
|
-
final_stable_ids:
|
|
2235
|
-
selection_token:
|
|
2236
|
-
client_hash:
|
|
2237
|
-
intent:
|
|
2238
|
-
known_tech:
|
|
2239
|
-
diagnostics:
|
|
2240
|
-
});
|
|
2241
|
-
var knowledgeSelectionEventSchema =
|
|
2597
|
+
event_type: z16.literal("knowledge_context_planned"),
|
|
2598
|
+
target_paths: z16.array(z16.string()),
|
|
2599
|
+
required_stable_ids: z16.array(z16.string()),
|
|
2600
|
+
ai_selectable_stable_ids: z16.array(z16.string()),
|
|
2601
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2602
|
+
selection_token: z16.string().optional(),
|
|
2603
|
+
client_hash: z16.string().optional(),
|
|
2604
|
+
intent: z16.string().optional(),
|
|
2605
|
+
known_tech: z16.array(z16.string()).optional(),
|
|
2606
|
+
diagnostics: z16.array(z16.unknown()).optional()
|
|
2607
|
+
});
|
|
2608
|
+
var knowledgeSelectionEventSchema = z16.object({
|
|
2242
2609
|
...eventLedgerEnvelopeSchema,
|
|
2243
|
-
event_type:
|
|
2244
|
-
selection_token:
|
|
2245
|
-
target_paths:
|
|
2246
|
-
required_stable_ids:
|
|
2247
|
-
ai_selectable_stable_ids:
|
|
2248
|
-
ai_selected_stable_ids:
|
|
2249
|
-
final_stable_ids:
|
|
2610
|
+
event_type: z16.literal("knowledge_selection"),
|
|
2611
|
+
selection_token: z16.string(),
|
|
2612
|
+
target_paths: z16.array(z16.string()),
|
|
2613
|
+
required_stable_ids: z16.array(z16.string()),
|
|
2614
|
+
ai_selectable_stable_ids: z16.array(z16.string()),
|
|
2615
|
+
ai_selected_stable_ids: z16.array(z16.string()),
|
|
2616
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2250
2617
|
ai_selection_reasons: stringRecordSchema,
|
|
2251
|
-
rejected_stable_ids:
|
|
2252
|
-
ignored_stable_ids:
|
|
2618
|
+
rejected_stable_ids: z16.array(z16.string()),
|
|
2619
|
+
ignored_stable_ids: z16.array(z16.string())
|
|
2253
2620
|
});
|
|
2254
|
-
var knowledgeSectionsFetchedEventSchema =
|
|
2621
|
+
var knowledgeSectionsFetchedEventSchema = z16.object({
|
|
2255
2622
|
...eventLedgerEnvelopeSchema,
|
|
2256
|
-
event_type:
|
|
2257
|
-
selection_token:
|
|
2258
|
-
target_paths:
|
|
2259
|
-
requested_sections:
|
|
2260
|
-
final_stable_ids:
|
|
2261
|
-
ai_selected_stable_ids:
|
|
2262
|
-
diagnostics:
|
|
2263
|
-
});
|
|
2264
|
-
var editIntentCheckedEventSchema =
|
|
2623
|
+
event_type: z16.literal("knowledge_sections_fetched"),
|
|
2624
|
+
selection_token: z16.string(),
|
|
2625
|
+
target_paths: z16.array(z16.string()).optional(),
|
|
2626
|
+
requested_sections: z16.array(z16.string()),
|
|
2627
|
+
final_stable_ids: z16.array(z16.string()),
|
|
2628
|
+
ai_selected_stable_ids: z16.array(z16.string()),
|
|
2629
|
+
diagnostics: z16.array(z16.unknown()).optional()
|
|
2630
|
+
});
|
|
2631
|
+
var editIntentCheckedEventSchema = z16.object({
|
|
2265
2632
|
...eventLedgerEnvelopeSchema,
|
|
2266
|
-
event_type:
|
|
2267
|
-
path:
|
|
2268
|
-
compliant:
|
|
2269
|
-
intent:
|
|
2270
|
-
ledger_entry_id:
|
|
2633
|
+
event_type: z16.literal("edit_intent_checked"),
|
|
2634
|
+
path: z16.string(),
|
|
2635
|
+
compliant: z16.boolean(),
|
|
2636
|
+
intent: z16.string(),
|
|
2637
|
+
ledger_entry_id: z16.string(),
|
|
2271
2638
|
// rc.35 TASK-07 (P0-2): add "hook" — emitted by the PreToolUse narrow hook
|
|
2272
2639
|
// for every Edit/Write/MultiEdit fire so cite-coverage doctor metrics see
|
|
2273
2640
|
// actual edit signals (previously editsTouched was permanently 0 because
|
|
2274
2641
|
// no production caller of appendLedgerEntry existed).
|
|
2275
|
-
ledger_source:
|
|
2276
|
-
commit_sha:
|
|
2277
|
-
parent_sha:
|
|
2278
|
-
parent_ledger_entry_id:
|
|
2279
|
-
diff_stat:
|
|
2280
|
-
annotation:
|
|
2281
|
-
matched_rule_context_ts:
|
|
2282
|
-
window_ms:
|
|
2283
|
-
});
|
|
2284
|
-
var knowledgeDriftDetectedEventSchema =
|
|
2642
|
+
ledger_source: z16.enum(["ai", "human", "hook"]).optional(),
|
|
2643
|
+
commit_sha: z16.string().optional(),
|
|
2644
|
+
parent_sha: z16.string().optional(),
|
|
2645
|
+
parent_ledger_entry_id: z16.string().optional(),
|
|
2646
|
+
diff_stat: z16.string().optional(),
|
|
2647
|
+
annotation: z16.string().optional(),
|
|
2648
|
+
matched_rule_context_ts: z16.number().int().nonnegative().nullable(),
|
|
2649
|
+
window_ms: z16.number().int().nonnegative()
|
|
2650
|
+
});
|
|
2651
|
+
var knowledgeDriftDetectedEventSchema = z16.object({
|
|
2285
2652
|
...eventLedgerEnvelopeSchema,
|
|
2286
|
-
event_type:
|
|
2287
|
-
revision:
|
|
2288
|
-
drifted_stable_ids:
|
|
2289
|
-
missing_files:
|
|
2290
|
-
stale_files:
|
|
2291
|
-
details:
|
|
2292
|
-
|
|
2293
|
-
file:
|
|
2294
|
-
stable_id:
|
|
2295
|
-
expected_hash:
|
|
2296
|
-
actual_hash:
|
|
2653
|
+
event_type: z16.literal("knowledge_drift_detected"),
|
|
2654
|
+
revision: z16.string().optional(),
|
|
2655
|
+
drifted_stable_ids: z16.array(z16.string()),
|
|
2656
|
+
missing_files: z16.array(z16.string()),
|
|
2657
|
+
stale_files: z16.array(z16.string()),
|
|
2658
|
+
details: z16.array(
|
|
2659
|
+
z16.object({
|
|
2660
|
+
file: z16.string(),
|
|
2661
|
+
stable_id: z16.string(),
|
|
2662
|
+
expected_hash: z16.string(),
|
|
2663
|
+
actual_hash: z16.string().nullable()
|
|
2297
2664
|
})
|
|
2298
2665
|
).optional()
|
|
2299
2666
|
});
|
|
2300
|
-
var mcpEventLedgerEventSchema =
|
|
2667
|
+
var mcpEventLedgerEventSchema = z16.object({
|
|
2301
2668
|
...eventLedgerEnvelopeSchema,
|
|
2302
|
-
event_type:
|
|
2303
|
-
mcp_event_id:
|
|
2304
|
-
stream_id:
|
|
2305
|
-
message:
|
|
2669
|
+
event_type: z16.literal("mcp_event"),
|
|
2670
|
+
mcp_event_id: z16.string(),
|
|
2671
|
+
stream_id: z16.string(),
|
|
2672
|
+
message: z16.unknown()
|
|
2306
2673
|
});
|
|
2307
|
-
var reapplyCompletedEventSchema =
|
|
2674
|
+
var reapplyCompletedEventSchema = z16.object({
|
|
2308
2675
|
...eventLedgerEnvelopeSchema,
|
|
2309
|
-
event_type:
|
|
2310
|
-
preserved_ledger:
|
|
2311
|
-
preserved_meta:
|
|
2312
|
-
rules_count:
|
|
2676
|
+
event_type: z16.literal("reapply_completed"),
|
|
2677
|
+
preserved_ledger: z16.boolean(),
|
|
2678
|
+
preserved_meta: z16.boolean(),
|
|
2679
|
+
rules_count: z16.number().int().nonnegative()
|
|
2313
2680
|
});
|
|
2314
|
-
var installDiffAppliedEventSchema =
|
|
2681
|
+
var installDiffAppliedEventSchema = z16.object({
|
|
2315
2682
|
...eventLedgerEnvelopeSchema,
|
|
2316
|
-
event_type:
|
|
2317
|
-
applied:
|
|
2318
|
-
canonical:
|
|
2319
|
-
drifted:
|
|
2683
|
+
event_type: z16.literal("install_diff_applied"),
|
|
2684
|
+
applied: z16.array(z16.string()),
|
|
2685
|
+
canonical: z16.array(z16.string()),
|
|
2686
|
+
drifted: z16.array(z16.string())
|
|
2320
2687
|
});
|
|
2321
|
-
var eventLedgerTruncatedEventSchema =
|
|
2688
|
+
var eventLedgerTruncatedEventSchema = z16.object({
|
|
2322
2689
|
...eventLedgerEnvelopeSchema,
|
|
2323
|
-
event_type:
|
|
2324
|
-
byte_offset:
|
|
2325
|
-
byte_length:
|
|
2326
|
-
corrupted_path:
|
|
2690
|
+
event_type: z16.literal("event_ledger_truncated"),
|
|
2691
|
+
byte_offset: z16.number().int().nonnegative(),
|
|
2692
|
+
byte_length: z16.number().int().nonnegative(),
|
|
2693
|
+
corrupted_path: z16.string()
|
|
2327
2694
|
});
|
|
2328
|
-
var
|
|
2695
|
+
var metaReconciledOnStartupEventSchema = z16.object({
|
|
2329
2696
|
...eventLedgerEnvelopeSchema,
|
|
2330
|
-
event_type:
|
|
2331
|
-
|
|
2332
|
-
|
|
2697
|
+
event_type: z16.literal("meta_reconciled_on_startup"),
|
|
2698
|
+
reconciled_files: z16.array(z16.string()),
|
|
2699
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2700
|
+
source: z16.literal("reconcileKnowledge")
|
|
2333
2701
|
});
|
|
2334
|
-
var
|
|
2702
|
+
var metaReconciledEventSchema = z16.object({
|
|
2335
2703
|
...eventLedgerEnvelopeSchema,
|
|
2336
|
-
event_type:
|
|
2337
|
-
|
|
2338
|
-
|
|
2339
|
-
legacy_marker: z18.literal("fabric:knowledge-base"),
|
|
2340
|
-
new_marker: z18.literal("fabric:bootstrap"),
|
|
2341
|
-
timestamp: z18.string()
|
|
2342
|
-
});
|
|
2343
|
-
var metaReconciledOnStartupEventSchema = z18.object({
|
|
2344
|
-
...eventLedgerEnvelopeSchema,
|
|
2345
|
-
event_type: z18.literal("meta_reconciled_on_startup"),
|
|
2346
|
-
reconciled_files: z18.array(z18.string()),
|
|
2347
|
-
duration_ms: z18.number().int().nonnegative(),
|
|
2348
|
-
source: z18.literal("reconcileKnowledge")
|
|
2349
|
-
});
|
|
2350
|
-
var metaReconciledEventSchema = z18.object({
|
|
2351
|
-
...eventLedgerEnvelopeSchema,
|
|
2352
|
-
event_type: z18.literal("meta_reconciled"),
|
|
2353
|
-
reconciled_files: z18.array(z18.string()),
|
|
2354
|
-
duration_ms: z18.number().int().nonnegative(),
|
|
2704
|
+
event_type: z16.literal("meta_reconciled"),
|
|
2705
|
+
reconciled_files: z16.array(z16.string()),
|
|
2706
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2355
2707
|
// v2.0.0-rc.23 TASK-005 (a-B): added `auto-heal-description` trigger so the
|
|
2356
2708
|
// read-path plan_context handler can drive a full reconcile when it detects
|
|
2357
2709
|
// any node carrying `description === undefined` (legacy meta drift that the
|
|
@@ -2365,7 +2717,7 @@ var metaReconciledEventSchema = z18.object({
|
|
|
2365
2717
|
// v2.0.0-rc.29 TASK-005 (BUG-G1): `auto-heal-after-drift` added so
|
|
2366
2718
|
// `ensureKnowledgeFresh` hot-path can chain a paired reconcile (closing the
|
|
2367
2719
|
// drift→heal gap) when the caller opts in via `autoHealOnDrift: true`.
|
|
2368
|
-
trigger:
|
|
2720
|
+
trigger: z16.enum([
|
|
2369
2721
|
"doctor",
|
|
2370
2722
|
"manual",
|
|
2371
2723
|
"auto-heal-description",
|
|
@@ -2373,195 +2725,207 @@ var metaReconciledEventSchema = z18.object({
|
|
|
2373
2725
|
"post-approve",
|
|
2374
2726
|
"post-modify"
|
|
2375
2727
|
]),
|
|
2376
|
-
source:
|
|
2728
|
+
source: z16.literal("reconcileKnowledge"),
|
|
2377
2729
|
// v2.0.0-rc.22 TASK-014 (Scope E): set when reconcileKnowledge forced a
|
|
2378
2730
|
// writeKnowledgeMeta on revision drift alone (no per-file content drift).
|
|
2379
2731
|
// Distinguishes top-level schema/revision repair from the standard per-file
|
|
2380
2732
|
// drift path. Optional so existing emitters stay unchanged.
|
|
2381
|
-
force_write_reason:
|
|
2733
|
+
force_write_reason: z16.enum(["revision_drift"]).optional()
|
|
2382
2734
|
});
|
|
2383
|
-
var claudeSkillPathMigratedEventSchema =
|
|
2735
|
+
var claudeSkillPathMigratedEventSchema = z16.object({
|
|
2384
2736
|
...eventLedgerEnvelopeSchema,
|
|
2385
|
-
event_type:
|
|
2386
|
-
from:
|
|
2387
|
-
to:
|
|
2737
|
+
event_type: z16.literal("claude_skill_path_migrated"),
|
|
2738
|
+
from: z16.string(),
|
|
2739
|
+
to: z16.string()
|
|
2388
2740
|
});
|
|
2389
|
-
var claudeHookPathMigratedEventSchema =
|
|
2741
|
+
var claudeHookPathMigratedEventSchema = z16.object({
|
|
2390
2742
|
...eventLedgerEnvelopeSchema,
|
|
2391
|
-
event_type:
|
|
2392
|
-
from:
|
|
2393
|
-
to:
|
|
2743
|
+
event_type: z16.literal("claude_hook_path_migrated"),
|
|
2744
|
+
from: z16.string(),
|
|
2745
|
+
to: z16.string()
|
|
2394
2746
|
});
|
|
2395
|
-
var codexSkillPathMigratedEventSchema =
|
|
2747
|
+
var codexSkillPathMigratedEventSchema = z16.object({
|
|
2396
2748
|
...eventLedgerEnvelopeSchema,
|
|
2397
|
-
event_type:
|
|
2398
|
-
from:
|
|
2399
|
-
to:
|
|
2749
|
+
event_type: z16.literal("codex_skill_path_migrated"),
|
|
2750
|
+
from: z16.string(),
|
|
2751
|
+
to: z16.string()
|
|
2400
2752
|
});
|
|
2401
|
-
var initScanCompletedEventSchema =
|
|
2753
|
+
var initScanCompletedEventSchema = z16.object({
|
|
2402
2754
|
...eventLedgerEnvelopeSchema,
|
|
2403
|
-
event_type:
|
|
2404
|
-
written_stable_ids:
|
|
2405
|
-
duration_ms:
|
|
2406
|
-
source:
|
|
2755
|
+
event_type: z16.literal("init_scan_completed"),
|
|
2756
|
+
written_stable_ids: z16.array(z16.string()),
|
|
2757
|
+
duration_ms: z16.number().int().nonnegative(),
|
|
2758
|
+
source: z16.enum(["init", "scan", "doctor_fix", "doctor-rescan"]).optional()
|
|
2407
2759
|
});
|
|
2408
|
-
var knowledgeProposedEventSchema =
|
|
2760
|
+
var knowledgeProposedEventSchema = z16.object({
|
|
2409
2761
|
...eventLedgerEnvelopeSchema,
|
|
2410
|
-
event_type:
|
|
2411
|
-
stable_id:
|
|
2412
|
-
timestamp:
|
|
2413
|
-
reason:
|
|
2762
|
+
event_type: z16.literal("knowledge_proposed"),
|
|
2763
|
+
stable_id: z16.string().optional(),
|
|
2764
|
+
timestamp: z16.string().datetime(),
|
|
2765
|
+
reason: z16.string().optional()
|
|
2414
2766
|
});
|
|
2415
|
-
var knowledgePromoteStartedEventSchema =
|
|
2767
|
+
var knowledgePromoteStartedEventSchema = z16.object({
|
|
2416
2768
|
...eventLedgerEnvelopeSchema,
|
|
2417
|
-
event_type:
|
|
2418
|
-
stable_id:
|
|
2419
|
-
timestamp:
|
|
2420
|
-
reason:
|
|
2769
|
+
event_type: z16.literal("knowledge_promote_started"),
|
|
2770
|
+
stable_id: z16.string().optional(),
|
|
2771
|
+
timestamp: z16.string().datetime(),
|
|
2772
|
+
reason: z16.string().optional()
|
|
2421
2773
|
});
|
|
2422
|
-
var knowledgePromotedEventSchema =
|
|
2774
|
+
var knowledgePromotedEventSchema = z16.object({
|
|
2423
2775
|
...eventLedgerEnvelopeSchema,
|
|
2424
|
-
event_type:
|
|
2425
|
-
stable_id:
|
|
2426
|
-
timestamp:
|
|
2427
|
-
reason:
|
|
2776
|
+
event_type: z16.literal("knowledge_promoted"),
|
|
2777
|
+
stable_id: z16.string().optional(),
|
|
2778
|
+
timestamp: z16.string().datetime(),
|
|
2779
|
+
reason: z16.string().optional()
|
|
2428
2780
|
});
|
|
2429
|
-
var knowledgePromoteFailedEventSchema =
|
|
2781
|
+
var knowledgePromoteFailedEventSchema = z16.object({
|
|
2430
2782
|
...eventLedgerEnvelopeSchema,
|
|
2431
|
-
event_type:
|
|
2432
|
-
stable_id:
|
|
2433
|
-
timestamp:
|
|
2434
|
-
reason:
|
|
2783
|
+
event_type: z16.literal("knowledge_promote_failed"),
|
|
2784
|
+
stable_id: z16.string().optional(),
|
|
2785
|
+
timestamp: z16.string().datetime(),
|
|
2786
|
+
reason: z16.string()
|
|
2435
2787
|
});
|
|
2436
|
-
var
|
|
2788
|
+
var knowledgeModifiedEventSchema = z16.object({
|
|
2789
|
+
...eventLedgerEnvelopeSchema,
|
|
2790
|
+
event_type: z16.literal("knowledge_modified"),
|
|
2791
|
+
stable_id: z16.string().optional(),
|
|
2792
|
+
timestamp: z16.string().datetime(),
|
|
2793
|
+
path: z16.string(),
|
|
2794
|
+
changed_fields: z16.array(z16.string()),
|
|
2795
|
+
before: z16.record(z16.unknown()),
|
|
2796
|
+
after: z16.record(z16.unknown()),
|
|
2797
|
+
reason: z16.string().optional()
|
|
2798
|
+
});
|
|
2799
|
+
var knowledgeLayerChangedEventSchema = z16.object({
|
|
2437
2800
|
...eventLedgerEnvelopeSchema,
|
|
2438
|
-
event_type:
|
|
2439
|
-
stable_id:
|
|
2440
|
-
timestamp:
|
|
2441
|
-
reason:
|
|
2442
|
-
from_layer:
|
|
2443
|
-
to_layer:
|
|
2801
|
+
event_type: z16.literal("knowledge_layer_changed"),
|
|
2802
|
+
stable_id: z16.string().optional(),
|
|
2803
|
+
timestamp: z16.string().datetime(),
|
|
2804
|
+
reason: z16.string().optional(),
|
|
2805
|
+
from_layer: z16.enum(["team", "personal"]),
|
|
2806
|
+
to_layer: z16.enum(["team", "personal"]),
|
|
2444
2807
|
// v2.0.0-rc.37 NEW-24: record the pre-flip stable_id so downstream consumers
|
|
2445
2808
|
// (fab_plan_context redirect surface, fab_get_knowledge_sections.redirect_to)
|
|
2446
2809
|
// can map a stale caller-held id back to the post-flip canonical id without
|
|
2447
2810
|
// requiring the caller to re-issue plan-context. Optional for forward-
|
|
2448
2811
|
// compatibility with rc ≤36 events that never carried this field.
|
|
2449
|
-
previous_stable_id:
|
|
2812
|
+
previous_stable_id: z16.string().optional()
|
|
2450
2813
|
});
|
|
2451
|
-
var knowledgeIdRedirectEventSchema =
|
|
2814
|
+
var knowledgeIdRedirectEventSchema = z16.object({
|
|
2452
2815
|
...eventLedgerEnvelopeSchema,
|
|
2453
|
-
event_type:
|
|
2454
|
-
timestamp:
|
|
2455
|
-
previous_stable_id:
|
|
2456
|
-
new_stable_id:
|
|
2457
|
-
reason:
|
|
2816
|
+
event_type: z16.literal("knowledge_id_redirect"),
|
|
2817
|
+
timestamp: z16.string().datetime(),
|
|
2818
|
+
previous_stable_id: z16.string(),
|
|
2819
|
+
new_stable_id: z16.string(),
|
|
2820
|
+
reason: z16.string().optional()
|
|
2458
2821
|
});
|
|
2459
|
-
var knowledgeSlugRenamedEventSchema =
|
|
2822
|
+
var knowledgeSlugRenamedEventSchema = z16.object({
|
|
2460
2823
|
...eventLedgerEnvelopeSchema,
|
|
2461
|
-
event_type:
|
|
2462
|
-
stable_id:
|
|
2463
|
-
timestamp:
|
|
2464
|
-
reason:
|
|
2465
|
-
from_slug:
|
|
2466
|
-
to_slug:
|
|
2467
|
-
});
|
|
2468
|
-
var knowledgeDemotedEventSchema =
|
|
2824
|
+
event_type: z16.literal("knowledge_slug_renamed"),
|
|
2825
|
+
stable_id: z16.string().optional(),
|
|
2826
|
+
timestamp: z16.string().datetime(),
|
|
2827
|
+
reason: z16.string().optional(),
|
|
2828
|
+
from_slug: z16.string(),
|
|
2829
|
+
to_slug: z16.string()
|
|
2830
|
+
});
|
|
2831
|
+
var knowledgeDemotedEventSchema = z16.object({
|
|
2469
2832
|
...eventLedgerEnvelopeSchema,
|
|
2470
|
-
event_type:
|
|
2471
|
-
stable_id:
|
|
2472
|
-
timestamp:
|
|
2473
|
-
reason:
|
|
2833
|
+
event_type: z16.literal("knowledge_demoted"),
|
|
2834
|
+
stable_id: z16.string().optional(),
|
|
2835
|
+
timestamp: z16.string().datetime(),
|
|
2836
|
+
reason: z16.string().optional()
|
|
2474
2837
|
});
|
|
2475
|
-
var knowledgeArchivedEventSchema =
|
|
2838
|
+
var knowledgeArchivedEventSchema = z16.object({
|
|
2476
2839
|
...eventLedgerEnvelopeSchema,
|
|
2477
|
-
event_type:
|
|
2478
|
-
stable_id:
|
|
2479
|
-
timestamp:
|
|
2480
|
-
reason:
|
|
2840
|
+
event_type: z16.literal("knowledge_archived"),
|
|
2841
|
+
stable_id: z16.string().optional(),
|
|
2842
|
+
timestamp: z16.string().datetime(),
|
|
2843
|
+
reason: z16.string().optional()
|
|
2481
2844
|
});
|
|
2482
|
-
var knowledgeArchiveAttemptedEventSchema =
|
|
2845
|
+
var knowledgeArchiveAttemptedEventSchema = z16.object({
|
|
2483
2846
|
...eventLedgerEnvelopeSchema,
|
|
2484
|
-
event_type:
|
|
2485
|
-
stable_id:
|
|
2486
|
-
timestamp:
|
|
2487
|
-
reason:
|
|
2847
|
+
event_type: z16.literal("knowledge_archive_attempted"),
|
|
2848
|
+
stable_id: z16.string().optional(),
|
|
2849
|
+
timestamp: z16.string().datetime(),
|
|
2850
|
+
reason: z16.string().optional()
|
|
2488
2851
|
});
|
|
2489
|
-
var knowledgeUnarchivedEventSchema =
|
|
2852
|
+
var knowledgeUnarchivedEventSchema = z16.object({
|
|
2490
2853
|
...eventLedgerEnvelopeSchema,
|
|
2491
|
-
event_type:
|
|
2492
|
-
stable_id:
|
|
2493
|
-
timestamp:
|
|
2494
|
-
reason:
|
|
2854
|
+
event_type: z16.literal("knowledge_unarchived"),
|
|
2855
|
+
stable_id: z16.string().optional(),
|
|
2856
|
+
timestamp: z16.string().datetime(),
|
|
2857
|
+
reason: z16.string().optional(),
|
|
2495
2858
|
// Pre-move archive path (e.g. ".fabric/.archive/decisions/KT-D-0007--single-cjs-hook.md").
|
|
2496
|
-
archive_path:
|
|
2497
|
-
// Post-move canonical path (e.g. "
|
|
2498
|
-
restored_to:
|
|
2859
|
+
archive_path: z16.string().optional(),
|
|
2860
|
+
// Post-move canonical path (e.g. "knowledge/decisions/KT-DEC-0007--single-cjs-hook.md" inside the resolved store).
|
|
2861
|
+
restored_to: z16.string().optional()
|
|
2499
2862
|
});
|
|
2500
|
-
var knowledgeDeferredEventSchema =
|
|
2863
|
+
var knowledgeDeferredEventSchema = z16.object({
|
|
2501
2864
|
...eventLedgerEnvelopeSchema,
|
|
2502
|
-
event_type:
|
|
2503
|
-
stable_id:
|
|
2504
|
-
|
|
2505
|
-
|
|
2506
|
-
|
|
2507
|
-
|
|
2508
|
-
|
|
2865
|
+
event_type: z16.literal("knowledge_deferred"),
|
|
2866
|
+
stable_id: z16.string().optional(),
|
|
2867
|
+
pending_path: z16.string().optional(),
|
|
2868
|
+
timestamp: z16.string().datetime(),
|
|
2869
|
+
reason: z16.string().optional(),
|
|
2870
|
+
until: z16.string().datetime().optional()
|
|
2871
|
+
});
|
|
2872
|
+
var knowledgeRejectedEventSchema = z16.object({
|
|
2509
2873
|
...eventLedgerEnvelopeSchema,
|
|
2510
|
-
event_type:
|
|
2511
|
-
stable_id:
|
|
2512
|
-
timestamp:
|
|
2513
|
-
reason:
|
|
2874
|
+
event_type: z16.literal("knowledge_rejected"),
|
|
2875
|
+
stable_id: z16.string().optional(),
|
|
2876
|
+
timestamp: z16.string().datetime(),
|
|
2877
|
+
reason: z16.string()
|
|
2514
2878
|
});
|
|
2515
|
-
var knowledgeConsumedEventSchema =
|
|
2879
|
+
var knowledgeConsumedEventSchema = z16.object({
|
|
2516
2880
|
...eventLedgerEnvelopeSchema,
|
|
2517
|
-
event_type:
|
|
2518
|
-
stable_id:
|
|
2519
|
-
consumed_at:
|
|
2520
|
-
client_hash:
|
|
2881
|
+
event_type: z16.literal("knowledge_consumed"),
|
|
2882
|
+
stable_id: z16.string(),
|
|
2883
|
+
consumed_at: z16.string().datetime(),
|
|
2884
|
+
client_hash: z16.string()
|
|
2521
2885
|
});
|
|
2522
|
-
var knowledgeScopeDegradedEventSchema =
|
|
2886
|
+
var knowledgeScopeDegradedEventSchema = z16.object({
|
|
2523
2887
|
...eventLedgerEnvelopeSchema,
|
|
2524
|
-
event_type:
|
|
2525
|
-
stable_id:
|
|
2526
|
-
timestamp:
|
|
2527
|
-
from_scope:
|
|
2528
|
-
to_scope:
|
|
2529
|
-
reason:
|
|
2530
|
-
});
|
|
2531
|
-
var doctorRunEventSchema =
|
|
2888
|
+
event_type: z16.literal("knowledge_scope_degraded"),
|
|
2889
|
+
stable_id: z16.string(),
|
|
2890
|
+
timestamp: z16.string().datetime(),
|
|
2891
|
+
from_scope: z16.enum(["narrow", "broad"]),
|
|
2892
|
+
to_scope: z16.enum(["narrow", "broad"]),
|
|
2893
|
+
reason: z16.string()
|
|
2894
|
+
});
|
|
2895
|
+
var doctorRunEventSchema = z16.object({
|
|
2532
2896
|
...eventLedgerEnvelopeSchema,
|
|
2533
|
-
event_type:
|
|
2534
|
-
mode:
|
|
2535
|
-
issues:
|
|
2536
|
-
mutations:
|
|
2537
|
-
timestamp:
|
|
2897
|
+
event_type: z16.literal("doctor_run"),
|
|
2898
|
+
mode: z16.enum(["lint", "fix-knowledge"]),
|
|
2899
|
+
issues: z16.number().int().nonnegative(),
|
|
2900
|
+
mutations: z16.number().int().nonnegative().optional(),
|
|
2901
|
+
timestamp: z16.string().datetime()
|
|
2538
2902
|
});
|
|
2539
|
-
var knowledgePathDangledEventSchema =
|
|
2903
|
+
var knowledgePathDangledEventSchema = z16.object({
|
|
2540
2904
|
...eventLedgerEnvelopeSchema,
|
|
2541
|
-
event_type:
|
|
2542
|
-
stable_id:
|
|
2543
|
-
removed_glob:
|
|
2905
|
+
event_type: z16.literal("knowledge_path_dangled"),
|
|
2906
|
+
stable_id: z16.string(),
|
|
2907
|
+
removed_glob: z16.string()
|
|
2544
2908
|
});
|
|
2545
|
-
var relevanceMigrationRunEventSchema =
|
|
2909
|
+
var relevanceMigrationRunEventSchema = z16.object({
|
|
2546
2910
|
...eventLedgerEnvelopeSchema,
|
|
2547
|
-
event_type:
|
|
2548
|
-
timestamp:
|
|
2549
|
-
scanned_count:
|
|
2550
|
-
touched_count:
|
|
2911
|
+
event_type: z16.literal("relevance_migration_run"),
|
|
2912
|
+
timestamp: z16.string().datetime(),
|
|
2913
|
+
scanned_count: z16.number().int().nonnegative(),
|
|
2914
|
+
touched_count: z16.number().int().nonnegative()
|
|
2551
2915
|
});
|
|
2552
|
-
var pendingAutoArchivedEventSchema =
|
|
2916
|
+
var pendingAutoArchivedEventSchema = z16.object({
|
|
2553
2917
|
...eventLedgerEnvelopeSchema,
|
|
2554
|
-
event_type:
|
|
2555
|
-
pending_path:
|
|
2556
|
-
archived_to:
|
|
2557
|
-
reason:
|
|
2918
|
+
event_type: z16.literal("pending_auto_archived"),
|
|
2919
|
+
pending_path: z16.string(),
|
|
2920
|
+
archived_to: z16.string(),
|
|
2921
|
+
reason: z16.string()
|
|
2558
2922
|
});
|
|
2559
|
-
var assistantTurnObservedEventSchema =
|
|
2923
|
+
var assistantTurnObservedEventSchema = z16.object({
|
|
2560
2924
|
...eventLedgerEnvelopeSchema,
|
|
2561
|
-
event_type:
|
|
2562
|
-
kb_line_raw:
|
|
2563
|
-
cite_ids:
|
|
2564
|
-
cite_tags:
|
|
2925
|
+
event_type: z16.literal("assistant_turn_observed"),
|
|
2926
|
+
kb_line_raw: z16.string().nullable(),
|
|
2927
|
+
cite_ids: z16.array(z16.string()).default([]),
|
|
2928
|
+
cite_tags: z16.array(citeTagSchema).default([]),
|
|
2565
2929
|
// v2.0.0-rc.24 TASK-01: per-cite contract commitments. Index-aligned with
|
|
2566
2930
|
// cite_ids/cite_tags (commitments[i] belongs to cite_ids[i]). Each slot
|
|
2567
2931
|
// carries `operators[]` (kind + glob target) or `skip_reason` when the cite
|
|
@@ -2569,15 +2933,15 @@ var assistantTurnObservedEventSchema = z18.object({
|
|
|
2569
2933
|
// empty array via `.default([])` and are excluded from contract-policy
|
|
2570
2934
|
// audits by the marker-gate (see cite_contract_policy_activated below).
|
|
2571
2935
|
// Mirrors the rc.20 cite_tags parallel-array evolution exactly.
|
|
2572
|
-
cite_commitments:
|
|
2573
|
-
|
|
2574
|
-
operators:
|
|
2575
|
-
|
|
2576
|
-
kind:
|
|
2577
|
-
target:
|
|
2936
|
+
cite_commitments: z16.array(
|
|
2937
|
+
z16.object({
|
|
2938
|
+
operators: z16.array(
|
|
2939
|
+
z16.object({
|
|
2940
|
+
kind: z16.enum(["edit", "not_edit", "require", "forbid"]),
|
|
2941
|
+
target: z16.string()
|
|
2578
2942
|
})
|
|
2579
2943
|
),
|
|
2580
|
-
skip_reason:
|
|
2944
|
+
skip_reason: z16.string().nullable()
|
|
2581
2945
|
})
|
|
2582
2946
|
).default([]),
|
|
2583
2947
|
// lifecycle-refactor W3-T4 (§2 store 轴 / store-qualified 观测): per-cite store
|
|
@@ -2587,174 +2951,183 @@ var assistantTurnObservedEventSchema = z18.object({
|
|
|
2587
2951
|
// doctor --cite-coverage can break compliance down per store WITHOUT joining
|
|
2588
2952
|
// against the store registry. Additive `.optional()` (NOT `.default([])`) so
|
|
2589
2953
|
// existing inline event constructors stay valid without supplying it — pre-W3-T4
|
|
2590
|
-
// events parse with the field absent and bucket under the
|
|
2591
|
-
cite_stores:
|
|
2592
|
-
client:
|
|
2593
|
-
turn_id:
|
|
2594
|
-
envelope_index:
|
|
2595
|
-
timestamp:
|
|
2596
|
-
});
|
|
2597
|
-
var citePolicyActivatedEventSchema =
|
|
2954
|
+
// events parse with the field absent and bucket under the unqualified default.
|
|
2955
|
+
cite_stores: z16.array(z16.string().nullable()).optional(),
|
|
2956
|
+
client: z16.enum(["cc", "codex"]).optional(),
|
|
2957
|
+
turn_id: z16.string(),
|
|
2958
|
+
envelope_index: z16.number().int().nonnegative().optional(),
|
|
2959
|
+
timestamp: z16.string().datetime()
|
|
2960
|
+
});
|
|
2961
|
+
var citePolicyActivatedEventSchema = z16.object({
|
|
2598
2962
|
...eventLedgerEnvelopeSchema,
|
|
2599
|
-
event_type:
|
|
2600
|
-
policy_version:
|
|
2601
|
-
timestamp:
|
|
2963
|
+
event_type: z16.literal("cite_policy_activated"),
|
|
2964
|
+
policy_version: z16.string(),
|
|
2965
|
+
timestamp: z16.string().datetime()
|
|
2602
2966
|
});
|
|
2603
|
-
var citeContractPolicyActivatedEventSchema =
|
|
2967
|
+
var citeContractPolicyActivatedEventSchema = z16.object({
|
|
2604
2968
|
...eventLedgerEnvelopeSchema,
|
|
2605
|
-
event_type:
|
|
2969
|
+
event_type: z16.literal("cite_contract_policy_activated")
|
|
2606
2970
|
});
|
|
2607
|
-
var eventsRotatedEventSchema =
|
|
2971
|
+
var eventsRotatedEventSchema = z16.object({
|
|
2608
2972
|
...eventLedgerEnvelopeSchema,
|
|
2609
|
-
event_type:
|
|
2610
|
-
cutoff_ts:
|
|
2611
|
-
archived_count:
|
|
2612
|
-
kept_count:
|
|
2613
|
-
archive_path:
|
|
2973
|
+
event_type: z16.literal("events_rotated"),
|
|
2974
|
+
cutoff_ts: z16.string().datetime(),
|
|
2975
|
+
archived_count: z16.number().int().nonnegative(),
|
|
2976
|
+
kept_count: z16.number().int().nonnegative(),
|
|
2977
|
+
archive_path: z16.string()
|
|
2614
2978
|
});
|
|
2615
|
-
var knowledgeMetaAutoHealedEventSchema =
|
|
2979
|
+
var knowledgeMetaAutoHealedEventSchema = z16.object({
|
|
2616
2980
|
...eventLedgerEnvelopeSchema,
|
|
2617
|
-
event_type:
|
|
2618
|
-
previous_revision_hash:
|
|
2619
|
-
revision_hash:
|
|
2620
|
-
trigger:
|
|
2621
|
-
caller:
|
|
2981
|
+
event_type: z16.literal("knowledge_meta_auto_healed"),
|
|
2982
|
+
previous_revision_hash: z16.string(),
|
|
2983
|
+
revision_hash: z16.string(),
|
|
2984
|
+
trigger: z16.literal("read"),
|
|
2985
|
+
caller: z16.enum(["planContext", "getKnowledgeSections", "getKnowledge", "extractKnowledge"]).optional()
|
|
2622
2986
|
});
|
|
2623
|
-
var serveLockClearedEventSchema =
|
|
2987
|
+
var serveLockClearedEventSchema = z16.object({
|
|
2624
2988
|
...eventLedgerEnvelopeSchema,
|
|
2625
|
-
event_type:
|
|
2626
|
-
pid:
|
|
2627
|
-
age_ms:
|
|
2628
|
-
timestamp:
|
|
2989
|
+
event_type: z16.literal("serve_lock_cleared"),
|
|
2990
|
+
pid: z16.number().int().nonnegative(),
|
|
2991
|
+
age_ms: z16.number().int().nonnegative(),
|
|
2992
|
+
timestamp: z16.string().datetime()
|
|
2629
2993
|
});
|
|
2630
|
-
var knowledgeEnrichedEventSchema =
|
|
2994
|
+
var knowledgeEnrichedEventSchema = z16.object({
|
|
2631
2995
|
...eventLedgerEnvelopeSchema,
|
|
2632
|
-
event_type:
|
|
2633
|
-
path:
|
|
2634
|
-
added_fields:
|
|
2635
|
-
mode:
|
|
2636
|
-
timestamp:
|
|
2996
|
+
event_type: z16.literal("knowledge_enriched"),
|
|
2997
|
+
path: z16.string(),
|
|
2998
|
+
added_fields: z16.array(z16.enum(["intent_clues", "tech_stack", "impact", "must_read_if"])),
|
|
2999
|
+
mode: z16.enum(["auto", "preview", "readonly", "interactive"]),
|
|
3000
|
+
timestamp: z16.string().datetime()
|
|
2637
3001
|
});
|
|
2638
|
-
var sessionArchiveAttemptedEventSchema =
|
|
3002
|
+
var sessionArchiveAttemptedEventSchema = z16.object({
|
|
2639
3003
|
...eventLedgerEnvelopeSchema,
|
|
2640
|
-
event_type:
|
|
2641
|
-
outcome:
|
|
2642
|
-
covered_through_ts:
|
|
2643
|
-
candidates_proposed:
|
|
2644
|
-
knowledge_proposed_ids:
|
|
3004
|
+
event_type: z16.literal("session_archive_attempted"),
|
|
3005
|
+
outcome: z16.enum(["proposed", "viability_failed", "user_dismissed", "skipped_no_signal"]),
|
|
3006
|
+
covered_through_ts: z16.number().int().nonnegative(),
|
|
3007
|
+
candidates_proposed: z16.number().int().nonnegative().default(0),
|
|
3008
|
+
knowledge_proposed_ids: z16.array(z16.string()).default([])
|
|
2645
3009
|
});
|
|
2646
|
-
var hookSurfaceEmittedEventSchema =
|
|
3010
|
+
var hookSurfaceEmittedEventSchema = z16.object({
|
|
2647
3011
|
...eventLedgerEnvelopeSchema,
|
|
2648
|
-
event_type:
|
|
2649
|
-
hook_name:
|
|
2650
|
-
client:
|
|
2651
|
-
target_channel:
|
|
2652
|
-
rendered_ids:
|
|
2653
|
-
delivery_status:
|
|
2654
|
-
suppression_reason:
|
|
2655
|
-
});
|
|
2656
|
-
var hookSignalEmittedEventSchema =
|
|
3012
|
+
event_type: z16.literal("hook_surface_emitted"),
|
|
3013
|
+
hook_name: z16.string(),
|
|
3014
|
+
client: z16.enum(["cc", "codex"]),
|
|
3015
|
+
target_channel: z16.string(),
|
|
3016
|
+
rendered_ids: z16.array(z16.string()),
|
|
3017
|
+
delivery_status: z16.enum(["delivered", "suppressed", "error"]),
|
|
3018
|
+
suppression_reason: z16.string().optional()
|
|
3019
|
+
});
|
|
3020
|
+
var hookSignalEmittedEventSchema = z16.object({
|
|
2657
3021
|
...eventLedgerEnvelopeSchema,
|
|
2658
|
-
event_type:
|
|
2659
|
-
signal_type:
|
|
2660
|
-
threshold:
|
|
2661
|
-
actual_value:
|
|
2662
|
-
fired:
|
|
3022
|
+
event_type: z16.literal("hook_signal_emitted"),
|
|
3023
|
+
signal_type: z16.enum(["archive", "review", "maintenance", "other"]),
|
|
3024
|
+
threshold: z16.number(),
|
|
3025
|
+
actual_value: z16.number(),
|
|
3026
|
+
fired: z16.boolean()
|
|
2663
3027
|
});
|
|
2664
|
-
var mcpStdioTraceEventSchema =
|
|
3028
|
+
var mcpStdioTraceEventSchema = z16.object({
|
|
2665
3029
|
...eventLedgerEnvelopeSchema,
|
|
2666
|
-
event_type:
|
|
2667
|
-
tool_name:
|
|
2668
|
-
request_id:
|
|
2669
|
-
duration_ms:
|
|
2670
|
-
status:
|
|
2671
|
-
payload_bytes_in:
|
|
2672
|
-
payload_bytes_out:
|
|
2673
|
-
error_code:
|
|
2674
|
-
});
|
|
2675
|
-
var payloadGuardObservedEventSchema =
|
|
3030
|
+
event_type: z16.literal("mcp_stdio_trace"),
|
|
3031
|
+
tool_name: z16.string(),
|
|
3032
|
+
request_id: z16.string(),
|
|
3033
|
+
duration_ms: z16.number().nonnegative(),
|
|
3034
|
+
status: z16.enum(["ok", "error"]),
|
|
3035
|
+
payload_bytes_in: z16.number().int().nonnegative(),
|
|
3036
|
+
payload_bytes_out: z16.number().int().nonnegative(),
|
|
3037
|
+
error_code: z16.string().optional()
|
|
3038
|
+
});
|
|
3039
|
+
var payloadGuardObservedEventSchema = z16.object({
|
|
2676
3040
|
...eventLedgerEnvelopeSchema,
|
|
2677
|
-
event_type:
|
|
2678
|
-
tool_name:
|
|
2679
|
-
path_count:
|
|
2680
|
-
tokens_estimated:
|
|
2681
|
-
truncated:
|
|
2682
|
-
cap:
|
|
2683
|
-
});
|
|
2684
|
-
var skillInvocationStartedEventSchema =
|
|
3041
|
+
event_type: z16.literal("payload_guard_observed"),
|
|
3042
|
+
tool_name: z16.string(),
|
|
3043
|
+
path_count: z16.number().int().nonnegative(),
|
|
3044
|
+
tokens_estimated: z16.number().int().nonnegative(),
|
|
3045
|
+
truncated: z16.boolean(),
|
|
3046
|
+
cap: z16.number().int().positive()
|
|
3047
|
+
});
|
|
3048
|
+
var skillInvocationStartedEventSchema = z16.object({
|
|
2685
3049
|
...eventLedgerEnvelopeSchema,
|
|
2686
|
-
event_type:
|
|
2687
|
-
skill_name:
|
|
2688
|
-
trigger_source:
|
|
2689
|
-
entry_point:
|
|
3050
|
+
event_type: z16.literal("skill_invocation_started"),
|
|
3051
|
+
skill_name: z16.string(),
|
|
3052
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3053
|
+
entry_point: z16.string()
|
|
2690
3054
|
});
|
|
2691
|
-
var skillInvocationCompletedEventSchema =
|
|
3055
|
+
var skillInvocationCompletedEventSchema = z16.object({
|
|
2692
3056
|
...eventLedgerEnvelopeSchema,
|
|
2693
|
-
event_type:
|
|
2694
|
-
skill_name:
|
|
2695
|
-
trigger_source:
|
|
2696
|
-
entry_point:
|
|
2697
|
-
outcome:
|
|
2698
|
-
elapsed_ms:
|
|
2699
|
-
});
|
|
2700
|
-
var skillPhaseTransitionEventSchema =
|
|
3057
|
+
event_type: z16.literal("skill_invocation_completed"),
|
|
3058
|
+
skill_name: z16.string(),
|
|
3059
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3060
|
+
entry_point: z16.string(),
|
|
3061
|
+
outcome: z16.enum(["completed", "aborted", "error", "no_op"]),
|
|
3062
|
+
elapsed_ms: z16.number().nonnegative().optional()
|
|
3063
|
+
});
|
|
3064
|
+
var skillPhaseTransitionEventSchema = z16.object({
|
|
2701
3065
|
...eventLedgerEnvelopeSchema,
|
|
2702
|
-
event_type:
|
|
2703
|
-
skill_name:
|
|
2704
|
-
phase:
|
|
2705
|
-
status:
|
|
2706
|
-
checkpoint:
|
|
2707
|
-
elapsed_ms:
|
|
2708
|
-
});
|
|
2709
|
-
var skillTriggerCandidateEventSchema =
|
|
3066
|
+
event_type: z16.literal("skill_phase_transition"),
|
|
3067
|
+
skill_name: z16.string(),
|
|
3068
|
+
phase: z16.string(),
|
|
3069
|
+
status: z16.enum(["entered", "completed", "skipped", "failed"]),
|
|
3070
|
+
checkpoint: z16.string().optional(),
|
|
3071
|
+
elapsed_ms: z16.number().nonnegative().optional()
|
|
3072
|
+
});
|
|
3073
|
+
var skillTriggerCandidateEventSchema = z16.object({
|
|
2710
3074
|
...eventLedgerEnvelopeSchema,
|
|
2711
|
-
event_type:
|
|
2712
|
-
skill_name:
|
|
2713
|
-
trigger_source:
|
|
2714
|
-
signal:
|
|
2715
|
-
invoked:
|
|
3075
|
+
event_type: z16.literal("skill_trigger_candidate"),
|
|
3076
|
+
skill_name: z16.string(),
|
|
3077
|
+
trigger_source: z16.enum(["user", "auto_invoke", "ai_self_trigger", "chained"]),
|
|
3078
|
+
signal: z16.string(),
|
|
3079
|
+
invoked: z16.boolean()
|
|
2716
3080
|
});
|
|
2717
|
-
var llmJudgeRunEventSchema =
|
|
3081
|
+
var llmJudgeRunEventSchema = z16.object({
|
|
2718
3082
|
...eventLedgerEnvelopeSchema,
|
|
2719
|
-
event_type:
|
|
2720
|
-
prompt:
|
|
2721
|
-
version:
|
|
2722
|
-
model:
|
|
2723
|
-
input_trace_id:
|
|
2724
|
-
score:
|
|
2725
|
-
rationale:
|
|
2726
|
-
});
|
|
2727
|
-
var clientCapabilitySnapshotEventSchema =
|
|
3083
|
+
event_type: z16.literal("llm_judge_run"),
|
|
3084
|
+
prompt: z16.string(),
|
|
3085
|
+
version: z16.string(),
|
|
3086
|
+
model: z16.string(),
|
|
3087
|
+
input_trace_id: z16.string(),
|
|
3088
|
+
score: z16.number(),
|
|
3089
|
+
rationale: z16.string()
|
|
3090
|
+
});
|
|
3091
|
+
var clientCapabilitySnapshotEventSchema = z16.object({
|
|
2728
3092
|
...eventLedgerEnvelopeSchema,
|
|
2729
|
-
event_type:
|
|
2730
|
-
client:
|
|
2731
|
-
capabilities:
|
|
2732
|
-
version:
|
|
3093
|
+
event_type: z16.literal("client_capability_snapshot"),
|
|
3094
|
+
client: z16.enum(["cc", "codex"]),
|
|
3095
|
+
capabilities: z16.array(z16.string()),
|
|
3096
|
+
version: z16.string()
|
|
2733
3097
|
});
|
|
2734
|
-
var sessionEndedEventSchema =
|
|
3098
|
+
var sessionEndedEventSchema = z16.object({
|
|
2735
3099
|
...eventLedgerEnvelopeSchema,
|
|
2736
|
-
event_type:
|
|
3100
|
+
event_type: z16.literal("session_ended")
|
|
2737
3101
|
});
|
|
2738
|
-
var fileMutatedEventSchema =
|
|
3102
|
+
var fileMutatedEventSchema = z16.object({
|
|
3103
|
+
...eventLedgerEnvelopeSchema,
|
|
3104
|
+
event_type: z16.literal("file_mutated"),
|
|
3105
|
+
path: z16.string(),
|
|
3106
|
+
tool_call_id: z16.string(),
|
|
3107
|
+
tool_name: z16.string().optional(),
|
|
3108
|
+
source_event_id: z16.string().optional(),
|
|
3109
|
+
store_id: z16.string().optional()
|
|
3110
|
+
});
|
|
3111
|
+
var knowledgeBodyReadEventSchema = z16.object({
|
|
2739
3112
|
...eventLedgerEnvelopeSchema,
|
|
2740
|
-
event_type:
|
|
2741
|
-
|
|
2742
|
-
|
|
2743
|
-
|
|
2744
|
-
|
|
2745
|
-
|
|
2746
|
-
});
|
|
2747
|
-
var precompactObservedEventSchema =
|
|
3113
|
+
event_type: z16.literal("knowledge_body_read"),
|
|
3114
|
+
stable_id: z16.string(),
|
|
3115
|
+
store: z16.string().optional(),
|
|
3116
|
+
path: z16.string(),
|
|
3117
|
+
tool_call_id: z16.string().optional(),
|
|
3118
|
+
tool_name: z16.string().optional()
|
|
3119
|
+
});
|
|
3120
|
+
var precompactObservedEventSchema = z16.object({
|
|
2748
3121
|
...eventLedgerEnvelopeSchema,
|
|
2749
|
-
event_type:
|
|
3122
|
+
event_type: z16.literal("precompact_observed")
|
|
2750
3123
|
});
|
|
2751
|
-
var graphEdgeCandidateRequestedEventSchema =
|
|
3124
|
+
var graphEdgeCandidateRequestedEventSchema = z16.object({
|
|
2752
3125
|
...eventLedgerEnvelopeSchema,
|
|
2753
|
-
event_type:
|
|
2754
|
-
stable_id:
|
|
2755
|
-
store:
|
|
3126
|
+
event_type: z16.literal("graph_edge_candidate_requested"),
|
|
3127
|
+
stable_id: z16.string(),
|
|
3128
|
+
store: z16.string().optional()
|
|
2756
3129
|
});
|
|
2757
|
-
var eventLedgerEventSchema =
|
|
3130
|
+
var eventLedgerEventSchema = z16.discriminatedUnion("event_type", [
|
|
2758
3131
|
knowledgeContextPlannedEventSchema,
|
|
2759
3132
|
knowledgeSelectionEventSchema,
|
|
2760
3133
|
knowledgeSectionsFetchedEventSchema,
|
|
@@ -2764,10 +3137,6 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2764
3137
|
reapplyCompletedEventSchema,
|
|
2765
3138
|
installDiffAppliedEventSchema,
|
|
2766
3139
|
eventLedgerTruncatedEventSchema,
|
|
2767
|
-
mcpConfigMigratedEventSchema,
|
|
2768
|
-
// v2.0.0-rc.19 TASK-004: bootstrap_marker_migrated — one-time fabric:knowledge-base
|
|
2769
|
-
// → fabric:bootstrap marker rewrite emitted per file by `fabric doctor --fix`.
|
|
2770
|
-
bootstrapMarkerMigratedEventSchema,
|
|
2771
3140
|
metaReconciledOnStartupEventSchema,
|
|
2772
3141
|
metaReconciledEventSchema,
|
|
2773
3142
|
claudeSkillPathMigratedEventSchema,
|
|
@@ -2779,6 +3148,7 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2779
3148
|
knowledgePromoteStartedEventSchema,
|
|
2780
3149
|
knowledgePromotedEventSchema,
|
|
2781
3150
|
knowledgePromoteFailedEventSchema,
|
|
3151
|
+
knowledgeModifiedEventSchema,
|
|
2782
3152
|
knowledgeLayerChangedEventSchema,
|
|
2783
3153
|
// v2.0.0-rc.37 NEW-24: dedicated old→new stable_id mapping event
|
|
2784
3154
|
knowledgeIdRedirectEventSchema,
|
|
@@ -2850,6 +3220,9 @@ var eventLedgerEventSchema = z18.discriminatedUnion("event_type", [
|
|
|
2850
3220
|
// lifecycle-refactor Wave 2 — dormant-hook activation markers.
|
|
2851
3221
|
sessionEndedEventSchema,
|
|
2852
3222
|
fileMutatedEventSchema,
|
|
3223
|
+
// KT-DEC-0030: knowledge_body_read — PostToolUse native-Read consumption marker
|
|
3224
|
+
// (replaces knowledge_consumed as the funnel's "body opened" signal).
|
|
3225
|
+
knowledgeBodyReadEventSchema,
|
|
2853
3226
|
precompactObservedEventSchema,
|
|
2854
3227
|
graphEdgeCandidateRequestedEventSchema
|
|
2855
3228
|
]);
|
|
@@ -2889,19 +3262,24 @@ var PROFILES = {
|
|
|
2889
3262
|
topK: 12,
|
|
2890
3263
|
payloadWarnBytes: 8192,
|
|
2891
3264
|
payloadHardBytes: 32768,
|
|
2892
|
-
injectionChars: 1e3
|
|
3265
|
+
injectionChars: 1e3,
|
|
3266
|
+
bodyBudgetBytes: 2048
|
|
2893
3267
|
},
|
|
2894
3268
|
balanced: {
|
|
2895
3269
|
topK: 24,
|
|
2896
3270
|
payloadWarnBytes: 16384,
|
|
2897
3271
|
payloadHardBytes: 65536,
|
|
2898
|
-
injectionChars: 2e3
|
|
3272
|
+
injectionChars: 2e3,
|
|
3273
|
+
// grill-report C-009: ~4KB ≈ 1~3 typical bodies. Independent of the 16KB
|
|
3274
|
+
// payloadWarnBytes (the response cap), small on purpose — see field doc.
|
|
3275
|
+
bodyBudgetBytes: 4096
|
|
2899
3276
|
},
|
|
2900
3277
|
generous: {
|
|
2901
3278
|
topK: 48,
|
|
2902
3279
|
payloadWarnBytes: 32768,
|
|
2903
3280
|
payloadHardBytes: 131072,
|
|
2904
|
-
injectionChars: 4e3
|
|
3281
|
+
injectionChars: 4e3,
|
|
3282
|
+
bodyBudgetBytes: 8192
|
|
2905
3283
|
}
|
|
2906
3284
|
};
|
|
2907
3285
|
var DEFAULT_RETRIEVAL_BUDGET_PROFILE = "balanced";
|
|
@@ -2911,7 +3289,8 @@ function resolveRetrievalBudget(overrides) {
|
|
|
2911
3289
|
topK: overrides?.topK ?? base.topK,
|
|
2912
3290
|
payloadWarnBytes: overrides?.payloadWarnBytes ?? base.payloadWarnBytes,
|
|
2913
3291
|
payloadHardBytes: overrides?.payloadHardBytes ?? base.payloadHardBytes,
|
|
2914
|
-
injectionChars: overrides?.injectionChars ?? base.injectionChars
|
|
3292
|
+
injectionChars: overrides?.injectionChars ?? base.injectionChars,
|
|
3293
|
+
bodyBudgetBytes: overrides?.bodyBudgetBytes ?? base.bodyBudgetBytes
|
|
2915
3294
|
};
|
|
2916
3295
|
}
|
|
2917
3296
|
function retrievalBudgetProfile(profile) {
|
|
@@ -2919,10 +3298,11 @@ function retrievalBudgetProfile(profile) {
|
|
|
2919
3298
|
}
|
|
2920
3299
|
export {
|
|
2921
3300
|
AGENTS_META_IDENTITY_SOURCES,
|
|
2922
|
-
AGENTS_META_LAYERS,
|
|
2923
3301
|
AGENTS_META_TOPOLOGY_TYPES,
|
|
2924
3302
|
AgentsMetaCountersSchema,
|
|
2925
|
-
|
|
3303
|
+
BOOTSTRAP_CANONICAL_BY_LOCALE,
|
|
3304
|
+
BOOTSTRAP_CANONICAL_EN,
|
|
3305
|
+
BOOTSTRAP_CANONICAL_ZH,
|
|
2926
3306
|
BOOTSTRAP_MARKER_BEGIN,
|
|
2927
3307
|
BOOTSTRAP_MARKER_END,
|
|
2928
3308
|
BOOTSTRAP_REGEX,
|
|
@@ -2942,9 +3322,6 @@ export {
|
|
|
2942
3322
|
KNOWN_SCOPE_PREFIXES,
|
|
2943
3323
|
KnowledgeEntryFrontmatterSchema,
|
|
2944
3324
|
KnowledgeTypeSchema,
|
|
2945
|
-
LEGACY_KB_MARKER_BEGIN,
|
|
2946
|
-
LEGACY_KB_MARKER_END,
|
|
2947
|
-
LEGACY_KB_REGEX,
|
|
2948
3325
|
LayerSchema,
|
|
2949
3326
|
MCP_STORE_AWARE_CONTRACTS,
|
|
2950
3327
|
MCP_STORE_AWARE_TOOLS,
|
|
@@ -2956,30 +3333,34 @@ export {
|
|
|
2956
3333
|
PERSONAL_SCOPE,
|
|
2957
3334
|
PERSONAL_STORE_SENTINEL,
|
|
2958
3335
|
PROJECT_ROOT_SIGNALS,
|
|
2959
|
-
|
|
3336
|
+
PROPOSED_REASON_DESCRIPTIONS_BY_LOCALE,
|
|
2960
3337
|
PROTECTED_TOKENS,
|
|
2961
3338
|
ProposedReasonSchema,
|
|
2962
3339
|
REDACTION_PLACEHOLDER_PREFIX,
|
|
2963
|
-
ResolverNotImplementedError,
|
|
2964
3340
|
SCOPE_COORDINATE_PATTERN,
|
|
2965
3341
|
STORES_ROOT_DIR,
|
|
3342
|
+
STORE_ALIAS_PATTERN,
|
|
2966
3343
|
STORE_GITIGNORE,
|
|
2967
3344
|
STORE_KNOWLEDGE_TYPE_DIRS,
|
|
2968
3345
|
STORE_LAYOUT,
|
|
3346
|
+
STORE_MOUNT_GROUPS,
|
|
3347
|
+
STORE_MOUNT_NAME_PATTERN,
|
|
2969
3348
|
STORE_PENDING_DIR,
|
|
3349
|
+
STORE_PROJECT_ID_PATTERN,
|
|
2970
3350
|
STORE_RESOLVER_WARNING_CODES,
|
|
2971
3351
|
STORE_UUID_PATTERN,
|
|
2972
3352
|
StableIdSchema,
|
|
2973
3353
|
UID_SEGMENT_PATTERN,
|
|
2974
3354
|
addMountedStore,
|
|
3355
|
+
addStoreProject,
|
|
2975
3356
|
agentsIdentitySourceSchema,
|
|
2976
|
-
agentsLayerSchema,
|
|
2977
3357
|
agentsMetaNodeSchema,
|
|
2978
3358
|
agentsMetaSchema,
|
|
2979
3359
|
agentsTopologyTypeSchema,
|
|
2980
3360
|
aggregatePendingAcrossStores,
|
|
2981
3361
|
aiLedgerEntrySchema,
|
|
2982
3362
|
allocateKnowledgeId,
|
|
3363
|
+
allocateStoreKnowledgeId,
|
|
2983
3364
|
annotateIntentRequestSchema,
|
|
2984
3365
|
archiveScanAnnotations,
|
|
2985
3366
|
archiveScanInputSchema,
|
|
@@ -2988,7 +3369,6 @@ export {
|
|
|
2988
3369
|
auditModeSchema,
|
|
2989
3370
|
bindRequiredStore,
|
|
2990
3371
|
bindingsSnapshotPath,
|
|
2991
|
-
bootstrapMarkerMigratedEventSchema,
|
|
2992
3372
|
buildDebugBundle,
|
|
2993
3373
|
buildFailureTrace,
|
|
2994
3374
|
buildScanRecommendations,
|
|
@@ -3011,11 +3391,12 @@ export {
|
|
|
3011
3391
|
defaultLayerFilterSchema,
|
|
3012
3392
|
defaultMessages,
|
|
3013
3393
|
deriveAgentsMetaIdentitySource,
|
|
3014
|
-
deriveAgentsMetaLayer,
|
|
3015
3394
|
deriveAgentsMetaStableId,
|
|
3016
3395
|
deriveAgentsMetaTopologyType,
|
|
3396
|
+
deriveMountLabel,
|
|
3017
3397
|
detachMountedStore,
|
|
3018
3398
|
detectNodeLocale,
|
|
3399
|
+
disambiguateAlias,
|
|
3019
3400
|
doctorRunEventSchema,
|
|
3020
3401
|
driftDetectedEventSchema,
|
|
3021
3402
|
editIntentCheckedEventSchema,
|
|
@@ -3075,6 +3456,7 @@ export {
|
|
|
3075
3456
|
isPersonalScope,
|
|
3076
3457
|
knowledgeArchiveAttemptedEventSchema,
|
|
3077
3458
|
knowledgeArchivedEventSchema,
|
|
3459
|
+
knowledgeBodyReadEventSchema,
|
|
3078
3460
|
knowledgeConsumedEventSchema,
|
|
3079
3461
|
knowledgeContextPlannedEventSchema,
|
|
3080
3462
|
knowledgeDeferredEventSchema,
|
|
@@ -3084,6 +3466,7 @@ export {
|
|
|
3084
3466
|
knowledgeIdRedirectEventSchema,
|
|
3085
3467
|
knowledgeLayerChangedEventSchema,
|
|
3086
3468
|
knowledgeMetaAutoHealedEventSchema,
|
|
3469
|
+
knowledgeModifiedEventSchema,
|
|
3087
3470
|
knowledgePathDangledEventSchema,
|
|
3088
3471
|
knowledgePromoteFailedEventSchema,
|
|
3089
3472
|
knowledgePromoteStartedEventSchema,
|
|
@@ -3114,7 +3497,7 @@ export {
|
|
|
3114
3497
|
localKnowledgeIdSchema,
|
|
3115
3498
|
lockApprovedEventSchema,
|
|
3116
3499
|
lockDriftEventSchema,
|
|
3117
|
-
|
|
3500
|
+
matchBootstrapCanonicalLocale,
|
|
3118
3501
|
mcpEventLedgerEventSchema,
|
|
3119
3502
|
mcpPayloadLimitsSchema,
|
|
3120
3503
|
mcpStdioTraceEventSchema,
|
|
@@ -3124,6 +3507,8 @@ export {
|
|
|
3124
3507
|
mountedStoreSchema,
|
|
3125
3508
|
normalizeCiteTag,
|
|
3126
3509
|
normalizeLocale,
|
|
3510
|
+
nudgeModeSchema,
|
|
3511
|
+
observeConfigSchema,
|
|
3127
3512
|
onboardSlotSchema,
|
|
3128
3513
|
parityCapabilitySchema,
|
|
3129
3514
|
parityClientExpectationSchema,
|
|
@@ -3153,20 +3538,28 @@ export {
|
|
|
3153
3538
|
readSetEntrySchema,
|
|
3154
3539
|
readSetGoldenCaseSchema,
|
|
3155
3540
|
readSetGoldenFileSchema,
|
|
3541
|
+
readStoreCounters,
|
|
3156
3542
|
readStoreIdentity,
|
|
3543
|
+
readStoreIdentityAsync,
|
|
3544
|
+
readStoreProjects,
|
|
3157
3545
|
reapplyCompletedEventSchema,
|
|
3158
3546
|
recallAnnotations,
|
|
3159
3547
|
recallInputSchema,
|
|
3160
3548
|
recallOutputSchema,
|
|
3161
3549
|
recognizeStoreDir,
|
|
3550
|
+
reconcileStoreCounters,
|
|
3551
|
+
redactPii,
|
|
3162
3552
|
redactSecrets,
|
|
3163
3553
|
relevanceMigrationRunEventSchema,
|
|
3164
3554
|
requiredStoreEntrySchema,
|
|
3555
|
+
resolveBootstrapCanonical,
|
|
3165
3556
|
resolveCandidates,
|
|
3166
3557
|
resolveFabricLocale,
|
|
3558
|
+
resolveGlobalLocale,
|
|
3167
3559
|
resolveGlobalRoot,
|
|
3168
3560
|
resolveRetrievalBudget,
|
|
3169
3561
|
resolveStoreQualifiedId,
|
|
3562
|
+
resolveWorkspaceBindingId,
|
|
3170
3563
|
resolvedBindingsSnapshotSchema,
|
|
3171
3564
|
retrievalBudgetProfile,
|
|
3172
3565
|
ruleDescriptionIndexItemSchema,
|
|
@@ -3185,12 +3578,21 @@ export {
|
|
|
3185
3578
|
skillInvocationStartedEventSchema,
|
|
3186
3579
|
skillPhaseTransitionEventSchema,
|
|
3187
3580
|
skillTriggerCandidateEventSchema,
|
|
3581
|
+
storeAliasSchema,
|
|
3188
3582
|
storeAwareEntrySchema,
|
|
3583
|
+
storeCountersPath,
|
|
3189
3584
|
storeCountersSchema,
|
|
3585
|
+
storeHasProject,
|
|
3190
3586
|
storeIdentitySchema,
|
|
3191
3587
|
storeKnowledgeTypeDir,
|
|
3588
|
+
storeMountGroup,
|
|
3589
|
+
storeMountNameSchema,
|
|
3590
|
+
storeMountSubPath,
|
|
3591
|
+
storeProjectSchema,
|
|
3592
|
+
storeProjectsFileSchema,
|
|
3192
3593
|
storeReadSetSchema,
|
|
3193
3594
|
storeRelativePath,
|
|
3595
|
+
storeRelativePathForMount,
|
|
3194
3596
|
storeResolveInputSchema,
|
|
3195
3597
|
storeResolverWarningCodeSchema,
|
|
3196
3598
|
storeResolverWarningSchema,
|
|
@@ -3200,6 +3602,7 @@ export {
|
|
|
3200
3602
|
uidSchema,
|
|
3201
3603
|
withDerivedAgentsMetaNodeDefaults,
|
|
3202
3604
|
writeBindingsSnapshot,
|
|
3605
|
+
writeRouteSchema,
|
|
3203
3606
|
writeTargetSchema,
|
|
3204
3607
|
writtenToStoreSchema,
|
|
3205
3608
|
zhCNMessages
|