@fenglimg/fabric-shared 2.0.0 → 2.0.1

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/index.js CHANGED
@@ -1,3 +1,12 @@
1
+ import {
2
+ BOOTSTRAP_CANONICAL,
3
+ BOOTSTRAP_MARKER_BEGIN,
4
+ BOOTSTRAP_MARKER_END,
5
+ BOOTSTRAP_REGEX,
6
+ LEGACY_KB_MARKER_BEGIN,
7
+ LEGACY_KB_MARKER_END,
8
+ LEGACY_KB_REGEX
9
+ } from "./chunk-LTDB2UDN.js";
1
10
  import {
2
11
  PROTECTED_TOKENS,
3
12
  createTranslator,
@@ -5,29 +14,37 @@ import {
5
14
  detectNodeLocale,
6
15
  enMessages,
7
16
  normalizeLocale,
17
+ resolveFabricLocale,
8
18
  zhCNMessages
9
- } from "./chunk-U2SR2M4L.js";
19
+ } from "./chunk-7CX32MYL.js";
10
20
  import {
11
21
  FabExtractKnowledgeInputSchema,
12
22
  FabExtractKnowledgeInputShape,
13
23
  FabExtractKnowledgeOutputSchema,
14
24
  FabReviewInputSchema,
25
+ FabReviewInputShape,
15
26
  FabReviewOutputSchema,
27
+ FabReviewOutputShape,
16
28
  KNOWLEDGE_TYPE_CODES,
17
29
  KnowledgeEntryFrontmatterSchema,
18
30
  KnowledgeTypeSchema,
19
31
  LayerSchema,
20
32
  MaturitySchema,
33
+ ONBOARD_SLOT_NAMES,
34
+ ONBOARD_SLOT_TOTAL,
21
35
  PROPOSED_REASON_DESCRIPTIONS,
22
36
  ProposedReasonSchema,
23
37
  StableIdSchema,
24
38
  annotateIntentRequestSchema,
39
+ archiveScanAnnotations,
40
+ archiveScanInputSchema,
41
+ archiveScanOutputSchema,
42
+ citeContractMetricsSchema,
43
+ citeCoverageReportSchema,
44
+ citeLayerTypeBreakdownSchema,
25
45
  fabExtractKnowledgeAnnotations,
26
46
  fabReviewAnnotations,
27
47
  formatKnowledgeId,
28
- getKnowledgeAnnotations,
29
- getKnowledgeInputSchema,
30
- getKnowledgeOutputSchema,
31
48
  historyStateQuerySchema,
32
49
  humanLockApproveRequestSchema,
33
50
  humanLockFileParamsSchema,
@@ -36,19 +53,30 @@ import {
36
53
  knowledgeSectionsOutputSchema,
37
54
  ledgerQuerySchema,
38
55
  ledgerSourceSchema,
56
+ onboardSlotSchema,
39
57
  parseKnowledgeId,
40
58
  planContextAnnotations,
41
59
  planContextHintNarrowEntrySchema,
42
60
  planContextHintOutputSchema,
43
61
  planContextInputSchema,
44
62
  planContextOutputSchema,
63
+ recallAnnotations,
64
+ recallInputSchema,
65
+ recallOutputSchema,
45
66
  structuredWarningSchema
46
- } from "./chunk-VQDCDCJA.js";
67
+ } from "./chunk-WVPDH4BF.js";
47
68
  import "./chunk-LXNCAKJZ.js";
48
69
 
49
70
  // src/schemas/agents-meta.ts
50
71
  import { z } from "zod";
51
72
  var FABRIC_AGENTS_PREFIX = ".fabric/agents/";
73
+ var KNOWLEDGE_TYPE_SINGULAR_TO_PLURAL = {
74
+ model: "models",
75
+ decision: "decisions",
76
+ guideline: "guidelines",
77
+ pitfall: "pitfalls",
78
+ process: "processes"
79
+ };
52
80
  var AGENTS_META_LAYERS = ["L0", "L1", "L2"];
53
81
  var AGENTS_META_TOPOLOGY_TYPES = ["mirror", "cross-cutting", "domain", "local", "global"];
54
82
  var AGENTS_META_IDENTITY_SOURCES = ["declared", "derived"];
@@ -64,7 +92,15 @@ var ruleDescriptionSchema = z.object({
64
92
  entities: z.array(z.string()).optional(),
65
93
  // v2.0 knowledge entry fields (TASK-002 schemas). All optional for backward compat.
66
94
  id: z.string().optional(),
67
- knowledge_type: z.enum(["model", "decision", "guideline", "pitfall", "process"]).optional(),
95
+ // rc.31 NEW-1: forward-compat for legacy on-disk agents.meta.json carrying
96
+ // singular knowledge_type values (model/decision/guideline/pitfall/process).
97
+ // Normalize to canonical plural form before enum validation so doctor and
98
+ // plan-context-hint can load pre-rc.28 meta files without aborting. Disk
99
+ // gets rewritten to plural on next reconcile (via knowledge-meta-builder).
100
+ knowledge_type: z.preprocess(
101
+ (value) => typeof value === "string" && value in KNOWLEDGE_TYPE_SINGULAR_TO_PLURAL ? KNOWLEDGE_TYPE_SINGULAR_TO_PLURAL[value] : value,
102
+ z.enum(["models", "decisions", "guidelines", "pitfalls", "processes"])
103
+ ).optional(),
68
104
  maturity: z.enum(["draft", "verified", "proven"]).optional(),
69
105
  knowledge_layer: z.enum(["personal", "team"]).optional(),
70
106
  layer_reason: z.string().optional(),
@@ -134,8 +170,7 @@ function withDerivedAgentsMetaNodeDefaults(node) {
134
170
  const identitySource = isKnowledgeEntry ? "declared" : deriveAgentsMetaIdentitySource(node);
135
171
  return {
136
172
  ...node,
137
- layer: node.layer ?? node.level ?? deriveAgentsMetaLayer(node.file),
138
- level: node.level ?? node.layer ?? deriveAgentsMetaLayer(node.file),
173
+ level: node.level ?? deriveAgentsMetaLayer(node.file),
139
174
  topology_type: node.topology_type ?? deriveAgentsMetaTopologyType(node.file),
140
175
  stable_id: stableId,
141
176
  identity_source: identitySource
@@ -164,7 +199,7 @@ function defaultAgentsMetaCounters() {
164
199
  }
165
200
  function deriveAgentsMetaStableId(file) {
166
201
  const normalized = normalizePath(file);
167
- if (normalized === "AGENTS.md" || normalized === ".fabric/bootstrap/README.md") {
202
+ if (normalized === "AGENTS.md") {
168
203
  return "bootstrap";
169
204
  }
170
205
  return getDepthSource(normalized).replace(/\.md$/u, "");
@@ -181,7 +216,7 @@ function deriveAgentsMetaIdentitySource(node) {
181
216
  }
182
217
  function deriveAgentsMetaLayer(file) {
183
218
  const normalized = normalizePath(file);
184
- if (normalized === "AGENTS.md" || normalized === ".fabric/bootstrap/README.md") {
219
+ if (normalized === "AGENTS.md") {
185
220
  return "L0";
186
221
  }
187
222
  if (hasCrossCuttingSegment(normalized)) {
@@ -304,19 +339,30 @@ var mcpPayloadLimitsSchema = z5.object({
304
339
  warnBytes: z5.number().int().positive().optional(),
305
340
  hardBytes: z5.number().int().positive().optional()
306
341
  }).optional();
307
- var knowledgeLanguageSchema = z5.enum(["match-existing", "zh-CN", "en"]);
342
+ var selectionTokenTtlMsSchema = z5.number().int().min(3e4).max(36e5);
343
+ var fabricLanguageSchema = z5.enum([
344
+ "match-existing",
345
+ "zh-CN",
346
+ "en",
347
+ "zh-CN-hybrid"
348
+ ]);
308
349
  var defaultLayerFilterSchema = z5.enum(["team", "personal", "both"]);
309
350
  var fabricConfigSchema = z5.object({
310
351
  clientPaths: clientPathsSchema.optional(),
311
- externalFixturePath: z5.string().optional(),
352
+ // rc.17 (R-cut): the dev/test fixture-path config field was removed
353
+ // end-to-end. The `EXTERNAL_FIXTURE_PATH` env var is now the sole source
354
+ // consumed by `resolveDevMode()`. No z.preprocess alias — pre-rc.17
355
+ // fabric-config.json files carrying the field will be silently dropped by
356
+ // the lenient root parser (no .strict() at root). Pre-user clean-slate per
357
+ // memory/feedback_clean_slate.md; mirrors the rc.12 hard-rename precedent
358
+ // documented above.
312
359
  scanIgnores: z5.array(z5.string()).optional(),
313
- auditMode: auditModeSchema.optional(),
314
360
  audit_mode: auditModeSchema.optional(),
315
361
  mcpPayloadLimits: mcpPayloadLimitsSchema,
316
362
  // Backward-compat: both fields are optional with defaults so existing
317
363
  // fabric-config.json files (pre-grill-followup) parse unchanged. The default
318
364
  // values themselves are load-bearing — see docs/data-schema.md.
319
- knowledge_language: knowledgeLanguageSchema.optional().default("match-existing"),
365
+ fabric_language: fabricLanguageSchema.optional().default("match-existing"),
320
366
  default_layer_filter: defaultLayerFilterSchema.optional().default("both"),
321
367
  // Cooldown for the fabric-hint Stop hook (formerly archive-hint, renamed in
322
368
  // rc.5 TASK-010). After ANY of the three signals (archive / review / import)
@@ -371,159 +417,496 @@ var fabricConfigSchema = z5.object({
371
417
  // the user doesn't run doctor. Default 7 keeps the reminder weekly at
372
418
  // worst — pairing 14d trigger + 7d cooldown means at most ~2 reminders
373
419
  // per month for a workspace that ignores them.
374
- maintenance_hint_cooldown_days: z5.number().int().positive().optional().default(7)
420
+ maintenance_hint_cooldown_days: z5.number().int().positive().optional().default(7),
421
+ // rc.9+ (skill-contract-fix B1): first-run import window in months. The
422
+ // `fabric-import` skill scans this many months of git history on the very
423
+ // first invocation (when no prior `import_run_completed` event exists).
424
+ // Default 60 (~5 years) captures the bulk of a mature repo's signal in
425
+ // one pass; small / fresh repos can lower to 12-24 with no loss.
426
+ import_window_first_run_months: z5.number().int().min(1).optional().default(60),
427
+ // rc.9+ (skill-contract-fix B1): rerun import window in months. After
428
+ // the first successful import, subsequent runs only scan this many
429
+ // recent months — assumed everything older has already been crystallized
430
+ // into pending or canonical knowledge. Default 2 keeps incremental cost
431
+ // low; raise to 6 if the workspace pauses fabric-import for long stretches.
432
+ import_window_rerun_months: z5.number().int().min(1).optional().default(2),
433
+ // rc.9+ (skill-contract-fix B1): hard cap on pending entries produced
434
+ // per fabric-import invocation. Prevents one run from dumping hundreds
435
+ // of proposals when a backfill window is wide open. Default 10 matches
436
+ // the rule-of-thumb "human can triage ~10 pending entries in one
437
+ // review pass." Range 1-50.
438
+ import_max_pending_per_run: z5.number().int().min(1).max(50).optional().default(10),
439
+ // rc.9+ (skill-contract-fix B1): hard cap on commits scanned per
440
+ // fabric-import invocation. Bounds runtime on monorepos with high
441
+ // commit velocity. Default 500 covers ~2 months of typical churn;
442
+ // range 50-2000. Hitting the cap mid-window is logged but non-fatal.
443
+ import_max_commits_scan: z5.number().int().min(50).max(2e3).optional().default(500),
444
+ // rc.9+ (skill-contract-fix B1): canonical-node count above which
445
+ // fabric-import's pre-flight should warn / suggest review instead of
446
+ // proceeding. A workspace with 50+ canonical entries usually benefits
447
+ // more from `fabric-review` to consolidate than from importing more.
448
+ // Default 50; raise to 100+ for large polyglot repos.
449
+ import_skip_canonical_threshold: z5.number().int().positive().optional().default(50),
450
+ // rc.9+ (skill-contract-fix B1): max candidate entries surfaced per
451
+ // fabric-archive batch (one invocation of the skill). Pagination knob
452
+ // for the archive UI flow. Default 8 keeps each batch reviewable in
453
+ // one sitting; raise for large repos with high archive throughput.
454
+ archive_max_candidates_per_batch: z5.number().int().positive().optional().default(8),
455
+ // rc.9+ (skill-contract-fix B1): max recently-touched paths included
456
+ // in fabric-archive's "relevant context" lookup. Limits the size of
457
+ // the path-relevance digest the skill emits when ranking candidates.
458
+ // Default 20; large repos with deep directory fan-out can raise to
459
+ // 50+ if archive candidates feel under-contextualized.
460
+ archive_max_recent_paths: z5.number().int().positive().optional().default(20),
461
+ // rc.9+ (skill-contract-fix B1): max prior fabric-archive sessions
462
+ // summarised in the digest the skill loads on start. Prevents the
463
+ // digest from ballooning past the model context budget on workspaces
464
+ // that have archived repeatedly. Default 10; lower if context pressure
465
+ // bites, raise if you want longer-range archive trend visibility.
466
+ archive_digest_max_sessions: z5.number().int().positive().optional().default(10),
467
+ // rc.9+ (skill-contract-fix B1): max review results returned per
468
+ // topic when `fabric-review` clusters pending entries. Pagination
469
+ // knob analogous to archive_max_candidates_per_batch but scoped to
470
+ // each topic cluster. Default 8; raise to 15-20 for large repos
471
+ // where each topic legitimately groups many pending entries.
472
+ review_topic_result_cap: z5.number().int().positive().optional().default(8),
473
+ // rc.9+ (skill-contract-fix B1): age threshold (in days) above which
474
+ // a pending entry is considered "stale" by fabric-review and surfaced
475
+ // for explicit resolve-or-drop decision. Default 14; tighter than the
476
+ // 7d Signal-B trigger because review specifically targets the long
477
+ // tail. Large repos with slower cadence can raise to 30.
478
+ review_stale_pending_days: z5.number().int().positive().optional().default(14),
479
+ // v2.0.0-rc.34 TASK-05: reverse-unarchive opt-in. When true, callers of the
480
+ // `unarchiveKnowledge` primitive (and any future doctor auto-detect lint built
481
+ // on top) will execute the file move + ledger emit. When false (default),
482
+ // the same callers MUST short-circuit before any mutation — the primitive is
483
+ // shipped but inert until explicitly enabled. Opt-in posture mirrors the
484
+ // archive-flow precedent: destructive-ish file moves stay behind a flag.
485
+ reverse_unarchive_enabled: z5.boolean().optional().default(false),
486
+ // v2.0.0-rc.34 TASK-05: forces `unarchiveKnowledge` into dry-run mode even
487
+ // when called with `options.dryRun=false`. Lets operators preview a
488
+ // restoration pass before flipping `reverse_unarchive_enabled` to true.
489
+ reverse_unarchive_dry_run: z5.boolean().optional().default(false),
490
+ // v2.0.0-rc.34 TASK-06: long-session cite-policy evict window in user-prompt
491
+ // turns. UserPromptSubmit hook (Claude Code only) maintains a per-session
492
+ // counter and re-injects the cite contract reminder via
493
+ // hookSpecificOutput.additionalContext when `turn_count % interval === 0`.
494
+ // Default 0 = OFF (opt-in). Recommend 10-20 for active sessions; 5 for
495
+ // high-contract-criticality projects. Other strategies (time-based,
496
+ // token-budget) deferred to rc.35 per plan locked-decisions 2026-05-26.
497
+ cite_evict_interval: z5.number().int().min(0).optional().default(0),
498
+ // v2.0.0-rc.22 Scope A T3: sliding-window retention (in days) for the
499
+ // event ledger rotation primitive (`rotateEventLedgerIfNeeded`). Lines
500
+ // whose `ts` is older than `now - fabric_event_retention_days * 86_400_000`
501
+ // are partitioned into `.fabric/events.archive/events-rotated-YYYY-MM-DD.jsonl`.
502
+ // Locked to 7/30/90 — three operator-friendly preset windows. Default 30
503
+ // is applied at the consumer site (rotateEventLedgerIfNeeded), so this
504
+ // field stays `.optional()` without a `.default()` to keep the schema
505
+ // surface honest: absence means "use the library default", not "schema
506
+ // default of 30 was injected." 7 = ~tight, 30 = balanced, 90 = forensic.
507
+ // Mirrors cite-policy precedent of locking enum-style numeric tunables
508
+ // to a small literal set (vs free `.positive()`) to prevent fat-finger
509
+ // misconfig.
510
+ fabric_event_retention_days: z5.union([z5.literal(7), z5.literal(30), z5.literal(90)]).optional(),
511
+ // v2.0.0-rc.23 TASK-014 (F8c): onboard slot opt-out list. Tracks slot
512
+ // names the user explicitly dismissed during fabric-archive's first-run
513
+ // onboard phase (or via `fabric config dismiss-slot <slot>`). Dismissed
514
+ // slots are excluded from `fabric onboard-coverage`'s `missing` set and the
515
+ // doctor `Onboard coverage` advisory's recompute, so the user is never
516
+ // re-prompted for slots they consciously declined.
517
+ //
518
+ // Re-opening a dismissed slot requires `fabric config onboard-reset <slot>`
519
+ // — a deliberate two-command UX to keep the dismiss intent reversible
520
+ // but never silently undone. Schema is intentionally `z.array(z.string())`
521
+ // rather than `z.array(onboardSlotSchema)` so historical configs survive
522
+ // a slot rename without a Zod parse error; downstream consumers
523
+ // intersect against ONBOARD_SLOT_NAMES at read time.
524
+ //
525
+ // Default `[]` keeps the field optional on existing configs — fresh
526
+ // installs land with no opt-outs.
527
+ onboard_slots_opted_out: z5.array(z5.string()).optional().default([]),
528
+ // v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the broad SessionStart hint
529
+ // banner emitted by knowledge-hint-broad.cjs. After plan-context-hint returns
530
+ // its full broad-scoped index, the hook slices the entries to this many
531
+ // before grouping/truncation rendering — keeps the banner from scrolling off
532
+ // screen on well-seeded repos (Werewolf-class projects routinely surface 40+
533
+ // broad entries which buried the actually-relevant top hits). Default 8 is
534
+ // calibrated against the rc.32 eval baseline (cite-coverage 3.1%): the
535
+ // banner needs to fit in ~1 screenful so the agent actually reads it.
536
+ // Range 1..50; values above 20 effectively disable the cap because the
537
+ // TRUNCATION_THRESHOLD=12 grouped-render kicks in. Mirrors the rc.7 T7 +
538
+ // archive_max_* pattern of externalizing previously-hardcoded thresholds.
539
+ hint_broad_top_k: z5.number().int().min(1).max(50).optional().default(8),
540
+ // v2.0.0-rc.37 NEW-16: durable per-signal dismiss for the fabric-hint Stop
541
+ // hook nudges. Any signal type listed here is suppressed at emit time across
542
+ // all sessions (the session-scoped sibling lives in a .fabric/.cache sidecar
543
+ // written on request). Mirrors the cite_evict_interval=0 opt-out convention —
544
+ // a knob for an existing surface, not a new feature. Unknown types ignored.
545
+ hint_dismiss_signals: z5.array(z5.enum(["archive", "review", "import", "maintenance"])).optional(),
546
+ // v2.0.0-rc.33 W2-1 (P0-9): TopK upper bound for the narrow PreToolUse hint
547
+ // emitted by knowledge-hint-narrow.cjs. After filtering to entries whose
548
+ // `relevance_scope === "narrow"` (rc.27 TASK-005 audit §2.5 fix), the hook
549
+ // slices to this many before the E3 emit-gate / renderSummary pipeline.
550
+ // Default 5 keeps each per-Edit hint terse — five lines max so the agent's
551
+ // working memory is not displaced by an unwieldy banner. Range 1..20.
552
+ hint_narrow_top_k: z5.number().int().min(1).max(20).optional().default(5),
553
+ // v2.0.0-rc.33 W2-1 (P0-9): per-file dedup window (in PreToolUse turns) for
554
+ // the narrow hint. Same (file_path, stable_id) tuple stays silent for this
555
+ // many turns even when the E3 cross-session cache would otherwise re-emit.
556
+ // Closes the rc.32 eval finding that a single hot file (e.g. werewolf
557
+ // GameRoom.tsx edited 30 times in a row) re-fired the same narrow hint
558
+ // each time, training the agent to ignore it. Default 5; range 1..50.
559
+ // Storage: .fabric/.cache/narrow-dedup-window.json — distinct from session-
560
+ // hints cache so a window-only suppression does not poison cross-session
561
+ // dedupe semantics.
562
+ hint_narrow_dedup_window_turns: z5.number().int().min(1).max(50).optional().default(5),
563
+ // v2.0.0-rc.33 W2-5 (P1-8): cooldown between broad SessionStart hint emits,
564
+ // in hours. Distinct from the archive_hint_cooldown_hours that gates the
565
+ // fabric-hint Stop hook — knowledge-hint-broad re-fires on every
566
+ // SessionStart by default (compact / clear / new-window), which on long
567
+ // sessions becomes redundant noise. Setting to 1 means "emit the broad
568
+ // menu at most once per hour"; 0 means "no cooldown, current behavior."
569
+ // Range 0..168 (one week). Stored alongside fabric-hint's cooldown cache
570
+ // under a distinct knowledge-hint-broad key.
571
+ hint_broad_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
572
+ // v2.0.0-rc.33 W2-5 (P1-8): cooldown for the narrow PreToolUse hint.
573
+ // Same shape as hint_broad_cooldown_hours but applies to per-Edit hint
574
+ // re-emission across the cooldown window — independent of E3 session-
575
+ // hints dedupe. Default 0 preserves rc.32 behavior; set to e.g. 1 to
576
+ // throttle hint frequency during rapid-fire editing sprints. Range
577
+ // 0..168 (one week).
578
+ hint_narrow_cooldown_hours: z5.number().int().min(0).max(168).optional().default(0),
579
+ // v2.0.0-rc.33 W4-B3 (T5 P2): per-maturity inactivity thresholds (days)
580
+ // driving orphan_demote. Hardcoded at stable=90/endorsed=30/draft=14 in
581
+ // rc.32; chatty workspaces want them tighter, slow ones want them looser.
582
+ // Each field optional; absent → defaults inside doctor.ts apply. Ranges
583
+ // chosen so a typo can't accidentally disable the lint (min 1).
584
+ orphan_demote_stable_days: z5.number().int().min(1).max(3650).optional(),
585
+ orphan_demote_endorsed_days: z5.number().int().min(1).max(3650).optional(),
586
+ orphan_demote_draft_days: z5.number().int().min(1).max(3650).optional(),
587
+ // v2.0.0-rc.33 W4-A3 (T4 P2): per-entry summary truncation length used by
588
+ // knowledge-hint-{broad,narrow}.cjs. Hard-coded at 80 chars in rc.32 — too
589
+ // short for entries with parameterized summaries (e.g. "Use bcrypt with
590
+ // cost=12 for password hashing"), too long for terse pitfalls. Range 40..240;
591
+ // default 80 preserves rc.32 behavior. Both hooks read the same key so the
592
+ // banner styling stays consistent across SessionStart + PreToolUse.
593
+ hint_summary_max_len: z5.number().int().min(40).max(240).optional().default(80),
594
+ // v2.0.0-rc.33 W2-6 (P0-7 + P0-8): when true, knowledge-hint hooks emit
595
+ // their banners as `hookSpecificOutput.additionalContext` JSON on stdout
596
+ // (per Claude Code PreToolUse hook contract — see
597
+ // https://docs.claude.com/en/docs/claude-code/hooks#preToolUse), so the
598
+ // agent receives them in-context instead of as stderr breadcrumbs the
599
+ // user may not surface to the model. Default true reflects the rc.33 cite-
600
+ // coverage focus (rc.32 baseline 3.1% → primary cause: reminders never
601
+ // entered model context). Set false to revert to legacy stderr-only mode
602
+ // for hosts that don't honor the JSON contract.
603
+ hint_reminder_to_context: z5.boolean().optional().default(true),
604
+ // v2.0.0-rc.29 TASK-008 (BUG-F3): selection-token TTL override. The
605
+ // `fab_plan_context` MCP tool hands clients a `selection_token` whose default
606
+ // 5-minute lifetime (`SELECTION_TOKEN_TTL_MS` at
607
+ // packages/server/src/services/plan-context.ts:91) was hard-coded and could
608
+ // not be tuned for slow review cycles. Operators on long-running sessions
609
+ // (manual paste-and-review flows, debugger pauses, etc.) reported tokens
610
+ // expiring mid-review. Override here; absence means "use the library default
611
+ // of 5*60*1000 ms." Range 30s..1h keeps the value useful — below 30s the
612
+ // token expires before MCP round-trips finish; above 1h it stops being a
613
+ // meaningful liveness signal for the plan-context cache.
614
+ //
615
+ // The single-field schema is exported separately (`selectionTokenTtlMsSchema`)
616
+ // so the server-side per-field reader can validate without re-running the
617
+ // whole fabricConfigSchema on every plan_context call — that lets a corrupt
618
+ // unrelated field stay isolated from the hot read path.
619
+ selection_token_ttl_ms: selectionTokenTtlMsSchema.optional()
375
620
  });
376
621
 
377
- // src/schemas/forensic-report.ts
622
+ // src/schemas/fabric-config-introspect.ts
378
623
  import { z as z6 } from "zod";
379
- var forensicCodeSampleSchema = z6.object({
380
- path: z6.string(),
381
- lines: z6.string(),
382
- snippet: z6.string(),
383
- pattern_hint: z6.string()
384
- });
385
- var forensicEvidenceAnchorSchema = z6.object({
386
- file: z6.string(),
387
- line: z6.string(),
388
- snippet: z6.string()
389
- });
390
- var forensicAssertionCoverageSchema = z6.object({
391
- ratio: z6.number().min(0).max(1),
392
- total: z6.number().int().nonnegative(),
393
- matched: z6.number().int().nonnegative(),
394
- co_occurring_patterns: z6.array(z6.string())
395
- });
396
- var forensicAssertionSchema = z6.object({
397
- type: z6.enum(["framework", "pattern", "invariant", "domain"]),
398
- statement: z6.string(),
399
- confidence: z6.enum(["HIGH", "MEDIUM", "LOW"]),
400
- evidence: z6.array(forensicEvidenceAnchorSchema),
624
+ var positiveIntSchema = z6.coerce.number().int().positive();
625
+ function makePositiveIntField(key, defaultValue) {
626
+ return {
627
+ key,
628
+ group: "B_hint_threshold",
629
+ widget: "text",
630
+ label_i18n_key: `cli.config.fields.${key}.label`,
631
+ description_i18n_key: `cli.config.fields.${key}.description`,
632
+ default: defaultValue,
633
+ validate(raw) {
634
+ const trimmed = raw.trim();
635
+ if (trimmed === "") {
636
+ return { ok: false, error: "Value is required (positive integer)." };
637
+ }
638
+ const parsed = positiveIntSchema.safeParse(trimmed);
639
+ if (!parsed.success) {
640
+ return {
641
+ ok: false,
642
+ error: "Must be a positive integer (e.g. 1, 12, 24)."
643
+ };
644
+ }
645
+ return { ok: true, value: parsed.data };
646
+ },
647
+ format_for_display(value) {
648
+ if (typeof value === "number") return String(value);
649
+ if (value === void 0 || value === null) return String(defaultValue);
650
+ return String(value);
651
+ }
652
+ };
653
+ }
654
+ function makeEnumField(key, group, enumValues, defaultValue) {
655
+ return {
656
+ key,
657
+ group,
658
+ widget: "select",
659
+ label_i18n_key: `cli.config.fields.${key}.label`,
660
+ description_i18n_key: `cli.config.fields.${key}.description`,
661
+ default: defaultValue,
662
+ enum_values: enumValues,
663
+ validate(raw) {
664
+ const trimmed = raw.trim();
665
+ if (!enumValues.includes(trimmed)) {
666
+ return {
667
+ ok: false,
668
+ error: `Must be one of: ${enumValues.join(", ")}.`
669
+ };
670
+ }
671
+ return { ok: true, value: trimmed };
672
+ },
673
+ format_for_display(value) {
674
+ if (typeof value === "string" && enumValues.includes(value)) return value;
675
+ if (value === void 0 || value === null) return defaultValue;
676
+ return String(value);
677
+ }
678
+ };
679
+ }
680
+ var SCHEMA_DEFAULTS = fabricConfigSchema.parse({});
681
+ function pickNumberDefault(key) {
682
+ const v = SCHEMA_DEFAULTS[key];
683
+ if (typeof v !== "number") {
684
+ throw new Error(
685
+ `fabric-config-introspect: expected numeric default for ${String(key)}, got ${typeof v}`
686
+ );
687
+ }
688
+ return v;
689
+ }
690
+ function pickStringDefault(key) {
691
+ const v = SCHEMA_DEFAULTS[key];
692
+ if (typeof v !== "string") {
693
+ throw new Error(
694
+ `fabric-config-introspect: expected string default for ${String(key)}, got ${typeof v}`
695
+ );
696
+ }
697
+ return v;
698
+ }
699
+ var AUDIT_MODE_PANEL_DEFAULT = "warn";
700
+ function getPanelFields() {
701
+ return PANEL_FIELDS;
702
+ }
703
+ function getPanelFieldByKey(key) {
704
+ return PANEL_FIELDS.find((f) => f.key === key);
705
+ }
706
+ var PANEL_FIELDS = [
707
+ // --- Group A: Locale (2) ---
708
+ makeEnumField(
709
+ "fabric_language",
710
+ "A_locale",
711
+ fabricLanguageSchema.options,
712
+ pickStringDefault("fabric_language")
713
+ ),
714
+ makeEnumField(
715
+ "default_layer_filter",
716
+ "A_locale",
717
+ defaultLayerFilterSchema.options,
718
+ pickStringDefault("default_layer_filter")
719
+ ),
720
+ // --- Group B: Hint thresholds (8 — see leading docstring for the
721
+ // 7-vs-8 reconciliation; archive_edit_threshold is the 8th key) ---
722
+ makePositiveIntField("archive_hint_hours", pickNumberDefault("archive_hint_hours")),
723
+ makePositiveIntField(
724
+ "archive_hint_cooldown_hours",
725
+ pickNumberDefault("archive_hint_cooldown_hours")
726
+ ),
727
+ makePositiveIntField(
728
+ "archive_edit_threshold",
729
+ pickNumberDefault("archive_edit_threshold")
730
+ ),
731
+ makePositiveIntField(
732
+ "underseed_node_threshold",
733
+ pickNumberDefault("underseed_node_threshold")
734
+ ),
735
+ makePositiveIntField(
736
+ "review_hint_pending_count",
737
+ pickNumberDefault("review_hint_pending_count")
738
+ ),
739
+ makePositiveIntField(
740
+ "review_hint_pending_age_days",
741
+ pickNumberDefault("review_hint_pending_age_days")
742
+ ),
743
+ makePositiveIntField(
744
+ "maintenance_hint_days",
745
+ pickNumberDefault("maintenance_hint_days")
746
+ ),
747
+ makePositiveIntField(
748
+ "maintenance_hint_cooldown_days",
749
+ pickNumberDefault("maintenance_hint_cooldown_days")
750
+ ),
751
+ // --- Group C: Audit (1) ---
752
+ makeEnumField(
753
+ "audit_mode",
754
+ "C_audit",
755
+ auditModeSchema.options,
756
+ AUDIT_MODE_PANEL_DEFAULT
757
+ )
758
+ ];
759
+
760
+ // src/schemas/forensic-report.ts
761
+ import { z as z7 } from "zod";
762
+ var forensicCodeSampleSchema = z7.object({
763
+ path: z7.string(),
764
+ lines: z7.string(),
765
+ snippet: z7.string(),
766
+ pattern_hint: z7.string()
767
+ });
768
+ var forensicEvidenceAnchorSchema = z7.object({
769
+ file: z7.string(),
770
+ line: z7.string(),
771
+ snippet: z7.string()
772
+ });
773
+ var forensicAssertionCoverageSchema = z7.object({
774
+ ratio: z7.number().min(0).max(1),
775
+ total: z7.number().int().nonnegative(),
776
+ matched: z7.number().int().nonnegative(),
777
+ co_occurring_patterns: z7.array(z7.string())
778
+ });
779
+ var forensicAssertionSchema = z7.object({
780
+ type: z7.enum(["framework", "pattern", "invariant", "domain"]),
781
+ statement: z7.string(),
782
+ confidence: z7.enum(["HIGH", "MEDIUM", "LOW"]),
783
+ evidence: z7.array(forensicEvidenceAnchorSchema),
401
784
  coverage: forensicAssertionCoverageSchema,
402
- proposed_rule: z6.string().optional(),
403
- alternatives: z6.array(z6.string()).optional()
404
- });
405
- var forensicTopologySchema = z6.object({
406
- total_files: z6.number().int().nonnegative(),
407
- by_ext: z6.record(z6.number().int().nonnegative()),
408
- key_dirs: z6.array(z6.string()),
409
- max_depth: z6.number().int().nonnegative()
410
- });
411
- var forensicEntryPointSchema = z6.object({
412
- path: z6.string(),
413
- reason: z6.string(),
414
- size_bytes: z6.number().int().nonnegative().optional()
415
- });
416
- var forensicFrameworkSchema = z6.object({
417
- kind: z6.string(),
418
- version: z6.string(),
419
- subkind: z6.string(),
420
- evidence: z6.array(z6.string())
421
- });
422
- var forensicReadmeSchema = z6.object({
423
- quality: z6.enum(["missing", "stub", "ok"]),
424
- line_count: z6.number().int().nonnegative(),
425
- has_contributing: z6.boolean()
426
- });
427
- var candidateFileEntrySchema = z6.object({
428
- path: z6.string(),
429
- family: z6.enum(["entry", "component", "config", "test", "domain"]),
430
- rationale: z6.string()
431
- });
432
- var forensicSamplingBudgetSchema = z6.object({
433
- max_files: z6.literal(15),
434
- max_lines_per_file: z6.literal(100)
435
- });
436
- var forensicReportSchema = z6.object({
437
- version: z6.string(),
438
- generated_at: z6.string(),
439
- generated_by: z6.string(),
440
- target: z6.string(),
441
- project_name: z6.string(),
785
+ proposed_rule: z7.string().optional(),
786
+ alternatives: z7.array(z7.string()).optional()
787
+ });
788
+ var forensicTopologySchema = z7.object({
789
+ total_files: z7.number().int().nonnegative(),
790
+ by_ext: z7.record(z7.number().int().nonnegative()),
791
+ key_dirs: z7.array(z7.string()),
792
+ max_depth: z7.number().int().nonnegative()
793
+ });
794
+ var forensicEntryPointSchema = z7.object({
795
+ path: z7.string(),
796
+ reason: z7.string(),
797
+ size_bytes: z7.number().int().nonnegative().optional()
798
+ });
799
+ var forensicFrameworkSchema = z7.object({
800
+ kind: z7.string(),
801
+ version: z7.string(),
802
+ subkind: z7.string(),
803
+ evidence: z7.array(z7.string())
804
+ });
805
+ var forensicReadmeSchema = z7.object({
806
+ quality: z7.enum(["missing", "stub", "ok"]),
807
+ line_count: z7.number().int().nonnegative(),
808
+ has_contributing: z7.boolean()
809
+ });
810
+ var candidateFileEntrySchema = z7.object({
811
+ path: z7.string(),
812
+ family: z7.enum(["entry", "component", "config", "test", "domain"]),
813
+ rationale: z7.string()
814
+ });
815
+ var forensicSamplingBudgetSchema = z7.object({
816
+ max_files: z7.literal(15),
817
+ max_lines_per_file: z7.literal(100)
818
+ });
819
+ var forensicReportSchema = z7.object({
820
+ version: z7.string(),
821
+ generated_at: z7.string(),
822
+ generated_by: z7.string(),
823
+ target: z7.string(),
824
+ project_name: z7.string(),
442
825
  framework: forensicFrameworkSchema,
443
826
  topology: forensicTopologySchema,
444
- entry_points: z6.array(forensicEntryPointSchema),
445
- code_samples: z6.array(forensicCodeSampleSchema),
446
- assertions: z6.array(forensicAssertionSchema),
447
- candidate_files: z6.array(candidateFileEntrySchema),
827
+ entry_points: z7.array(forensicEntryPointSchema),
828
+ code_samples: z7.array(forensicCodeSampleSchema),
829
+ assertions: z7.array(forensicAssertionSchema),
830
+ candidate_files: z7.array(candidateFileEntrySchema),
448
831
  sampling_budget: forensicSamplingBudgetSchema,
449
832
  readme: forensicReadmeSchema,
450
- recommendations_for_skill: z6.array(z6.string()).optional()
833
+ recommendations_for_skill: z7.array(z7.string()).optional()
451
834
  });
452
835
 
453
836
  // src/schemas/init-context.ts
454
- import { z as z7 } from "zod";
455
- var initContextFrameworkSchema = z7.object({
456
- kind: z7.string(),
457
- version: z7.string(),
458
- subkind: z7.string()
837
+ import { z as z8 } from "zod";
838
+ var initContextFrameworkSchema = z8.object({
839
+ kind: z8.string(),
840
+ version: z8.string(),
841
+ subkind: z8.string()
459
842
  });
460
- var initContextInvariantConfidenceSnapshotSchema = z7.object({
461
- confidence: z7.enum(["HIGH", "MEDIUM", "LOW"]),
462
- evidence_refs: z7.array(z7.string())
843
+ var initContextInvariantConfidenceSnapshotSchema = z8.object({
844
+ confidence: z8.enum(["HIGH", "MEDIUM", "LOW"]),
845
+ evidence_refs: z8.array(z8.string())
463
846
  });
464
- var initContextSourceEvidenceSchema = z7.object({
465
- file: z7.string(),
466
- lines: z7.string()
847
+ var initContextSourceEvidenceSchema = z8.object({
848
+ file: z8.string(),
849
+ lines: z8.string()
467
850
  });
468
- var initContextInvariantSchema = z7.object({
469
- type: z7.enum(["ban", "require", "protect"]),
470
- rule: z7.string(),
471
- rationale: z7.string().optional(),
851
+ var initContextInvariantSchema = z8.object({
852
+ type: z8.enum(["ban", "require", "protect"]),
853
+ rule: z8.string(),
854
+ rationale: z8.string().optional(),
472
855
  confidence_snapshot: initContextInvariantConfidenceSnapshotSchema.optional(),
473
- source_evidence: z7.array(initContextSourceEvidenceSchema).optional()
474
- });
475
- var initContextDomainGroupSchema = z7.object({
476
- name: z7.string(),
477
- paths: z7.array(z7.string()),
478
- summary: z7.string().optional(),
479
- topology_type: z7.enum(["mirror", "cross-cutting"]).optional(),
480
- target_path: z7.string().optional()
481
- });
482
- var initContextInterviewTrailEntrySchema = z7.object({
483
- phase: z7.string(),
484
- question: z7.string(),
485
- answer: z7.string(),
486
- presentation: z7.string().optional(),
487
- user_corrections: z7.array(z7.string()).optional()
488
- });
489
- var initContextSchema = z7.object({
856
+ source_evidence: z8.array(initContextSourceEvidenceSchema).optional()
857
+ });
858
+ var initContextDomainGroupSchema = z8.object({
859
+ name: z8.string(),
860
+ paths: z8.array(z8.string()),
861
+ summary: z8.string().optional(),
862
+ topology_type: z8.enum(["mirror", "cross-cutting"]).optional(),
863
+ target_path: z8.string().optional()
864
+ });
865
+ var initContextInterviewTrailEntrySchema = z8.object({
866
+ phase: z8.string(),
867
+ question: z8.string(),
868
+ answer: z8.string(),
869
+ presentation: z8.string().optional(),
870
+ user_corrections: z8.array(z8.string()).optional()
871
+ });
872
+ var initContextSchema = z8.object({
490
873
  framework: initContextFrameworkSchema,
491
- architecture_patterns: z7.array(z7.string()),
492
- invariants: z7.array(initContextInvariantSchema),
493
- domain_groups: z7.array(initContextDomainGroupSchema),
494
- interview_trail: z7.array(initContextInterviewTrailEntrySchema),
495
- forensic_ref: z7.string()
874
+ architecture_patterns: z8.array(z8.string()),
875
+ invariants: z8.array(initContextInvariantSchema),
876
+ domain_groups: z8.array(initContextDomainGroupSchema),
877
+ interview_trail: z8.array(initContextInterviewTrailEntrySchema),
878
+ forensic_ref: z8.string()
496
879
  });
497
880
 
498
881
  // src/schemas/events.ts
499
- import { z as z8 } from "zod";
500
- var metaUpdatedEventSchema = z8.object({
501
- type: z8.literal("meta:updated"),
882
+ import { z as z9 } from "zod";
883
+ var metaUpdatedEventSchema = z9.object({
884
+ type: z9.literal("meta:updated"),
502
885
  payload: agentsMetaSchema
503
886
  });
504
- var lockDriftEventSchema = z8.object({
505
- type: z8.literal("lock:drift"),
506
- payload: z8.object({
507
- locked: z8.array(humanLockEntrySchema),
508
- drifted: z8.array(humanLockEntrySchema)
887
+ var lockDriftEventSchema = z9.object({
888
+ type: z9.literal("lock:drift"),
889
+ payload: z9.object({
890
+ locked: z9.array(humanLockEntrySchema),
891
+ drifted: z9.array(humanLockEntrySchema)
509
892
  })
510
893
  });
511
- var lockApprovedEventSchema = z8.object({
512
- type: z8.literal("lock:approved"),
513
- payload: z8.object({
514
- locked: z8.array(humanLockEntrySchema),
515
- approved: z8.array(humanLockEntrySchema)
894
+ var lockApprovedEventSchema = z9.object({
895
+ type: z9.literal("lock:approved"),
896
+ payload: z9.object({
897
+ locked: z9.array(humanLockEntrySchema),
898
+ approved: z9.array(humanLockEntrySchema)
516
899
  })
517
900
  });
518
- var ledgerAppendedEventSchema = z8.object({
519
- type: z8.literal("ledger:appended"),
901
+ var ledgerAppendedEventSchema = z9.object({
902
+ type: z9.literal("ledger:appended"),
520
903
  payload: ledgerEntrySchema
521
904
  });
522
- var driftDetectedEventSchema = z8.object({
523
- type: z8.literal("drift:detected"),
905
+ var driftDetectedEventSchema = z9.object({
906
+ type: z9.literal("drift:detected"),
524
907
  payload: forensicReportSchema
525
908
  });
526
- var fabricEventSchema = z8.discriminatedUnion("type", [
909
+ var fabricEventSchema = z9.discriminatedUnion("type", [
527
910
  metaUpdatedEventSchema,
528
911
  lockDriftEventSchema,
529
912
  lockApprovedEventSchema,
@@ -532,271 +915,426 @@ var fabricEventSchema = z8.discriminatedUnion("type", [
532
915
  ]);
533
916
 
534
917
  // src/schemas/event-ledger.ts
535
- import { z as z9 } from "zod";
918
+ import { z as z10 } from "zod";
536
919
  var eventLedgerEnvelopeSchema = {
537
- kind: z9.literal("fabric-event"),
538
- id: z9.string(),
539
- ts: z9.number().int().nonnegative(),
540
- schema_version: z9.literal(1),
541
- correlation_id: z9.string().optional(),
542
- session_id: z9.string().optional()
920
+ kind: z10.literal("fabric-event"),
921
+ id: z10.string(),
922
+ ts: z10.number().int().nonnegative(),
923
+ schema_version: z10.literal(1),
924
+ correlation_id: z10.string().optional(),
925
+ session_id: z10.string().optional()
543
926
  };
544
- var stringRecordSchema = z9.record(z9.string());
545
- var knowledgeContextPlannedEventSchema = z9.object({
546
- ...eventLedgerEnvelopeSchema,
547
- event_type: z9.literal("knowledge_context_planned"),
548
- target_paths: z9.array(z9.string()),
549
- required_stable_ids: z9.array(z9.string()),
550
- ai_selectable_stable_ids: z9.array(z9.string()),
551
- final_stable_ids: z9.array(z9.string()),
552
- selection_token: z9.string().optional(),
553
- client_hash: z9.string().optional(),
554
- intent: z9.string().optional(),
555
- known_tech: z9.array(z9.string()).optional(),
556
- diagnostics: z9.array(z9.unknown()).optional()
557
- });
558
- var knowledgeSelectionEventSchema = z9.object({
559
- ...eventLedgerEnvelopeSchema,
560
- event_type: z9.literal("knowledge_selection"),
561
- selection_token: z9.string(),
562
- target_paths: z9.array(z9.string()),
563
- required_stable_ids: z9.array(z9.string()),
564
- ai_selectable_stable_ids: z9.array(z9.string()),
565
- ai_selected_stable_ids: z9.array(z9.string()),
566
- final_stable_ids: z9.array(z9.string()),
927
+ var stringRecordSchema = z10.record(z10.string());
928
+ var knowledgeContextPlannedEventSchema = z10.object({
929
+ ...eventLedgerEnvelopeSchema,
930
+ event_type: z10.literal("knowledge_context_planned"),
931
+ target_paths: z10.array(z10.string()),
932
+ required_stable_ids: z10.array(z10.string()),
933
+ ai_selectable_stable_ids: z10.array(z10.string()),
934
+ final_stable_ids: z10.array(z10.string()),
935
+ selection_token: z10.string().optional(),
936
+ client_hash: z10.string().optional(),
937
+ intent: z10.string().optional(),
938
+ known_tech: z10.array(z10.string()).optional(),
939
+ diagnostics: z10.array(z10.unknown()).optional()
940
+ });
941
+ var knowledgeSelectionEventSchema = z10.object({
942
+ ...eventLedgerEnvelopeSchema,
943
+ event_type: z10.literal("knowledge_selection"),
944
+ selection_token: z10.string(),
945
+ target_paths: z10.array(z10.string()),
946
+ required_stable_ids: z10.array(z10.string()),
947
+ ai_selectable_stable_ids: z10.array(z10.string()),
948
+ ai_selected_stable_ids: z10.array(z10.string()),
949
+ final_stable_ids: z10.array(z10.string()),
567
950
  ai_selection_reasons: stringRecordSchema,
568
- rejected_stable_ids: z9.array(z9.string()),
569
- ignored_stable_ids: z9.array(z9.string())
570
- });
571
- var knowledgeSectionsFetchedEventSchema = z9.object({
572
- ...eventLedgerEnvelopeSchema,
573
- event_type: z9.literal("knowledge_sections_fetched"),
574
- selection_token: z9.string(),
575
- target_paths: z9.array(z9.string()).optional(),
576
- requested_sections: z9.array(z9.string()),
577
- final_stable_ids: z9.array(z9.string()),
578
- ai_selected_stable_ids: z9.array(z9.string()),
579
- diagnostics: z9.array(z9.unknown()).optional()
580
- });
581
- var editIntentCheckedEventSchema = z9.object({
582
- ...eventLedgerEnvelopeSchema,
583
- event_type: z9.literal("edit_intent_checked"),
584
- path: z9.string(),
585
- compliant: z9.boolean(),
586
- intent: z9.string(),
587
- ledger_entry_id: z9.string(),
588
- ledger_source: z9.enum(["ai", "human"]).optional(),
589
- commit_sha: z9.string().optional(),
590
- parent_sha: z9.string().optional(),
591
- parent_ledger_entry_id: z9.string().optional(),
592
- diff_stat: z9.string().optional(),
593
- annotation: z9.string().optional(),
594
- matched_rule_context_ts: z9.number().int().nonnegative().nullable(),
595
- window_ms: z9.number().int().nonnegative()
596
- });
597
- var knowledgeDriftDetectedEventSchema = z9.object({
598
- ...eventLedgerEnvelopeSchema,
599
- event_type: z9.literal("knowledge_drift_detected"),
600
- revision: z9.string().optional(),
601
- drifted_stable_ids: z9.array(z9.string()),
602
- missing_files: z9.array(z9.string()),
603
- stale_files: z9.array(z9.string()),
604
- details: z9.array(
605
- z9.object({
606
- file: z9.string(),
607
- stable_id: z9.string(),
608
- expected_hash: z9.string(),
609
- actual_hash: z9.string().nullable()
951
+ rejected_stable_ids: z10.array(z10.string()),
952
+ ignored_stable_ids: z10.array(z10.string())
953
+ });
954
+ var knowledgeSectionsFetchedEventSchema = z10.object({
955
+ ...eventLedgerEnvelopeSchema,
956
+ event_type: z10.literal("knowledge_sections_fetched"),
957
+ selection_token: z10.string(),
958
+ target_paths: z10.array(z10.string()).optional(),
959
+ requested_sections: z10.array(z10.string()),
960
+ final_stable_ids: z10.array(z10.string()),
961
+ ai_selected_stable_ids: z10.array(z10.string()),
962
+ diagnostics: z10.array(z10.unknown()).optional()
963
+ });
964
+ var editIntentCheckedEventSchema = z10.object({
965
+ ...eventLedgerEnvelopeSchema,
966
+ event_type: z10.literal("edit_intent_checked"),
967
+ path: z10.string(),
968
+ compliant: z10.boolean(),
969
+ intent: z10.string(),
970
+ ledger_entry_id: z10.string(),
971
+ // rc.35 TASK-07 (P0-2): add "hook" — emitted by the PreToolUse narrow hook
972
+ // for every Edit/Write/MultiEdit fire so cite-coverage doctor metrics see
973
+ // actual edit signals (previously editsTouched was permanently 0 because
974
+ // no production caller of appendLedgerEntry existed).
975
+ ledger_source: z10.enum(["ai", "human", "hook"]).optional(),
976
+ commit_sha: z10.string().optional(),
977
+ parent_sha: z10.string().optional(),
978
+ parent_ledger_entry_id: z10.string().optional(),
979
+ diff_stat: z10.string().optional(),
980
+ annotation: z10.string().optional(),
981
+ matched_rule_context_ts: z10.number().int().nonnegative().nullable(),
982
+ window_ms: z10.number().int().nonnegative()
983
+ });
984
+ var knowledgeDriftDetectedEventSchema = z10.object({
985
+ ...eventLedgerEnvelopeSchema,
986
+ event_type: z10.literal("knowledge_drift_detected"),
987
+ revision: z10.string().optional(),
988
+ drifted_stable_ids: z10.array(z10.string()),
989
+ missing_files: z10.array(z10.string()),
990
+ stale_files: z10.array(z10.string()),
991
+ details: z10.array(
992
+ z10.object({
993
+ file: z10.string(),
994
+ stable_id: z10.string(),
995
+ expected_hash: z10.string(),
996
+ actual_hash: z10.string().nullable()
610
997
  })
611
998
  ).optional()
612
999
  });
613
- var mcpEventLedgerEventSchema = z9.object({
1000
+ var mcpEventLedgerEventSchema = z10.object({
1001
+ ...eventLedgerEnvelopeSchema,
1002
+ event_type: z10.literal("mcp_event"),
1003
+ mcp_event_id: z10.string(),
1004
+ stream_id: z10.string(),
1005
+ message: z10.unknown()
1006
+ });
1007
+ var reapplyCompletedEventSchema = z10.object({
1008
+ ...eventLedgerEnvelopeSchema,
1009
+ event_type: z10.literal("reapply_completed"),
1010
+ preserved_ledger: z10.boolean(),
1011
+ preserved_meta: z10.boolean(),
1012
+ rules_count: z10.number().int().nonnegative()
1013
+ });
1014
+ var installDiffAppliedEventSchema = z10.object({
1015
+ ...eventLedgerEnvelopeSchema,
1016
+ event_type: z10.literal("install_diff_applied"),
1017
+ applied: z10.array(z10.string()),
1018
+ canonical: z10.array(z10.string()),
1019
+ drifted: z10.array(z10.string())
1020
+ });
1021
+ var eventLedgerTruncatedEventSchema = z10.object({
1022
+ ...eventLedgerEnvelopeSchema,
1023
+ event_type: z10.literal("event_ledger_truncated"),
1024
+ byte_offset: z10.number().int().nonnegative(),
1025
+ byte_length: z10.number().int().nonnegative(),
1026
+ corrupted_path: z10.string()
1027
+ });
1028
+ var mcpConfigMigratedEventSchema = z10.object({
1029
+ ...eventLedgerEnvelopeSchema,
1030
+ event_type: z10.literal("mcp_config_migrated"),
1031
+ source: z10.literal("doctor_fix"),
1032
+ removed_from: z10.string()
1033
+ });
1034
+ var bootstrapMarkerMigratedEventSchema = z10.object({
1035
+ ...eventLedgerEnvelopeSchema,
1036
+ event_type: z10.literal("bootstrap_marker_migrated"),
1037
+ path: z10.string(),
1038
+ migrated_count: z10.number().int().nonnegative(),
1039
+ legacy_marker: z10.literal("fabric:knowledge-base"),
1040
+ new_marker: z10.literal("fabric:bootstrap"),
1041
+ timestamp: z10.string()
1042
+ });
1043
+ var metaReconciledOnStartupEventSchema = z10.object({
1044
+ ...eventLedgerEnvelopeSchema,
1045
+ event_type: z10.literal("meta_reconciled_on_startup"),
1046
+ reconciled_files: z10.array(z10.string()),
1047
+ duration_ms: z10.number().int().nonnegative(),
1048
+ source: z10.literal("reconcileKnowledge")
1049
+ });
1050
+ var metaReconciledEventSchema = z10.object({
1051
+ ...eventLedgerEnvelopeSchema,
1052
+ event_type: z10.literal("meta_reconciled"),
1053
+ reconciled_files: z10.array(z10.string()),
1054
+ duration_ms: z10.number().int().nonnegative(),
1055
+ // v2.0.0-rc.23 TASK-005 (a-B): added `auto-heal-description` trigger so the
1056
+ // read-path plan_context handler can drive a full reconcile when it detects
1057
+ // any node carrying `description === undefined` (legacy meta drift that the
1058
+ // revision-hash gate cannot catch — a missing description doesn't move the
1059
+ // revision). Symmetric to rc.22 D2 read-side auto-heal but covers the
1060
+ // description-undefined case which the revision drift gate misses.
1061
+ // v2.0.0-rc.27 TASK-001 (§2.9): `post-approve` / `post-modify` added so
1062
+ // `fab_review` write-actions can flush newly-promoted entries into
1063
+ // `agents.meta.json.nodes[id]` synchronously — without this the new entry
1064
+ // remains description-less until the next plan_context auto-heal.
1065
+ // v2.0.0-rc.29 TASK-005 (BUG-G1): `auto-heal-after-drift` added so
1066
+ // `ensureKnowledgeFresh` hot-path can chain a paired reconcile (closing the
1067
+ // drift→heal gap) when the caller opts in via `autoHealOnDrift: true`.
1068
+ trigger: z10.enum([
1069
+ "doctor",
1070
+ "manual",
1071
+ "auto-heal-description",
1072
+ "auto-heal-after-drift",
1073
+ "post-approve",
1074
+ "post-modify"
1075
+ ]),
1076
+ source: z10.literal("reconcileKnowledge"),
1077
+ // v2.0.0-rc.22 TASK-014 (Scope E): set when reconcileKnowledge forced a
1078
+ // writeKnowledgeMeta on revision drift alone (no per-file content drift).
1079
+ // Distinguishes top-level schema/revision repair from the standard per-file
1080
+ // drift path. Optional so existing emitters stay unchanged.
1081
+ force_write_reason: z10.enum(["revision_drift"]).optional()
1082
+ });
1083
+ var claudeSkillPathMigratedEventSchema = z10.object({
1084
+ ...eventLedgerEnvelopeSchema,
1085
+ event_type: z10.literal("claude_skill_path_migrated"),
1086
+ from: z10.string(),
1087
+ to: z10.string()
1088
+ });
1089
+ var claudeHookPathMigratedEventSchema = z10.object({
1090
+ ...eventLedgerEnvelopeSchema,
1091
+ event_type: z10.literal("claude_hook_path_migrated"),
1092
+ from: z10.string(),
1093
+ to: z10.string()
1094
+ });
1095
+ var codexSkillPathMigratedEventSchema = z10.object({
1096
+ ...eventLedgerEnvelopeSchema,
1097
+ event_type: z10.literal("codex_skill_path_migrated"),
1098
+ from: z10.string(),
1099
+ to: z10.string()
1100
+ });
1101
+ var initScanCompletedEventSchema = z10.object({
1102
+ ...eventLedgerEnvelopeSchema,
1103
+ event_type: z10.literal("init_scan_completed"),
1104
+ written_stable_ids: z10.array(z10.string()),
1105
+ duration_ms: z10.number().int().nonnegative(),
1106
+ source: z10.enum(["init", "scan", "doctor_fix", "doctor-rescan"]).optional()
1107
+ });
1108
+ var knowledgeProposedEventSchema = z10.object({
614
1109
  ...eventLedgerEnvelopeSchema,
615
- event_type: z9.literal("mcp_event"),
616
- mcp_event_id: z9.string(),
617
- stream_id: z9.string(),
618
- message: z9.unknown()
1110
+ event_type: z10.literal("knowledge_proposed"),
1111
+ stable_id: z10.string().optional(),
1112
+ timestamp: z10.string().datetime(),
1113
+ reason: z10.string().optional()
619
1114
  });
620
- var reapplyCompletedEventSchema = z9.object({
1115
+ var knowledgePromoteStartedEventSchema = z10.object({
621
1116
  ...eventLedgerEnvelopeSchema,
622
- event_type: z9.literal("reapply_completed"),
623
- preserved_ledger: z9.boolean(),
624
- preserved_meta: z9.boolean(),
625
- rules_count: z9.number().int().nonnegative()
1117
+ event_type: z10.literal("knowledge_promote_started"),
1118
+ stable_id: z10.string().optional(),
1119
+ timestamp: z10.string().datetime(),
1120
+ reason: z10.string().optional()
626
1121
  });
627
- var eventLedgerTruncatedEventSchema = z9.object({
1122
+ var knowledgePromotedEventSchema = z10.object({
628
1123
  ...eventLedgerEnvelopeSchema,
629
- event_type: z9.literal("event_ledger_truncated"),
630
- byte_offset: z9.number().int().nonnegative(),
631
- byte_length: z9.number().int().nonnegative(),
632
- corrupted_path: z9.string()
1124
+ event_type: z10.literal("knowledge_promoted"),
1125
+ stable_id: z10.string().optional(),
1126
+ timestamp: z10.string().datetime(),
1127
+ reason: z10.string().optional()
633
1128
  });
634
- var mcpConfigMigratedEventSchema = z9.object({
1129
+ var knowledgePromoteFailedEventSchema = z10.object({
635
1130
  ...eventLedgerEnvelopeSchema,
636
- event_type: z9.literal("mcp_config_migrated"),
637
- source: z9.literal("doctor_fix"),
638
- removed_from: z9.string()
1131
+ event_type: z10.literal("knowledge_promote_failed"),
1132
+ stable_id: z10.string().optional(),
1133
+ timestamp: z10.string().datetime(),
1134
+ reason: z10.string()
639
1135
  });
640
- var metaReconciledOnStartupEventSchema = z9.object({
1136
+ var knowledgeLayerChangedEventSchema = z10.object({
641
1137
  ...eventLedgerEnvelopeSchema,
642
- event_type: z9.literal("meta_reconciled_on_startup"),
643
- reconciled_files: z9.array(z9.string()),
644
- duration_ms: z9.number().int().nonnegative(),
645
- source: z9.literal("reconcileKnowledge")
1138
+ event_type: z10.literal("knowledge_layer_changed"),
1139
+ stable_id: z10.string().optional(),
1140
+ timestamp: z10.string().datetime(),
1141
+ reason: z10.string().optional(),
1142
+ from_layer: z10.enum(["team", "personal"]),
1143
+ to_layer: z10.enum(["team", "personal"]),
1144
+ // v2.0.0-rc.37 NEW-24: record the pre-flip stable_id so downstream consumers
1145
+ // (fab_plan_context redirect surface, fab_get_knowledge_sections.redirect_to)
1146
+ // can map a stale caller-held id back to the post-flip canonical id without
1147
+ // requiring the caller to re-issue plan-context. Optional for forward-
1148
+ // compatibility with rc ≤36 events that never carried this field.
1149
+ previous_stable_id: z10.string().optional()
646
1150
  });
647
- var metaReconciledEventSchema = z9.object({
1151
+ var knowledgeIdRedirectEventSchema = z10.object({
648
1152
  ...eventLedgerEnvelopeSchema,
649
- event_type: z9.literal("meta_reconciled"),
650
- reconciled_files: z9.array(z9.string()),
651
- duration_ms: z9.number().int().nonnegative(),
652
- trigger: z9.enum(["doctor", "manual"]),
653
- source: z9.literal("reconcileKnowledge")
1153
+ event_type: z10.literal("knowledge_id_redirect"),
1154
+ timestamp: z10.string().datetime(),
1155
+ previous_stable_id: z10.string(),
1156
+ new_stable_id: z10.string(),
1157
+ reason: z10.string().optional()
654
1158
  });
655
- var claudeSkillPathMigratedEventSchema = z9.object({
1159
+ var knowledgeSlugRenamedEventSchema = z10.object({
656
1160
  ...eventLedgerEnvelopeSchema,
657
- event_type: z9.literal("claude_skill_path_migrated"),
658
- from: z9.string(),
659
- to: z9.string()
1161
+ event_type: z10.literal("knowledge_slug_renamed"),
1162
+ stable_id: z10.string().optional(),
1163
+ timestamp: z10.string().datetime(),
1164
+ reason: z10.string().optional(),
1165
+ from_slug: z10.string(),
1166
+ to_slug: z10.string()
660
1167
  });
661
- var claudeHookPathMigratedEventSchema = z9.object({
1168
+ var knowledgeDemotedEventSchema = z10.object({
662
1169
  ...eventLedgerEnvelopeSchema,
663
- event_type: z9.literal("claude_hook_path_migrated"),
664
- from: z9.string(),
665
- to: z9.string()
1170
+ event_type: z10.literal("knowledge_demoted"),
1171
+ stable_id: z10.string().optional(),
1172
+ timestamp: z10.string().datetime(),
1173
+ reason: z10.string().optional()
666
1174
  });
667
- var codexSkillPathMigratedEventSchema = z9.object({
1175
+ var knowledgeArchivedEventSchema = z10.object({
668
1176
  ...eventLedgerEnvelopeSchema,
669
- event_type: z9.literal("codex_skill_path_migrated"),
670
- from: z9.string(),
671
- to: z9.string()
1177
+ event_type: z10.literal("knowledge_archived"),
1178
+ stable_id: z10.string().optional(),
1179
+ timestamp: z10.string().datetime(),
1180
+ reason: z10.string().optional()
672
1181
  });
673
- var initScanCompletedEventSchema = z9.object({
1182
+ var knowledgeArchiveAttemptedEventSchema = z10.object({
674
1183
  ...eventLedgerEnvelopeSchema,
675
- event_type: z9.literal("init_scan_completed"),
676
- written_stable_ids: z9.array(z9.string()),
677
- duration_ms: z9.number().int().nonnegative(),
678
- source: z9.enum(["init", "scan", "doctor_fix"]).optional()
1184
+ event_type: z10.literal("knowledge_archive_attempted"),
1185
+ stable_id: z10.string().optional(),
1186
+ timestamp: z10.string().datetime(),
1187
+ reason: z10.string().optional()
679
1188
  });
680
- var knowledgeProposedEventSchema = z9.object({
1189
+ var knowledgeUnarchivedEventSchema = z10.object({
681
1190
  ...eventLedgerEnvelopeSchema,
682
- event_type: z9.literal("knowledge_proposed"),
683
- stable_id: z9.string().optional(),
684
- timestamp: z9.string().datetime(),
685
- reason: z9.string().optional()
1191
+ event_type: z10.literal("knowledge_unarchived"),
1192
+ stable_id: z10.string().optional(),
1193
+ timestamp: z10.string().datetime(),
1194
+ reason: z10.string().optional(),
1195
+ // Pre-move archive path (e.g. ".fabric/.archive/decisions/KT-D-0007--single-cjs-hook.md").
1196
+ archive_path: z10.string().optional(),
1197
+ // Post-move canonical path (e.g. ".fabric/knowledge/team/decisions/KT-D-0007--single-cjs-hook.md").
1198
+ restored_to: z10.string().optional()
686
1199
  });
687
- var knowledgePromoteStartedEventSchema = z9.object({
1200
+ var knowledgeDeferredEventSchema = z10.object({
688
1201
  ...eventLedgerEnvelopeSchema,
689
- event_type: z9.literal("knowledge_promote_started"),
690
- stable_id: z9.string().optional(),
691
- timestamp: z9.string().datetime(),
692
- reason: z9.string().optional()
1202
+ event_type: z10.literal("knowledge_deferred"),
1203
+ stable_id: z10.string().optional(),
1204
+ timestamp: z10.string().datetime(),
1205
+ reason: z10.string().optional(),
1206
+ until: z10.string().datetime().optional()
693
1207
  });
694
- var knowledgePromotedEventSchema = z9.object({
1208
+ var knowledgeRejectedEventSchema = z10.object({
695
1209
  ...eventLedgerEnvelopeSchema,
696
- event_type: z9.literal("knowledge_promoted"),
697
- stable_id: z9.string().optional(),
698
- timestamp: z9.string().datetime(),
699
- reason: z9.string().optional()
1210
+ event_type: z10.literal("knowledge_rejected"),
1211
+ stable_id: z10.string().optional(),
1212
+ timestamp: z10.string().datetime(),
1213
+ reason: z10.string()
700
1214
  });
701
- var knowledgePromoteFailedEventSchema = z9.object({
1215
+ var knowledgeConsumedEventSchema = z10.object({
702
1216
  ...eventLedgerEnvelopeSchema,
703
- event_type: z9.literal("knowledge_promote_failed"),
704
- stable_id: z9.string().optional(),
705
- timestamp: z9.string().datetime(),
706
- reason: z9.string()
1217
+ event_type: z10.literal("knowledge_consumed"),
1218
+ stable_id: z10.string(),
1219
+ consumed_at: z10.string().datetime(),
1220
+ client_hash: z10.string()
707
1221
  });
708
- var knowledgeLayerChangedEventSchema = z9.object({
1222
+ var knowledgeScopeDegradedEventSchema = z10.object({
709
1223
  ...eventLedgerEnvelopeSchema,
710
- event_type: z9.literal("knowledge_layer_changed"),
711
- stable_id: z9.string().optional(),
712
- timestamp: z9.string().datetime(),
713
- reason: z9.string().optional(),
714
- from_layer: z9.enum(["team", "personal"]),
715
- to_layer: z9.enum(["team", "personal"])
1224
+ event_type: z10.literal("knowledge_scope_degraded"),
1225
+ stable_id: z10.string(),
1226
+ timestamp: z10.string().datetime(),
1227
+ from_scope: z10.enum(["narrow", "broad"]),
1228
+ to_scope: z10.enum(["narrow", "broad"]),
1229
+ reason: z10.string()
716
1230
  });
717
- var knowledgeSlugRenamedEventSchema = z9.object({
1231
+ var doctorRunEventSchema = z10.object({
718
1232
  ...eventLedgerEnvelopeSchema,
719
- event_type: z9.literal("knowledge_slug_renamed"),
720
- stable_id: z9.string().optional(),
721
- timestamp: z9.string().datetime(),
722
- reason: z9.string().optional(),
723
- from_slug: z9.string(),
724
- to_slug: z9.string()
1233
+ event_type: z10.literal("doctor_run"),
1234
+ mode: z10.enum(["lint", "fix-knowledge"]),
1235
+ issues: z10.number().int().nonnegative(),
1236
+ mutations: z10.number().int().nonnegative().optional(),
1237
+ timestamp: z10.string().datetime()
725
1238
  });
726
- var knowledgeDemotedEventSchema = z9.object({
1239
+ var knowledgePathDangledEventSchema = z10.object({
727
1240
  ...eventLedgerEnvelopeSchema,
728
- event_type: z9.literal("knowledge_demoted"),
729
- stable_id: z9.string().optional(),
730
- timestamp: z9.string().datetime(),
731
- reason: z9.string().optional()
1241
+ event_type: z10.literal("knowledge_path_dangled"),
1242
+ stable_id: z10.string(),
1243
+ removed_glob: z10.string()
732
1244
  });
733
- var knowledgeArchivedEventSchema = z9.object({
1245
+ var relevanceMigrationRunEventSchema = z10.object({
734
1246
  ...eventLedgerEnvelopeSchema,
735
- event_type: z9.literal("knowledge_archived"),
736
- stable_id: z9.string().optional(),
737
- timestamp: z9.string().datetime(),
738
- reason: z9.string().optional()
1247
+ event_type: z10.literal("relevance_migration_run"),
1248
+ timestamp: z10.string().datetime(),
1249
+ scanned_count: z10.number().int().nonnegative(),
1250
+ touched_count: z10.number().int().nonnegative()
739
1251
  });
740
- var knowledgeArchiveAttemptedEventSchema = z9.object({
1252
+ var pendingAutoArchivedEventSchema = z10.object({
741
1253
  ...eventLedgerEnvelopeSchema,
742
- event_type: z9.literal("knowledge_archive_attempted"),
743
- stable_id: z9.string().optional(),
744
- timestamp: z9.string().datetime(),
745
- reason: z9.string().optional()
1254
+ event_type: z10.literal("pending_auto_archived"),
1255
+ pending_path: z10.string(),
1256
+ archived_to: z10.string(),
1257
+ reason: z10.string()
746
1258
  });
747
- var knowledgeDeferredEventSchema = z9.object({
1259
+ var assistantTurnObservedEventSchema = z10.object({
748
1260
  ...eventLedgerEnvelopeSchema,
749
- event_type: z9.literal("knowledge_deferred"),
750
- stable_id: z9.string().optional(),
751
- timestamp: z9.string().datetime(),
752
- reason: z9.string().optional(),
753
- until: z9.string().datetime().optional()
1261
+ event_type: z10.literal("assistant_turn_observed"),
1262
+ kb_line_raw: z10.string().nullable(),
1263
+ cite_ids: z10.array(z10.string()).default([]),
1264
+ cite_tags: z10.array(z10.enum(["planned", "recalled", "chained-from", "dismissed", "none"])).default([]),
1265
+ // v2.0.0-rc.24 TASK-01: per-cite contract commitments. Index-aligned with
1266
+ // cite_ids/cite_tags (commitments[i] belongs to cite_ids[i]). Each slot
1267
+ // carries `operators[]` (kind + glob target) or `skip_reason` when the cite
1268
+ // cannot be operator-ized. Old rc.20-rc.23 events naturally parse with an
1269
+ // empty array via `.default([])` and are excluded from contract-policy
1270
+ // audits by the marker-gate (see cite_contract_policy_activated below).
1271
+ // Mirrors the rc.20 cite_tags parallel-array evolution exactly.
1272
+ cite_commitments: z10.array(
1273
+ z10.object({
1274
+ operators: z10.array(
1275
+ z10.object({
1276
+ kind: z10.enum(["edit", "not_edit", "require", "forbid"]),
1277
+ target: z10.string()
1278
+ })
1279
+ ),
1280
+ skip_reason: z10.string().nullable()
1281
+ })
1282
+ ).default([]),
1283
+ client: z10.enum(["cc", "codex", "cursor"]).optional(),
1284
+ turn_id: z10.string(),
1285
+ envelope_index: z10.number().int().nonnegative().optional(),
1286
+ timestamp: z10.string().datetime()
754
1287
  });
755
- var knowledgeRejectedEventSchema = z9.object({
1288
+ var citePolicyActivatedEventSchema = z10.object({
756
1289
  ...eventLedgerEnvelopeSchema,
757
- event_type: z9.literal("knowledge_rejected"),
758
- stable_id: z9.string().optional(),
759
- timestamp: z9.string().datetime(),
760
- reason: z9.string()
1290
+ event_type: z10.literal("cite_policy_activated"),
1291
+ policy_version: z10.string(),
1292
+ timestamp: z10.string().datetime()
761
1293
  });
762
- var knowledgeConsumedEventSchema = z9.object({
1294
+ var citeContractPolicyActivatedEventSchema = z10.object({
763
1295
  ...eventLedgerEnvelopeSchema,
764
- event_type: z9.literal("knowledge_consumed"),
765
- stable_id: z9.string(),
766
- consumed_at: z9.string().datetime(),
767
- client_hash: z9.string()
1296
+ event_type: z10.literal("cite_contract_policy_activated")
768
1297
  });
769
- var knowledgeScopeDegradedEventSchema = z9.object({
1298
+ var eventsRotatedEventSchema = z10.object({
770
1299
  ...eventLedgerEnvelopeSchema,
771
- event_type: z9.literal("knowledge_scope_degraded"),
772
- stable_id: z9.string(),
773
- timestamp: z9.string().datetime(),
774
- from_scope: z9.enum(["narrow", "broad"]),
775
- to_scope: z9.enum(["narrow", "broad"]),
776
- reason: z9.string()
1300
+ event_type: z10.literal("events_rotated"),
1301
+ cutoff_ts: z10.string().datetime(),
1302
+ archived_count: z10.number().int().nonnegative(),
1303
+ kept_count: z10.number().int().nonnegative(),
1304
+ archive_path: z10.string()
777
1305
  });
778
- var doctorRunEventSchema = z9.object({
1306
+ var knowledgeMetaAutoHealedEventSchema = z10.object({
779
1307
  ...eventLedgerEnvelopeSchema,
780
- event_type: z9.literal("doctor_run"),
781
- mode: z9.enum(["lint", "apply-lint"]),
782
- issues: z9.number().int().nonnegative(),
783
- mutations: z9.number().int().nonnegative().optional(),
784
- timestamp: z9.string().datetime()
1308
+ event_type: z10.literal("knowledge_meta_auto_healed"),
1309
+ previous_revision_hash: z10.string(),
1310
+ revision_hash: z10.string(),
1311
+ trigger: z10.literal("read"),
1312
+ caller: z10.enum(["planContext", "getKnowledgeSections", "getKnowledge", "extractKnowledge"]).optional()
785
1313
  });
786
- var knowledgePathDangledEventSchema = z9.object({
1314
+ var serveLockClearedEventSchema = z10.object({
787
1315
  ...eventLedgerEnvelopeSchema,
788
- event_type: z9.literal("knowledge_path_dangled"),
789
- stable_id: z9.string(),
790
- removed_glob: z9.string()
1316
+ event_type: z10.literal("serve_lock_cleared"),
1317
+ pid: z10.number().int().nonnegative(),
1318
+ age_ms: z10.number().int().nonnegative(),
1319
+ timestamp: z10.string().datetime()
791
1320
  });
792
- var pendingAutoArchivedEventSchema = z9.object({
1321
+ var knowledgeEnrichedEventSchema = z10.object({
793
1322
  ...eventLedgerEnvelopeSchema,
794
- event_type: z9.literal("pending_auto_archived"),
795
- pending_path: z9.string(),
796
- archived_to: z9.string(),
797
- reason: z9.string()
1323
+ event_type: z10.literal("knowledge_enriched"),
1324
+ path: z10.string(),
1325
+ added_fields: z10.array(z10.enum(["intent_clues", "tech_stack", "impact", "must_read_if"])),
1326
+ mode: z10.enum(["auto", "preview", "readonly", "interactive"]),
1327
+ timestamp: z10.string().datetime()
798
1328
  });
799
- var eventLedgerEventSchema = z9.discriminatedUnion("event_type", [
1329
+ var sessionArchiveAttemptedEventSchema = z10.object({
1330
+ ...eventLedgerEnvelopeSchema,
1331
+ event_type: z10.literal("session_archive_attempted"),
1332
+ outcome: z10.enum(["proposed", "viability_failed", "user_dismissed", "skipped_no_signal"]),
1333
+ covered_through_ts: z10.number().int().nonnegative(),
1334
+ candidates_proposed: z10.number().int().nonnegative().default(0),
1335
+ knowledge_proposed_ids: z10.array(z10.string()).default([])
1336
+ });
1337
+ var eventLedgerEventSchema = z10.discriminatedUnion("event_type", [
800
1338
  knowledgeContextPlannedEventSchema,
801
1339
  knowledgeSelectionEventSchema,
802
1340
  knowledgeSectionsFetchedEventSchema,
@@ -804,8 +1342,12 @@ var eventLedgerEventSchema = z9.discriminatedUnion("event_type", [
804
1342
  knowledgeDriftDetectedEventSchema,
805
1343
  mcpEventLedgerEventSchema,
806
1344
  reapplyCompletedEventSchema,
1345
+ installDiffAppliedEventSchema,
807
1346
  eventLedgerTruncatedEventSchema,
808
1347
  mcpConfigMigratedEventSchema,
1348
+ // v2.0.0-rc.19 TASK-004: bootstrap_marker_migrated — one-time fabric:knowledge-base
1349
+ // → fabric:bootstrap marker rewrite emitted per file by `fabric doctor --fix`.
1350
+ bootstrapMarkerMigratedEventSchema,
809
1351
  metaReconciledOnStartupEventSchema,
810
1352
  metaReconciledEventSchema,
811
1353
  claudeSkillPathMigratedEventSchema,
@@ -818,10 +1360,14 @@ var eventLedgerEventSchema = z9.discriminatedUnion("event_type", [
818
1360
  knowledgePromotedEventSchema,
819
1361
  knowledgePromoteFailedEventSchema,
820
1362
  knowledgeLayerChangedEventSchema,
1363
+ // v2.0.0-rc.37 NEW-24: dedicated old→new stable_id mapping event
1364
+ knowledgeIdRedirectEventSchema,
821
1365
  knowledgeSlugRenamedEventSchema,
822
1366
  knowledgeDemotedEventSchema,
823
1367
  knowledgeArchivedEventSchema,
824
1368
  knowledgeArchiveAttemptedEventSchema,
1369
+ // v2.0.0-rc.34 TASK-05: reverse of knowledge_archived
1370
+ knowledgeUnarchivedEventSchema,
825
1371
  knowledgeDeferredEventSchema,
826
1372
  knowledgeRejectedEventSchema,
827
1373
  // v2.0 rc.5 TASK-014: knowledge_consumed (consumption tracking)
@@ -835,24 +1381,161 @@ var eventLedgerEventSchema = z9.discriminatedUnion("event_type", [
835
1381
  // #24 when a glob in relevance_paths resolves to zero filesystem matches.
836
1382
  knowledgePathDangledEventSchema,
837
1383
  // v2.0.0-rc.7 T10: doctor_run — emitted by `fabric doctor` to drive Signal D.
838
- doctorRunEventSchema
1384
+ doctorRunEventSchema,
1385
+ // v2.0.0-rc.9 TASK-003 (A3): relevance_migration_run — emitted by
1386
+ // `doctor --apply-lint` after the lint #26 frontmatter back-fill pass.
1387
+ relevanceMigrationRunEventSchema,
1388
+ // v2.0.0-rc.20 TASK-02: assistant_turn_observed — per-turn cite-policy
1389
+ // observation (raw KB: line text + parsed cite_ids/cite_tags + client).
1390
+ assistantTurnObservedEventSchema,
1391
+ // v2.0.0-rc.20 TASK-02: cite_policy_activated — session/policy-bump
1392
+ // marker recording when a given policy_version became active.
1393
+ citePolicyActivatedEventSchema,
1394
+ // v2.0.0-rc.24 TASK-01: cite_contract_policy_activated — drift-gated
1395
+ // idempotent marker opening the contract-policy audit window. Distinct
1396
+ // from cite_policy_activated so contract metrics get their own window.
1397
+ citeContractPolicyActivatedEventSchema,
1398
+ // v2.0.0-rc.22 Scope D T-D1: knowledge_meta_auto_healed — emitted by
1399
+ // loadActiveMeta when read-path drift triggers an in-place meta rebuild.
1400
+ knowledgeMetaAutoHealedEventSchema,
1401
+ // v2.0.0-rc.22 Scope A T3: events_rotated — emitted as the first line of
1402
+ // the post-rotation events.jsonl when sliding-window-by-age rotation moves
1403
+ // stale entries to events.archive/events-rotated-YYYY-MM-DD.jsonl.
1404
+ eventsRotatedEventSchema,
1405
+ // v2.0.0-rc.23 TASK-010 (e): serve_lock_cleared — emitted by
1406
+ // `fabric doctor --fix` when a stale `.fabric/.serve.lock` with a dead PID is
1407
+ // unlinked.
1408
+ serveLockClearedEventSchema,
1409
+ // v2.0.0-rc.23 TASK-007 (a-C2): knowledge_enriched — emitted by
1410
+ // `fabric doctor --enrich-descriptions` once per modified canonical knowledge
1411
+ // file when one or more of the four rc.23 description-grade frontmatter
1412
+ // fields is back-filled.
1413
+ knowledgeEnrichedEventSchema,
1414
+ // v2.0.0-rc.25 TASK-01: session_archive_attempted — emitted by the
1415
+ // fabric-archive skill at the end of every invocation. Drives Phase 0.0
1416
+ // cross-session digest, outcome-based rescan filter (skips user_dismissed),
1417
+ // covered_through_ts watermark, and `fabric doctor --archive-history`.
1418
+ sessionArchiveAttemptedEventSchema
839
1419
  ]);
1420
+
1421
+ // src/cite-line-parser.ts
1422
+ var ID_RE = /^K[TP]-[A-Z]+-\d+$/;
1423
+ var SENTINEL_RE = /^KB:\s*none\b\s*(?:\[[^\]]*\])?\s*$/i;
1424
+ var FULL_RE = /^KB:\s+(K[TP]-[A-Z]+-\d+(?:\s*,\s*K[TP]-[A-Z]+-\d+)*)(?:\s+\(([^)]*)\))?(?:\s+\[([^\]]+)\])?(?:\s+→\s*(.+))?\s*$/;
1425
+ var CHAINED_FROM_ID_RE = /chained-from\s+(K[TP]-[A-Z]+-\d+)/i;
1426
+ var ALLOWED_TAGS = /* @__PURE__ */ new Set([
1427
+ "planned",
1428
+ "recalled",
1429
+ "chained-from",
1430
+ "dismissed",
1431
+ "none"
1432
+ ]);
1433
+ function parseTag(rawTag) {
1434
+ if (!rawTag) return "none";
1435
+ const head = rawTag.trim().split(/[\s:]+/)[0].toLowerCase();
1436
+ return ALLOWED_TAGS.has(head) ? head : "none";
1437
+ }
1438
+ function parseContractTail(tail) {
1439
+ const result = { operators: [], skip_reason: null };
1440
+ if (!tail) return result;
1441
+ const tokens = tail.trim().split(/\s+/).filter((t) => t.length > 0);
1442
+ for (const token of tokens) {
1443
+ const skipMatch = token.match(/^skip:(.+)$/i);
1444
+ if (skipMatch) {
1445
+ if (result.skip_reason === null) result.skip_reason = skipMatch[1];
1446
+ continue;
1447
+ }
1448
+ const notEditMatch = token.match(/^!edit:(.+)$/i);
1449
+ if (notEditMatch) {
1450
+ result.operators.push({ kind: "not_edit", target: notEditMatch[1] });
1451
+ continue;
1452
+ }
1453
+ const opMatch = token.match(/^(edit|require|forbid):(.+)$/i);
1454
+ if (opMatch) {
1455
+ result.operators.push({
1456
+ kind: opMatch[1].toLowerCase(),
1457
+ target: opMatch[2]
1458
+ });
1459
+ }
1460
+ }
1461
+ return result;
1462
+ }
1463
+ function parseLine(line) {
1464
+ const trimmed = line.trim();
1465
+ if (trimmed.length === 0) return null;
1466
+ if (SENTINEL_RE.test(trimmed)) {
1467
+ return { ids: [], tag: "none", commitment: null };
1468
+ }
1469
+ const fullMatch = trimmed.match(FULL_RE);
1470
+ if (fullMatch) {
1471
+ const primaryIds = fullMatch[1].split(",").map((part) => part.trim()).filter((part) => part.length > 0);
1472
+ if (primaryIds.some((id) => !ID_RE.test(id))) return null;
1473
+ const rawTag = fullMatch[3];
1474
+ const tag = parseTag(rawTag);
1475
+ const chainedIds = [];
1476
+ if (rawTag !== void 0) {
1477
+ const chained = CHAINED_FROM_ID_RE.exec(rawTag);
1478
+ if (chained !== null && ID_RE.test(chained[1])) {
1479
+ chainedIds.push(chained[1]);
1480
+ }
1481
+ }
1482
+ return {
1483
+ ids: [...primaryIds, ...chainedIds],
1484
+ tag,
1485
+ commitment: parseContractTail(fullMatch[4])
1486
+ };
1487
+ }
1488
+ return null;
1489
+ }
1490
+ function parseCiteLine(raw) {
1491
+ const result = {
1492
+ cite_ids: [],
1493
+ cite_tags: [],
1494
+ cite_commitments: []
1495
+ };
1496
+ if (typeof raw !== "string") return result;
1497
+ for (const line of raw.split(/\r?\n/)) {
1498
+ const parsed = parseLine(line);
1499
+ if (!parsed) continue;
1500
+ result.cite_tags.push(parsed.tag);
1501
+ for (const id of parsed.ids) {
1502
+ result.cite_ids.push(id);
1503
+ }
1504
+ if (parsed.commitment !== null) {
1505
+ for (let i = 0; i < parsed.ids.length; i += 1) {
1506
+ result.cite_commitments.push(parsed.commitment);
1507
+ }
1508
+ }
1509
+ }
1510
+ return result;
1511
+ }
840
1512
  export {
841
1513
  AGENTS_META_IDENTITY_SOURCES,
842
1514
  AGENTS_META_LAYERS,
843
1515
  AGENTS_META_TOPOLOGY_TYPES,
844
1516
  AgentsMetaCountersSchema,
1517
+ BOOTSTRAP_CANONICAL,
1518
+ BOOTSTRAP_MARKER_BEGIN,
1519
+ BOOTSTRAP_MARKER_END,
1520
+ BOOTSTRAP_REGEX,
845
1521
  FabExtractKnowledgeInputSchema,
846
1522
  FabExtractKnowledgeInputShape,
847
1523
  FabExtractKnowledgeOutputSchema,
848
1524
  FabReviewInputSchema,
1525
+ FabReviewInputShape,
849
1526
  FabReviewOutputSchema,
1527
+ FabReviewOutputShape,
850
1528
  KNOWLEDGE_TEST_INDEX_SCHEMA_VERSION,
851
1529
  KNOWLEDGE_TYPE_CODES,
852
1530
  KnowledgeEntryFrontmatterSchema,
853
1531
  KnowledgeTypeSchema,
1532
+ LEGACY_KB_MARKER_BEGIN,
1533
+ LEGACY_KB_MARKER_END,
1534
+ LEGACY_KB_REGEX,
854
1535
  LayerSchema,
855
1536
  MaturitySchema,
1537
+ ONBOARD_SLOT_NAMES,
1538
+ ONBOARD_SLOT_TOTAL,
856
1539
  PROPOSED_REASON_DESCRIPTIONS,
857
1540
  PROTECTED_TOKENS,
858
1541
  ProposedReasonSchema,
@@ -865,8 +1548,18 @@ export {
865
1548
  aiLedgerEntrySchema,
866
1549
  allocateKnowledgeId,
867
1550
  annotateIntentRequestSchema,
1551
+ archiveScanAnnotations,
1552
+ archiveScanInputSchema,
1553
+ archiveScanOutputSchema,
1554
+ assistantTurnObservedEventSchema,
868
1555
  auditModeSchema,
1556
+ bootstrapMarkerMigratedEventSchema,
869
1557
  candidateFileEntrySchema,
1558
+ citeContractMetricsSchema,
1559
+ citeContractPolicyActivatedEventSchema,
1560
+ citeCoverageReportSchema,
1561
+ citeLayerTypeBreakdownSchema,
1562
+ citePolicyActivatedEventSchema,
870
1563
  claudeHookPathMigratedEventSchema,
871
1564
  claudeSkillPathMigratedEventSchema,
872
1565
  clientPathsSchema,
@@ -886,10 +1579,12 @@ export {
886
1579
  enMessages,
887
1580
  eventLedgerEventSchema,
888
1581
  eventLedgerTruncatedEventSchema,
1582
+ eventsRotatedEventSchema,
889
1583
  fabExtractKnowledgeAnnotations,
890
1584
  fabReviewAnnotations,
891
1585
  fabricConfigSchema,
892
1586
  fabricEventSchema,
1587
+ fabricLanguageSchema,
893
1588
  forensicAssertionCoverageSchema,
894
1589
  forensicAssertionSchema,
895
1590
  forensicCodeSampleSchema,
@@ -901,9 +1596,8 @@ export {
901
1596
  forensicSamplingBudgetSchema,
902
1597
  forensicTopologySchema,
903
1598
  formatKnowledgeId,
904
- getKnowledgeAnnotations,
905
- getKnowledgeInputSchema,
906
- getKnowledgeOutputSchema,
1599
+ getPanelFieldByKey,
1600
+ getPanelFields,
907
1601
  historyStateQuerySchema,
908
1602
  humanLedgerEntrySchema,
909
1603
  humanLockApproveRequestSchema,
@@ -918,6 +1612,7 @@ export {
918
1612
  initContextSchema,
919
1613
  initContextSourceEvidenceSchema,
920
1614
  initScanCompletedEventSchema,
1615
+ installDiffAppliedEventSchema,
921
1616
  isKnowledgeStableId,
922
1617
  knowledgeArchiveAttemptedEventSchema,
923
1618
  knowledgeArchivedEventSchema,
@@ -926,8 +1621,10 @@ export {
926
1621
  knowledgeDeferredEventSchema,
927
1622
  knowledgeDemotedEventSchema,
928
1623
  knowledgeDriftDetectedEventSchema,
929
- knowledgeLanguageSchema,
1624
+ knowledgeEnrichedEventSchema,
1625
+ knowledgeIdRedirectEventSchema,
930
1626
  knowledgeLayerChangedEventSchema,
1627
+ knowledgeMetaAutoHealedEventSchema,
931
1628
  knowledgePathDangledEventSchema,
932
1629
  knowledgePromoteFailedEventSchema,
933
1630
  knowledgePromoteStartedEventSchema,
@@ -944,6 +1641,7 @@ export {
944
1641
  knowledgeTestIndexSchema,
945
1642
  knowledgeTestLinkSchema,
946
1643
  knowledgeTestOrphanAnnotationSchema,
1644
+ knowledgeUnarchivedEventSchema,
947
1645
  ledgerAppendedEventSchema,
948
1646
  ledgerEntrySchema,
949
1647
  ledgerQuerySchema,
@@ -957,6 +1655,8 @@ export {
957
1655
  metaReconciledOnStartupEventSchema,
958
1656
  metaUpdatedEventSchema,
959
1657
  normalizeLocale,
1658
+ onboardSlotSchema,
1659
+ parseCiteLine,
960
1660
  parseKnowledgeId,
961
1661
  pendingAutoArchivedEventSchema,
962
1662
  planContextAnnotations,
@@ -965,8 +1665,16 @@ export {
965
1665
  planContextInputSchema,
966
1666
  planContextOutputSchema,
967
1667
  reapplyCompletedEventSchema,
1668
+ recallAnnotations,
1669
+ recallInputSchema,
1670
+ recallOutputSchema,
1671
+ relevanceMigrationRunEventSchema,
1672
+ resolveFabricLocale,
968
1673
  ruleDescriptionIndexItemSchema,
969
1674
  ruleDescriptionSchema,
1675
+ selectionTokenTtlMsSchema,
1676
+ serveLockClearedEventSchema,
1677
+ sessionArchiveAttemptedEventSchema,
970
1678
  structuredWarningSchema,
971
1679
  withDerivedAgentsMetaNodeDefaults,
972
1680
  zhCNMessages