facult 2.13.0 → 2.13.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -81,6 +81,18 @@ curl -fsSL https://github.com/hack-dance/fclt/releases/latest/download/fclt-inst
81
81
 
82
82
  Windows and manual installs can download binaries from the [latest release](https://github.com/hack-dance/fclt/releases/latest).
83
83
 
84
+ Check and repair local setup:
85
+
86
+ ```bash
87
+ fclt doctor --json
88
+ fclt doctor --repair
89
+ ```
90
+
91
+ `doctor --json` is read-only. `doctor --repair` is the self-heal path for legacy
92
+ state, broken canonical global guidance, missing review artifacts, and stale
93
+ local integration layout. Canonical repairs keep a backup under
94
+ `.ai/.facult/backups/doctor/`.
95
+
84
96
  Update an installed binary:
85
97
 
86
98
  ```bash
@@ -2,7 +2,16 @@
2
2
 
3
3
  This machine has a default Facult operating-model layer available.
4
4
 
5
- When work produces durable friction, weak verification, stale guidance, or a missing skill/tool capability, preserve that signal with `fclt ai writeback ...` when the target and scope are clear. When repeated writebacks or clearly missing capability point at a concrete improvement, use `fclt ai evolve ...` or the `capability-evolution` skill to make a reviewable proposal.
5
+ Default behavior:
6
+
7
+ - Treat meaningful work as a work unit: know the goal, acceptance criteria, required context, constraints, evidence, output artifact, verification path, and likely writeback target.
8
+ - Use the strongest practical feedback loop for the risk. Do not treat shallow success as proof when a better check is available.
9
+ - When work produces durable friction, weak verification, stale guidance, or a missing skill/tool capability, preserve that signal with `fclt ai writeback ...` when the target and scope are clear.
10
+ - Use `fclt ai evolve ...` or the `capability-evolution` skill only when repeated writebacks, a clearly missing capability, or a stale canonical asset point at a concrete improvement.
11
+ - Keep one-off preferences and speculative ideas out of evolution. Use writeback, notes, or task tracking instead.
12
+ - Use project scope for repo-specific workflow and global scope for reusable cross-project doctrine. Promote project capability only after evidence shows reuse.
13
+ - Use Linear or another task system for executable product/tooling work that needs ownership, priority, state, or delivery follow-through.
14
+ - Keep writeback/evolution runtime and review artifacts in the global `.ai` review tree; do not commit generated writeback queues or private review artifacts into project repos.
6
15
 
7
16
  For work-unit framing, read `@builtin/facult-operating-model/instructions/WORK_UNITS.md`.
8
17
  For composing refs, snippets, instructions, skills, agents, MCP, and automations as evolvable units, read `@builtin/facult-operating-model/instructions/CAPABILITY_COMPOSITION.md`.
@@ -20,3 +29,13 @@ Builtin specialist agents are available for:
20
29
  Builtin skills are available for:
21
30
  - capability evolution
22
31
  - project operating-layer design
32
+
33
+ Useful health and review commands:
34
+
35
+ ```bash
36
+ fclt doctor --json
37
+ fclt status --json
38
+ fclt ai writeback list
39
+ fclt ai writeback group --by asset
40
+ fclt ai evolve list
41
+ ```
package/bin/fclt.cjs CHANGED
@@ -15,6 +15,9 @@ const REPO_NAME = "fclt";
15
15
  const PACKAGE_NAME = "facult";
16
16
  const DOWNLOAD_RETRIES = 12;
17
17
  const DOWNLOAD_RETRY_DELAY_MS = 5000;
18
+ const ACTIVE_RUNTIME_WAIT_MS = 10_000;
19
+ const ACTIVE_RUNTIME_WAIT_INTERVAL_MS = 100;
20
+ const STALE_RUNTIME_TEMP_MS = 10 * 60 * 1000;
18
21
 
19
22
  function isHelpLikeArgs(args) {
20
23
  return (
@@ -80,70 +83,75 @@ async function main() {
80
83
  let installedBinaryThisRun = false;
81
84
 
82
85
  if (!(await fileExists(binaryPath))) {
83
- const packageManager = detectPackageManager();
84
- const hasSourceFallback = await canUseSourceFallback(sourceEntry);
85
- const incompleteCache = await hasIncompleteRuntimeCache({
86
+ await removeStaleRuntimeTemps({
86
87
  installDir,
87
88
  binaryName,
89
+ maxAgeMs: STALE_RUNTIME_TEMP_MS,
88
90
  });
91
+ const packageManager = detectPackageManager();
92
+ const hasSourceFallback = await canUseSourceFallback(sourceEntry);
89
93
 
90
- if (incompleteCache) {
91
- await removeIncompleteRuntimeTemps({ installDir, binaryName });
92
- }
93
-
94
- if (hasSourceFallback && (incompleteCache || isHelpLikeArgs(args))) {
94
+ if (hasSourceFallback && isHelpLikeArgs(args)) {
95
95
  return runSourceFallback({
96
96
  sourceEntry,
97
97
  version,
98
98
  packageManager,
99
- reason: new Error(
100
- incompleteCache
101
- ? "incomplete cached runtime download"
102
- : "runtime binary missing for help-like command"
103
- ),
99
+ reason: new Error("runtime binary missing for help-like command"),
104
100
  });
105
101
  }
106
102
 
107
- const tag = `v${version}`;
108
- const assetName = `${PACKAGE_NAME}-${version}-${resolved.platform}-${resolved.arch}${resolved.ext}`;
109
- const url = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${tag}/${assetName}`;
110
- const tmpPath = `${binaryPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
111
-
112
- try {
113
- await fsp.mkdir(installDir, { recursive: true });
114
- await downloadWithRetry(url, tmpPath, {
115
- attempts: DOWNLOAD_RETRIES,
116
- delayMs: DOWNLOAD_RETRY_DELAY_MS,
103
+ if (await hasIncompleteRuntimeCache({ installDir, binaryName })) {
104
+ await waitForFile(binaryPath, {
105
+ timeoutMs: ACTIVE_RUNTIME_WAIT_MS,
106
+ intervalMs: ACTIVE_RUNTIME_WAIT_INTERVAL_MS,
117
107
  });
118
- if (resolved.platform !== "windows") {
119
- await fsp.chmod(tmpPath, 0o755);
120
- }
121
- await fsp.rename(tmpPath, binaryPath);
122
- installedBinaryThisRun = true;
123
- } catch (error) {
124
- await safeUnlink(tmpPath);
125
- if (await canUseSourceFallback(sourceEntry)) {
126
- return runSourceFallback({
127
- sourceEntry,
128
- version,
129
- packageManager: detectPackageManager(),
130
- reason: error,
108
+ }
109
+
110
+ if (await fileExists(binaryPath)) {
111
+ // Another concurrent launcher finished the runtime install while this
112
+ // process was waiting.
113
+ } else {
114
+ const tag = `v${version}`;
115
+ const assetName = `${PACKAGE_NAME}-${version}-${resolved.platform}-${resolved.arch}${resolved.ext}`;
116
+ const url = `https://github.com/${REPO_OWNER}/${REPO_NAME}/releases/download/${tag}/${assetName}`;
117
+ const tmpPath = `${binaryPath}.tmp-${process.pid}-${Date.now()}-${Math.random().toString(16).slice(2)}`;
118
+
119
+ try {
120
+ await fsp.mkdir(installDir, { recursive: true });
121
+ await downloadWithRetry(url, tmpPath, {
122
+ attempts: DOWNLOAD_RETRIES,
123
+ delayMs: DOWNLOAD_RETRY_DELAY_MS,
131
124
  });
125
+ if (resolved.platform !== "windows") {
126
+ await fsp.chmod(tmpPath, 0o755);
127
+ }
128
+ await installDownloadedRuntime(tmpPath, binaryPath);
129
+ installedBinaryThisRun = true;
130
+ } catch (error) {
131
+ await safeUnlink(tmpPath);
132
+ if (await canUseSourceFallback(sourceEntry)) {
133
+ return runSourceFallback({
134
+ sourceEntry,
135
+ version,
136
+ packageManager: detectPackageManager(),
137
+ reason: error,
138
+ });
139
+ }
140
+ const message =
141
+ error instanceof Error ? error.message : String(error ?? "");
142
+ console.error(
143
+ [
144
+ "Unable to download the fclt binary for this platform.",
145
+ `Expected asset: ${assetName}`,
146
+ `URL: ${url}`,
147
+ `Reason: ${message}`,
148
+ "",
149
+ "Try installing directly from releases:",
150
+ "https://github.com/hack-dance/fclt/releases",
151
+ ].join("\n")
152
+ );
153
+ process.exit(1);
132
154
  }
133
- const message =
134
- error instanceof Error ? error.message : String(error ?? "");
135
- console.error(
136
- [
137
- "Unable to download the fclt binary for this platform.",
138
- `Expected asset: ${assetName}`,
139
- `URL: ${url}`,
140
- `Reason: ${message}`,
141
- "",
142
- "Try installing directly from releases:",
143
- "https://github.com/hack-dance/fclt/releases",
144
- ].join("\n")
145
- );
146
- process.exit(1);
147
155
  }
148
156
  }
149
157
 
@@ -367,19 +375,58 @@ async function hasIncompleteRuntimeCache({ installDir, binaryName }) {
367
375
  }
368
376
  }
369
377
 
370
- async function removeIncompleteRuntimeTemps({ installDir, binaryName }) {
378
+ async function removeStaleRuntimeTemps({ installDir, binaryName, maxAgeMs }) {
371
379
  try {
372
380
  const entries = await fsp.readdir(installDir);
373
- await Promise.all(
374
- entries
375
- .filter((entry) => entry.startsWith(`${binaryName}.tmp-`))
376
- .map((entry) => safeUnlink(path.join(installDir, entry)))
377
- );
381
+ const now = Date.now();
382
+ const stalePaths = [];
383
+ for (const entry of entries) {
384
+ if (!entry.startsWith(`${binaryName}.tmp-`)) {
385
+ continue;
386
+ }
387
+ const candidate = path.join(installDir, entry);
388
+ try {
389
+ const stats = await fsp.stat(candidate);
390
+ if (now - stats.mtimeMs > maxAgeMs) {
391
+ stalePaths.push(candidate);
392
+ }
393
+ } catch {
394
+ // Ignore temp files that disappeared while scanning.
395
+ }
396
+ }
397
+ await Promise.all(stalePaths.map((candidate) => safeUnlink(candidate)));
378
398
  } catch {
379
399
  // Ignore missing runtime dirs while cleaning stale temp files.
380
400
  }
381
401
  }
382
402
 
403
+ async function waitForFile(filePath, { timeoutMs, intervalMs }) {
404
+ const deadline = Date.now() + timeoutMs;
405
+ while (Date.now() < deadline) {
406
+ if (await fileExists(filePath)) {
407
+ return true;
408
+ }
409
+ await sleep(intervalMs);
410
+ }
411
+ return await fileExists(filePath);
412
+ }
413
+
414
+ async function installDownloadedRuntime(tmpPath, binaryPath) {
415
+ if (await fileExists(binaryPath)) {
416
+ await safeUnlink(tmpPath);
417
+ return;
418
+ }
419
+ try {
420
+ await fsp.rename(tmpPath, binaryPath);
421
+ } catch (error) {
422
+ if (await fileExists(binaryPath)) {
423
+ await safeUnlink(tmpPath);
424
+ return;
425
+ }
426
+ throw error;
427
+ }
428
+ }
429
+
383
430
  async function safeUnlink(filePath) {
384
431
  try {
385
432
  await fsp.unlink(filePath);
package/docs/reference.md CHANGED
@@ -20,6 +20,12 @@ Use these first. They let you inspect tool state without claiming ownership of a
20
20
  actions. `paths --json` reports canonical, generated, runtime, and review paths
21
21
  for agents and integrations.
22
22
 
23
+ Use `fclt doctor --repair` as the one-command self-heal path for local state.
24
+ It repairs legacy generated state, stale Codex authoring paths, explicit project
25
+ sync policy, invalid canonical global guidance, and missing Markdown review
26
+ artifacts. Destructive-looking canonical repairs keep a backup under
27
+ `.ai/.facult/backups/doctor/`.
28
+
23
29
  ## Graph
24
30
 
25
31
  ```bash
@@ -82,6 +88,9 @@ fclt ai evolve promote EV-00003 --to global --project
82
88
  ```
83
89
 
84
90
  Use these to turn repeated work friction into reviewed capability changes.
91
+ Plain list output shows the active root and scope so an empty project queue is
92
+ not confused with the global queue. Use `--global`, `--project`, or `--root`
93
+ when reviewing a specific scope, and use `--json` for automation.
85
94
 
86
95
  ## Sources, Audit, And Updates
87
96
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "facult",
3
- "version": "2.13.0",
3
+ "version": "2.13.2",
4
4
  "description": "Manage canonical AI capabilities, sync surfaces, and evolution state.",
5
5
  "type": "module",
6
6
  "license": "MIT",
package/src/ai.ts CHANGED
@@ -1070,6 +1070,55 @@ export async function listProposals(args?: {
1070
1070
  return [...byId.values()].sort((a, b) => a.id.localeCompare(b.id));
1071
1071
  }
1072
1072
 
1073
+ export async function refreshAiReviewArtifacts(args: {
1074
+ homeDir?: string;
1075
+ rootDir: string;
1076
+ }): Promise<{
1077
+ writebackCount: number;
1078
+ proposalCount: number;
1079
+ writebackReviewDir: string;
1080
+ evolutionReviewDir: string;
1081
+ }> {
1082
+ const homeDir = args.homeDir ?? process.env.HOME ?? "";
1083
+ const writebacks = await listWritebacks({ homeDir, rootDir: args.rootDir });
1084
+ for (const record of writebacks) {
1085
+ await writeWritebackReviewArtifact({
1086
+ homeDir,
1087
+ rootDir: args.rootDir,
1088
+ record,
1089
+ });
1090
+ }
1091
+
1092
+ const proposals = await listProposals({ homeDir, rootDir: args.rootDir });
1093
+ for (const proposal of proposals) {
1094
+ const sourceWritebacks = (
1095
+ await Promise.all(
1096
+ proposal.sourceWritebacks.map((id) =>
1097
+ showWriteback(id, { homeDir, rootDir: args.rootDir })
1098
+ )
1099
+ )
1100
+ ).filter((entry): entry is AiWritebackRecord => Boolean(entry));
1101
+ await writeProposalReviewArtifact({
1102
+ homeDir,
1103
+ rootDir: args.rootDir,
1104
+ proposal,
1105
+ writebacks: sourceWritebacks,
1106
+ });
1107
+ }
1108
+
1109
+ const writebackReviewDir = facultAiWritebackReviewDir(homeDir, args.rootDir);
1110
+ const evolutionReviewDir = facultAiEvolutionReviewDir(homeDir, args.rootDir);
1111
+ await mkdir(writebackReviewDir, { recursive: true });
1112
+ await mkdir(evolutionReviewDir, { recursive: true });
1113
+
1114
+ return {
1115
+ writebackCount: writebacks.length,
1116
+ proposalCount: proposals.length,
1117
+ writebackReviewDir,
1118
+ evolutionReviewDir,
1119
+ };
1120
+ }
1121
+
1073
1122
  export async function showProposal(
1074
1123
  id: string,
1075
1124
  args: { homeDir?: string; rootDir: string }
@@ -1862,6 +1911,14 @@ async function writebackCommand(argv: string[]) {
1862
1911
  console.log(JSON.stringify(rows, null, 2));
1863
1912
  return;
1864
1913
  }
1914
+ console.log(`writebacks root: ${rootDir}`);
1915
+ console.log(
1916
+ `writebacks scope: ${projectRootFromAiRoot(rootDir, process.env.HOME ?? "") ? "project" : "global"}`
1917
+ );
1918
+ if (rows.length === 0) {
1919
+ console.log("No writebacks found for this scope.");
1920
+ return;
1921
+ }
1865
1922
  for (const row of rows) {
1866
1923
  console.log(`${row.id}\t${row.kind}\t[${row.status}]\t${row.summary}`);
1867
1924
  }
@@ -1,5 +1,5 @@
1
1
  // Generated by scripts/generate-builtin-assets.ts. Do not edit by hand.
2
2
 
3
3
  export const BUILTIN_OPERATING_MODEL_FILES = JSON.parse(
4
- '{"AGENTS.global.md":"# Facult Operating Defaults\\n\\nThis machine has a default Facult operating-model layer available.\\n\\nWhen work produces durable friction, weak verification, stale guidance, or a missing skill/tool capability, preserve that signal with `fclt ai writeback ...` when the target and scope are clear. When repeated writebacks or clearly missing capability point at a concrete improvement, use `fclt ai evolve ...` or the `capability-evolution` skill to make a reviewable proposal.\\n\\nFor work-unit framing, read `@builtin/facult-operating-model/instructions/WORK_UNITS.md`.\\nFor composing refs, snippets, instructions, skills, agents, MCP, and automations as evolvable units, read `@builtin/facult-operating-model/instructions/CAPABILITY_COMPOSITION.md`.\\nFor writeback and evolution, read `@builtin/facult-operating-model/instructions/EVOLUTION.md`.\\nFor learning and writeback defaults, read `@builtin/facult-operating-model/instructions/LEARNING_AND_WRITEBACK.md`.\\nFor deciding whether capability belongs in global or project scope, read `@builtin/facult-operating-model/instructions/PROJECT_CAPABILITY.md`.\\nFor project operating-layer design, read `@builtin/facult-operating-model/instructions/INTEGRATION.md`.\\n\\nBuiltin specialist agents are available for:\\n- writeback curation\\n- evolution planning\\n- scope promotion\\n- integration auditing\\n\\nBuiltin skills are available for:\\n- capability evolution\\n- project operating-layer design\\n","agents/evolution-planner/agent.toml":"name = \\"evolution-planner\\"\\ndescription = \\"Turn repeated writeback into concrete capability proposals.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou plan capability evolution.\\n\\nPrioritize:\\n- smallest useful change\\n- correct target asset type\\n- correct target scope\\n- evidence that justifies the change\\n- repeated writeback clusters or clearly missing capabilities, not isolated preferences\\n\\nProposal kinds you should consider first:\\n- update_asset\\n- create_asset\\n- extract_snippet\\n- add_skill\\n- promote_asset\\n\\nDefault to project scope when the pattern is repo-local.\\nPromote to global only when reuse is demonstrated and pollution risk is low.\\n\\nReturn concise proposals ordered by expected leverage, including:\\n- proposal kind\\n- target asset\\n- target scope\\n- why this is the smallest durable change\\n\\nDo not escalate to evolution when a single writeback is enough.\\nDo not use evolution as a substitute for executable task tracking when the main need is owner, priority, state, or implementation follow-through.\\n\\"\\"\\"\\n","agents/integration-auditor/agent.toml":"name = \\"integration-auditor\\"\\ndescription = \\"Find where local success can still fail system-wide.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou audit integration risk.\\n\\nPrioritize:\\n- hidden dependencies\\n- rollout hazards\\n- operational constraints\\n- gaps between local verification and real system behavior\\n\\nReturn concise findings ordered by impact.\\n\\"\\"\\"\\n","agents/scope-promoter/agent.toml":"name = \\"scope-promoter\\"\\ndescription = \\"Decide whether learning belongs at project or global scope.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou decide scope.\\n\\nPrioritize:\\n- project specificity\\n- cross-project reuse potential\\n- pollution risk from globalizing too early\\n\\nWhen recommending promotion, make the standard path explicit:\\n- keep the source capability in project scope until promotion is approved\\n- create a reviewable global proposal\\n- do not treat promotion as implicit apply\\n\\nReturn concise decisions with rationale.\\n\\"\\"\\"\\n","agents/writeback-curator/agent.toml":"name = \\"writeback-curator\\"\\ndescription = \\"Turn noisy outcomes into high-signal writeback.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou curate durable writeback.\\n\\nPrioritize:\\n- repeated failures\\n- repeated wins\\n- stale guidance\\n- missing capability edges\\n- tool, skill, MCP, plugin, automation, or instruction friction that repeatedly slows work down\\n\\nFor each recommendation, prefer returning:\\n- suggested writeback kind\\n- best target asset or destination\\n- best scope (`project` or `global`)\\n- the evidence that justifies recording it\\n\\nDo not emit low-signal noise.\\nIf the learning is repo-specific, keep it project-scoped by default.\\nWhen the signal is already strong and the target is clear, prefer recommending direct writeback capture rather than abstract advice.\\nWhen the issue is executable tooling work, recommend task tracking for the fix and writeback only for the reusable operating-model learning.\\n\\"\\"\\"\\n","instructions/CAPABILITY_COMPOSITION.md":"---\\ndescription: \\"Compose small capability units across global and project roots, then evolve the smallest affected unit.\\"\\ntags: [\\"facult\\", \\"composition\\", \\"refs\\", \\"snippets\\", \\"instructions\\"]\\n---\\n\\n# Capability Composition\\n\\nUse `fclt` capability as small units that can be composed, inspected, rendered, and evolved independently.\\n\\nThe main units are:\\n\\n- instructions: standalone markdown doctrine such as language preferences, verification rules, or review standards\\n- snippets: small markdown partials inserted into one or more rendered docs\\n- skills: task-specific workflows with `SKILL.md`\\n- agents: focused role manifests\\n- MCP definitions: tool interfaces and their safe auth shape\\n- automations: scheduled review or maintenance loops\\n- tool rules/config: tool-specific defaults and policy\\n\\n## Composition Rules\\n\\n- Keep reusable doctrine in `instructions/`.\\n- Keep repeated paragraphs or policy blocks in `snippets/`.\\n- Keep workflow execution in `skills/`.\\n- Keep persona or delegation behavior in `agents/`.\\n- Keep tool wiring in `mcp/` and `tools/<tool>/`.\\n- Compose broad agent docs from refs and snippets instead of copying text by hand.\\n- Prefer one narrow reusable unit over one large instruction file that mixes unrelated domains.\\n\\nExamples:\\n\\n- `@ai/instructions/BUN.md` for shared Bun preferences.\\n- `@ai/instructions/RUST.md` for shared Rust preferences.\\n- `@project/instructions/TESTING.md` for repo-specific test policy.\\n- `<!-- fclty:global/codex/baseline -->` for a shared rendered block.\\n\\n## Scope\\n\\nUse global scope for capability that should follow the user across projects.\\n\\nUse project scope for capability that belongs to a repo, team workflow, architecture, or local test harness.\\n\\nPromote project capability to global only when repeated evidence shows reuse across projects. Do not globalize a project quirk just because it worked once.\\n\\n## Writeback and Evolution\\n\\nTarget the smallest affected unit.\\n\\n- If a paragraph is reused in several rendered docs, target the snippet.\\n- If a domain rule is wrong, target the instruction.\\n- If a workflow is incomplete, target the skill.\\n- If a delegated role is unclear, target the agent.\\n- If a tool interface is missing or unsafe, target the MCP or tool config.\\n- If a scheduled review loop is noisy or missing context, target the automation.\\n\\nGood writeback targets are graph-backed selectors when possible:\\n\\n```bash\\nfclt ai writeback add --kind missing_context --summary \\"Bun guidance did not cover test runner selection.\\" --asset instruction:BUN\\nfclt ai writeback add --kind reusable_pattern --summary \\"Project test policy should become a shared verification snippet.\\" --asset @project/instructions/TESTING.md\\nfclt ai writeback add --kind bad_default --summary \\"The review automation escalated one-off preferences.\\" --asset automation:evolution-review\\n```\\n\\nUse `fclt ai evolve ...` only after repeated signal, a clearly missing capability, or a stale canonical asset points at a concrete change. Prefer the smallest valid proposal kind: `update_asset`, `create_asset`, `extract_snippet`, `add_skill`, or `promote_asset`.\\n\\n## Agent Defaults\\n\\nWhen an agent sees a repeated preference like \\"use Bun for JS projects\\" or \\"prefer Cargo nextest for Rust tests\\", it should not bury that in chat. It should identify whether the durable unit is:\\n\\n- a global instruction\\n- a project instruction\\n- a snippet reused by rendered docs\\n- a skill workflow\\n- a project-to-global promotion candidate\\n\\nThen it should record writeback against that unit, or draft a proposal when the evidence is already strong enough.\\n","instructions/EVOLUTION.md":"---\\ndescription: Turn repeated signal into concrete capability changes.\\ntags: [facult, evolution, writeback]\\n---\\n\\n# Evolution\\n\\nUse writeback and evolution to improve the AI operating layer itself.\\n\\nEvolution is the synthesis and change side of the feedback loop. It turns accumulated writebacks, repeated tool friction, stale canonical assets, or clearly missing capability into small reviewable changes to instructions, skills, snippets, agents, or other markdown canonical assets.\\n\\nUse capability composition when choosing the target. Instructions, snippets, skills, agents, MCP/tool config, and automations are separate units. Target the smallest unit that actually needs to change instead of rewriting a broad agent doc.\\n\\n## When To Record Writeback\\n\\nRecord writeback when one of these is true:\\n\\n- the same failure repeats\\n- the same success pattern repeats\\n- guidance is stale or missing\\n- a prompt or loop has to be restated often\\n- a project-specific pattern looks reusable\\n\\nDo not record low-signal noise:\\n\\n- one-off annoyance with no reuse value\\n- generic \\"could be better\\" commentary\\n- duplicate observations with no new evidence\\n\\nThe intended default is that agents record strong writebacks themselves when the signal is clear enough, rather than only recommending that a user do it manually later.\\n\\nDo not wait for a weekly review to preserve high-signal evidence. Do wait for repeated evidence or a clearly missing capability before drafting a proposal.\\n\\n## Scope\\n\\nChoose `project` scope when the learning depends on:\\n\\n- repo architecture\\n- team workflow\\n- project tooling\\n- local testing or verification behavior\\n\\nChoose `global` scope when the learning is reusable across projects.\\n\\nPromote from project to global only after repeated reuse or strong evidence.\\n\\n## Writeback Kinds\\n\\nCommon kinds:\\n\\n- `weak_verification`\\n- `false_positive`\\n- `missing_context`\\n- `reusable_pattern`\\n- `capability_gap`\\n- `bad_default`\\n\\nEvery good writeback should try to include:\\n\\n- a concrete summary\\n- the best target asset if known\\n- the right scope\\n- domain or tags when useful\\n\\nGood target examples:\\n\\n- `instruction:BUN` when shared Bun guidance is stale or missing\\n- `@project/instructions/TESTING.md` when repo test policy needs project-scoped evolution\\n- `snippet:global/lang/bun` when a repeated rendered block should be fixed or extracted\\n- `skill:capability-evolution` when a workflow skill is missing steps or examples\\n- `automation:evolution-review` when the scheduled review loop is noisy or incomplete\\n\\n## Operator Flow\\n\\nTypical workflow:\\n\\n```bash\\nfclt ai writeback add --kind weak_verification --summary \\"Checks were too shallow\\" --asset instruction:VERIFICATION\\nfclt ai writeback group --by asset\\nfclt ai writeback summarize --by domain\\nfclt ai evolve propose\\nfclt ai evolve draft EV-00001\\nfclt ai evolve accept EV-00001\\nfclt ai evolve apply EV-00001\\n```\\n\\nUse `fclt ai evolve draft <id> --append \\"...\\"` to revise a draft while preserving draft history.\\n\\nReview surfaces:\\n\\n- open `~/.ai/writebacks/` and `~/.ai/evolution/` in a Markdown editor for frontmatter-rich global and project-scoped review artifacts\\n- `fclt status --json` for queue/proposal paths, review artifact paths, counts, and active scope\\n- `fclt ai writeback list|show|group|summarize` for raw and clustered signal\\n- `fclt ai evolve list|show|review` for proposal state without applying changes\\n- `fclt templates init automation learning-review` for recurring capture/review\\n- `fclt templates init automation evolution-review` for recurring proposal review\\n- `fclt templates init automation tool-call-audit` for repeated tool-friction review\\n\\nEvolution proposal metadata, markdown drafts, patch artifacts, writeback queues,\\nand journals are runtime state. `fclt` stores JSON queues, proposal records,\\ndraft refs, patches, and journals in machine-local `fclt` state. It mirrors\\nhuman-readable review artifacts into global `~/.ai/writebacks/...` and\\n`~/.ai/evolution/...`, including project-scoped artifacts under\\n`projects/<slug-hash>/` with cwd/project metadata in frontmatter. Canonical\\nassets in `~/.ai` or `<repo>/.ai` should only change when a proposal is applied.\\n\\n## Default Agent Behavior\\n\\nUse the smallest action that fits the signal:\\n\\n1. record one strong writeback when there is a clear durable learning\\n2. use `writeback-curator` when the target, kind, or scope is ambiguous\\n3. use `capability-evolution` or `evolution-planner` when repeated signal should become a proposal\\n4. do not draft or apply proposals just because a writeback exists; require repeated evidence or a clearly missing capability\\n\\nAvoid creating writeback/evolution noise for one-off nits, vague preferences, or speculative ideas without evidence.\\n\\nWhen the friction is executable product/tooling work that needs ownership,\\npriority, state, or implementation follow-through, create or update a real task\\nsystem item instead of forcing it into capability evolution. Use evolution for\\nthe reusable operating-layer change.\\n\\n## Proposal Kinds\\n\\nCurrent supported proposal kinds:\\n\\n- `update_asset`\\n- `create_asset`\\n- `extract_snippet`\\n- `add_skill`\\n- `promote_asset`\\n\\nUse the smallest durable change that fits the evidence.\\n\\nExamples:\\n\\n- `update_asset`: fix a stale instruction, snippet, agent, or automation markdown asset.\\n- `create_asset`: add a missing instruction such as `BUN.md` or `RUST.md`.\\n- `extract_snippet`: move repeated guidance out of several docs into one snippet.\\n- `add_skill`: create a workflow when instructions are not enough.\\n- `promote_asset`: move a proven project instruction/snippet/skill toward global reuse.\\n\\n## Review And Apply Rules\\n\\n- draft before apply\\n- accept before apply\\n- prefer the smallest safe change\\n- keep reviewable evidence tied to source writebacks\\n- do not globalize project behavior too early\\n- do not apply high-risk global instruction, skill, plugin, or shared-tool changes without explicit review/approval\\n\\nApply is for markdown canonical assets only. If the target is wrong, revise the proposal rather than forcing it through.\\n","instructions/INTEGRATION.md":"---\\ndescription: Detect where local success can still fail at integration boundaries.\\ntags: [facult, integration, verification]\\n---\\n\\n# Integration\\n\\nDistinguish local correctness from system correctness. Check hidden dependencies, rollout order, and operational constraints before calling work done.\\n","instructions/LEARNING_AND_WRITEBACK.md":"---\\ndescription: Preserve durable signal and record writeback when the operating layer should learn.\\ntags: [facult, learning, writeback]\\n---\\n\\n# Learning And Writeback\\n\\nUse this when work produces a durable decision, failure, success pattern, or missing guardrail that should outlive the current task.\\n\\nThis is the capture side of the feedback loop. The goal is to let normal agent work produce reusable signal without requiring a human to manually restate every friction point later.\\n\\n## Default Behavior\\n\\nThe normal path should be agent-driven.\\n\\nIf you can clearly answer:\\n\\n- what was learned\\n- why it matters\\n- where it should land\\n- whether it belongs in `project` or `global`\\n\\nthen record the writeback instead of only suggesting that someone should do it later.\\n\\nUse:\\n\\n```bash\\nfclt ai writeback add --kind <kind> --summary \\"<summary>\\" --asset <asset-selector>\\n```\\n\\nThe writeback queue is runtime state, not canonical source. `fclt` stores JSON\\nqueue state in machine-local `fclt` state so sandboxed agents can record durable\\nfriction without mutating canonical assets unless an evolution proposal is later\\nreviewed and applied.\\n\\nEvery writeback also refreshes a Markdown review artifact under the global\\n`~/.ai/writebacks/...` tree. Global signal lands in `~/.ai/writebacks/global/`;\\nproject-scoped signal lands in `~/.ai/writebacks/projects/<slug-hash>/` with\\nfrontmatter for scope, project root, cwd, target asset, status, tags, evidence,\\nand timestamps. Do not write writeback review artifacts into a repo-local `.ai`;\\nrepo-local state should contribute project metadata and evidence, not bundled\\nprivate review files.\\n\\nProject-scoped writebacks should usually be recorded from the repo that produced\\nthe evidence. Global writebacks should be reserved for shared doctrine, shared\\nskills, shared agents, tool behavior, or cross-project capability gaps.\\n\\nTarget the smallest composable unit that explains the friction:\\n\\n- instruction: domain guidance, preferences, verification rules, or review doctrine\\n- snippet: repeated markdown block used by more than one rendered doc\\n- skill: workflow execution steps or examples\\n- agent: delegated role behavior\\n- MCP/tool config: tool interface, auth shape, or rendered integration\\n- automation: scheduled review loop, cadence, prompt, or memory\\n\\n## Record Writeback When\\n\\n- the same failure or weak loop appears again\\n- a reusable success pattern shows up\\n- guidance is clearly stale or missing\\n- a repo-local behavior probably belongs in project capability\\n- a cross-project behavior probably belongs in global capability\\n- a skill, tool, MCP, plugin, automation, or instruction gap repeatedly slows work down\\n- an agent has to restate the same workaround, verification rule, or review rule\\n- a repeated preference should become an atomic instruction such as `BUN.md`, `RUST.md`, or a project-specific testing policy\\n\\n## Do Not Record Writeback For\\n\\n- one-off annoyance with no durable value\\n- weak commentary with no target\\n- speculative ideas without evidence\\n- duplicate noise with no new signal\\n\\n## Follow Through\\n\\n- prefer one strong writeback over many weak ones\\n- mention the writeback id when summarizing what changed\\n- escalate to `capability-evolution` or `fclt ai evolve ...` only when the signal is repeated or clearly points at a durable capability change\\n- use `fclt ai writeback group --by asset` or `fclt ai writeback summarize --by domain` to review accumulated signal before proposing broad changes\\n- use scheduled `learning-review`, `evolution-review`, or `tool-call-audit` automations when the signal should be reviewed in the background\\n","instructions/PROJECT_CAPABILITY.md":"---\\ndescription: Decide what belongs in repo-local .ai versus the global store.\\ntags: [facult, project, scope]\\n---\\n\\n# Project Capability\\n\\nPrefer project scope when the guidance depends on repo architecture, team workflow, or colocated tooling. Promote to global only after repeated cross-project reuse.\\n\\n## Project First\\n\\nDefault to `<repo>/.ai` when the capability is about:\\n\\n- local architecture\\n- repo-specific testing or verification\\n- team conventions\\n- project tools and workflows\\n\\n## Promote Carefully\\n\\nPromote to `~/.ai` only when:\\n\\n- the same pattern succeeds in more than one repo\\n- the capability is not coupled to local architecture\\n- the global version will not create noise for unrelated projects\\n\\nUse:\\n\\n```bash\\nfclt ai evolve promote EV-00001 --to global --project\\n```\\n\\nThat creates a new global proposal for review. It does not auto-apply the promotion.\\n","instructions/WORK_UNITS.md":"---\\ndescription: \\"Define work units so agent tasks have a clear goal, evidence path, artifact, and writeback target.\\"\\ntags: [\\"work-units\\", \\"planning\\", \\"verification\\", \\"writeback\\"]\\n---\\n\\n# Work Units\\n\\nA work unit is the smallest coherent unit of agent work that can be understood, verified, and preserved.\\n\\nIt is not just the user\'s latest sentence. It is the operational shape around that sentence: what is being changed, why it matters, what evidence is needed, what artifact should remain, and how future agents should benefit from the result.\\n\\n## Minimum Contract\\n\\nA well-formed work unit names:\\n\\n- goal: the outcome the user needs\\n- acceptance criteria: what must be true when the work is done\\n- required context: source files, docs, systems, messages, or prior decisions needed for correctness\\n- constraints: permissions, privacy, compatibility, deadlines, ownership, or scope limits\\n- signals or evidence: checks that can confirm progress or falsify assumptions\\n- output artifact: code, docs, proposal, issue, note, draft, or report\\n- verification path: commands, review surfaces, manual checks, or source-of-truth reads\\n- writeback target: where durable learning belongs if the work teaches something reusable\\n\\nIf one of these is missing and the gap blocks correctness, surface the gap early and recover it before moving faster.\\n\\n## Why It Exists\\n\\nWork-unit framing prevents shallow completion. It helps agents avoid:\\n\\n- changing files before understanding the target\\n- treating a weak green signal as proof\\n- losing reusable learning in chat\\n- creating duplicate tasks or proposals\\n- turning one-off preferences into global rules\\n- pushing project-specific details into global capability\\n\\n## How To Use It\\n\\nFor simple tasks, keep the work unit implicit but still verify the result.\\n\\nFor ambiguous, high-impact, or multi-step tasks, make the work unit explicit before executing. A compact form is enough:\\n\\n```text\\nGoal:\\nAcceptance:\\nContext:\\nConstraints:\\nEvidence:\\nArtifact:\\nVerification:\\nWriteback:\\n```\\n\\nUse the smallest framing that makes the task correct. Do not turn every request into paperwork.\\n\\n## Writeback\\n\\nWhen the work reveals durable friction, missing capability, stale guidance, or a repeatable workflow, prefer one strong writeback over many weak ones.\\n\\nUse `fclt ai writeback add ...` when the target asset, scope, and evidence are clear. Use `fclt ai evolve ...` only when repeated signal supports a concrete proposal.\\n","skills/capability-evolution/SKILL.md":"---\\ndescription: Convert repeated writeback into concrete fclt capability proposals.\\ntags: [facult, evolution, writeback]\\n---\\n\\n# capability-evolution\\n\\n## When To Use\\nUse this skill when the same missing guidance, weak loop, or recurring win appears often enough that the AI system itself should probably change.\\n\\nDo not wait for a human operator by default if the signal is already clear and the environment permits local AI runtime state to be updated.\\n\\nUse writeback first when the signal is useful but not yet repeated. Use evolution when accumulated writebacks, repeated tool friction, or a clearly missing capability point at a specific target asset or new capability.\\n\\n## Scope Decision\\n\\nChoose `project` when the behavior depends on repo-local architecture or workflow.\\n\\nChoose `global` when the behavior is broadly reusable.\\n\\nIf unsure, start at project scope and promote later with evidence.\\n\\n## Working Flow\\n\\n1. record the strongest writeback\\n2. group or summarize repeated signal\\n3. choose the smallest valid proposal kind\\n4. draft the proposal\\n5. accept only after the target and scope are correct\\n6. apply only when the markdown target is the intended canonical asset\\n\\nUse:\\n\\n```bash\\nfclt ai writeback add ...\\nfclt ai writeback group --by asset\\nfclt ai writeback summarize --by domain\\nfclt ai evolve propose\\nfclt ai evolve draft EV-00001\\nfclt ai evolve draft EV-00001 --append \\"tighten the rule with a concrete verification step\\"\\nfclt ai evolve accept EV-00001\\nfclt ai evolve apply EV-00001\\n```\\n\\nFor background review loops, use:\\n\\n```bash\\nfclt templates init automation learning-review\\nfclt templates init automation evolution-review\\nfclt templates init automation tool-call-audit\\n```\\n\\nIf there is not yet enough repeated signal for evolution, record the writeback and stop there.\\n\\n## Proposal Kind Selection\\n\\n- `update_asset` for tightening existing guidance\\n- `create_asset` for missing instructions or docs\\n- `extract_snippet` for reusable partial guidance\\n- `add_skill` for reusable workflow instruction\\n- `promote_asset` for project-to-global promotion\\n\\nUse task tracking instead of evolution when the main work is an executable tool or product fix that needs an owner, priority, state, or delivery plan. Use evolution for the reusable instruction, skill, or operating-model change that should survive that fix.\\n\\n## Output Contract\\n- repeated signal\\n- proposed asset change\\n- target scope\\n- evidence\\n- smallest useful next step\\n","skills/project-operating-layer-design/SKILL.md":"---\\ndescription: Design or improve a repo-local .ai operating layer.\\ntags: [facult, project, design]\\n---\\n\\n# project-operating-layer-design\\n\\n## When To Use\\nUse this skill when a project needs its own `.ai/` structure, repo-specific instructions, or local bootstrap guidance.\\n\\n## Output Contract\\n- recommended `.ai/` layout\\n- what stays project-local\\n- what stays global\\n- what should remain generated runtime output only\\n"}'
4
+ '{"AGENTS.global.md":"# Facult Operating Defaults\\n\\nThis machine has a default Facult operating-model layer available.\\n\\nDefault behavior:\\n\\n- Treat meaningful work as a work unit: know the goal, acceptance criteria, required context, constraints, evidence, output artifact, verification path, and likely writeback target.\\n- Use the strongest practical feedback loop for the risk. Do not treat shallow success as proof when a better check is available.\\n- When work produces durable friction, weak verification, stale guidance, or a missing skill/tool capability, preserve that signal with `fclt ai writeback ...` when the target and scope are clear.\\n- Use `fclt ai evolve ...` or the `capability-evolution` skill only when repeated writebacks, a clearly missing capability, or a stale canonical asset point at a concrete improvement.\\n- Keep one-off preferences and speculative ideas out of evolution. Use writeback, notes, or task tracking instead.\\n- Use project scope for repo-specific workflow and global scope for reusable cross-project doctrine. Promote project capability only after evidence shows reuse.\\n- Use Linear or another task system for executable product/tooling work that needs ownership, priority, state, or delivery follow-through.\\n- Keep writeback/evolution runtime and review artifacts in the global `.ai` review tree; do not commit generated writeback queues or private review artifacts into project repos.\\n\\nFor work-unit framing, read `@builtin/facult-operating-model/instructions/WORK_UNITS.md`.\\nFor composing refs, snippets, instructions, skills, agents, MCP, and automations as evolvable units, read `@builtin/facult-operating-model/instructions/CAPABILITY_COMPOSITION.md`.\\nFor writeback and evolution, read `@builtin/facult-operating-model/instructions/EVOLUTION.md`.\\nFor learning and writeback defaults, read `@builtin/facult-operating-model/instructions/LEARNING_AND_WRITEBACK.md`.\\nFor deciding whether capability belongs in global or project scope, read `@builtin/facult-operating-model/instructions/PROJECT_CAPABILITY.md`.\\nFor project operating-layer design, read `@builtin/facult-operating-model/instructions/INTEGRATION.md`.\\n\\nBuiltin specialist agents are available for:\\n- writeback curation\\n- evolution planning\\n- scope promotion\\n- integration auditing\\n\\nBuiltin skills are available for:\\n- capability evolution\\n- project operating-layer design\\n\\nUseful health and review commands:\\n\\n```bash\\nfclt doctor --json\\nfclt status --json\\nfclt ai writeback list\\nfclt ai writeback group --by asset\\nfclt ai evolve list\\n```\\n","agents/evolution-planner/agent.toml":"name = \\"evolution-planner\\"\\ndescription = \\"Turn repeated writeback into concrete capability proposals.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou plan capability evolution.\\n\\nPrioritize:\\n- smallest useful change\\n- correct target asset type\\n- correct target scope\\n- evidence that justifies the change\\n- repeated writeback clusters or clearly missing capabilities, not isolated preferences\\n\\nProposal kinds you should consider first:\\n- update_asset\\n- create_asset\\n- extract_snippet\\n- add_skill\\n- promote_asset\\n\\nDefault to project scope when the pattern is repo-local.\\nPromote to global only when reuse is demonstrated and pollution risk is low.\\n\\nReturn concise proposals ordered by expected leverage, including:\\n- proposal kind\\n- target asset\\n- target scope\\n- why this is the smallest durable change\\n\\nDo not escalate to evolution when a single writeback is enough.\\nDo not use evolution as a substitute for executable task tracking when the main need is owner, priority, state, or implementation follow-through.\\n\\"\\"\\"\\n","agents/integration-auditor/agent.toml":"name = \\"integration-auditor\\"\\ndescription = \\"Find where local success can still fail system-wide.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou audit integration risk.\\n\\nPrioritize:\\n- hidden dependencies\\n- rollout hazards\\n- operational constraints\\n- gaps between local verification and real system behavior\\n\\nReturn concise findings ordered by impact.\\n\\"\\"\\"\\n","agents/scope-promoter/agent.toml":"name = \\"scope-promoter\\"\\ndescription = \\"Decide whether learning belongs at project or global scope.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou decide scope.\\n\\nPrioritize:\\n- project specificity\\n- cross-project reuse potential\\n- pollution risk from globalizing too early\\n\\nWhen recommending promotion, make the standard path explicit:\\n- keep the source capability in project scope until promotion is approved\\n- create a reviewable global proposal\\n- do not treat promotion as implicit apply\\n\\nReturn concise decisions with rationale.\\n\\"\\"\\"\\n","agents/writeback-curator/agent.toml":"name = \\"writeback-curator\\"\\ndescription = \\"Turn noisy outcomes into high-signal writeback.\\"\\n\\ndeveloper_instructions = \\"\\"\\"\\nYou curate durable writeback.\\n\\nPrioritize:\\n- repeated failures\\n- repeated wins\\n- stale guidance\\n- missing capability edges\\n- tool, skill, MCP, plugin, automation, or instruction friction that repeatedly slows work down\\n\\nFor each recommendation, prefer returning:\\n- suggested writeback kind\\n- best target asset or destination\\n- best scope (`project` or `global`)\\n- the evidence that justifies recording it\\n\\nDo not emit low-signal noise.\\nIf the learning is repo-specific, keep it project-scoped by default.\\nWhen the signal is already strong and the target is clear, prefer recommending direct writeback capture rather than abstract advice.\\nWhen the issue is executable tooling work, recommend task tracking for the fix and writeback only for the reusable operating-model learning.\\n\\"\\"\\"\\n","instructions/CAPABILITY_COMPOSITION.md":"---\\ndescription: \\"Compose small capability units across global and project roots, then evolve the smallest affected unit.\\"\\ntags: [\\"facult\\", \\"composition\\", \\"refs\\", \\"snippets\\", \\"instructions\\"]\\n---\\n\\n# Capability Composition\\n\\nUse `fclt` capability as small units that can be composed, inspected, rendered, and evolved independently.\\n\\nThe main units are:\\n\\n- instructions: standalone markdown doctrine such as language preferences, verification rules, or review standards\\n- snippets: small markdown partials inserted into one or more rendered docs\\n- skills: task-specific workflows with `SKILL.md`\\n- agents: focused role manifests\\n- MCP definitions: tool interfaces and their safe auth shape\\n- automations: scheduled review or maintenance loops\\n- tool rules/config: tool-specific defaults and policy\\n\\n## Composition Rules\\n\\n- Keep reusable doctrine in `instructions/`.\\n- Keep repeated paragraphs or policy blocks in `snippets/`.\\n- Keep workflow execution in `skills/`.\\n- Keep persona or delegation behavior in `agents/`.\\n- Keep tool wiring in `mcp/` and `tools/<tool>/`.\\n- Compose broad agent docs from refs and snippets instead of copying text by hand.\\n- Prefer one narrow reusable unit over one large instruction file that mixes unrelated domains.\\n\\nExamples:\\n\\n- `@ai/instructions/BUN.md` for shared Bun preferences.\\n- `@ai/instructions/RUST.md` for shared Rust preferences.\\n- `@project/instructions/TESTING.md` for repo-specific test policy.\\n- `<!-- fclty:global/codex/baseline -->` for a shared rendered block.\\n\\n## Scope\\n\\nUse global scope for capability that should follow the user across projects.\\n\\nUse project scope for capability that belongs to a repo, team workflow, architecture, or local test harness.\\n\\nPromote project capability to global only when repeated evidence shows reuse across projects. Do not globalize a project quirk just because it worked once.\\n\\n## Writeback and Evolution\\n\\nTarget the smallest affected unit.\\n\\n- If a paragraph is reused in several rendered docs, target the snippet.\\n- If a domain rule is wrong, target the instruction.\\n- If a workflow is incomplete, target the skill.\\n- If a delegated role is unclear, target the agent.\\n- If a tool interface is missing or unsafe, target the MCP or tool config.\\n- If a scheduled review loop is noisy or missing context, target the automation.\\n\\nGood writeback targets are graph-backed selectors when possible:\\n\\n```bash\\nfclt ai writeback add --kind missing_context --summary \\"Bun guidance did not cover test runner selection.\\" --asset instruction:BUN\\nfclt ai writeback add --kind reusable_pattern --summary \\"Project test policy should become a shared verification snippet.\\" --asset @project/instructions/TESTING.md\\nfclt ai writeback add --kind bad_default --summary \\"The review automation escalated one-off preferences.\\" --asset automation:evolution-review\\n```\\n\\nUse `fclt ai evolve ...` only after repeated signal, a clearly missing capability, or a stale canonical asset points at a concrete change. Prefer the smallest valid proposal kind: `update_asset`, `create_asset`, `extract_snippet`, `add_skill`, or `promote_asset`.\\n\\n## Agent Defaults\\n\\nWhen an agent sees a repeated preference like \\"use Bun for JS projects\\" or \\"prefer Cargo nextest for Rust tests\\", it should not bury that in chat. It should identify whether the durable unit is:\\n\\n- a global instruction\\n- a project instruction\\n- a snippet reused by rendered docs\\n- a skill workflow\\n- a project-to-global promotion candidate\\n\\nThen it should record writeback against that unit, or draft a proposal when the evidence is already strong enough.\\n","instructions/EVOLUTION.md":"---\\ndescription: Turn repeated signal into concrete capability changes.\\ntags: [facult, evolution, writeback]\\n---\\n\\n# Evolution\\n\\nUse writeback and evolution to improve the AI operating layer itself.\\n\\nEvolution is the synthesis and change side of the feedback loop. It turns accumulated writebacks, repeated tool friction, stale canonical assets, or clearly missing capability into small reviewable changes to instructions, skills, snippets, agents, or other markdown canonical assets.\\n\\nUse capability composition when choosing the target. Instructions, snippets, skills, agents, MCP/tool config, and automations are separate units. Target the smallest unit that actually needs to change instead of rewriting a broad agent doc.\\n\\n## When To Record Writeback\\n\\nRecord writeback when one of these is true:\\n\\n- the same failure repeats\\n- the same success pattern repeats\\n- guidance is stale or missing\\n- a prompt or loop has to be restated often\\n- a project-specific pattern looks reusable\\n\\nDo not record low-signal noise:\\n\\n- one-off annoyance with no reuse value\\n- generic \\"could be better\\" commentary\\n- duplicate observations with no new evidence\\n\\nThe intended default is that agents record strong writebacks themselves when the signal is clear enough, rather than only recommending that a user do it manually later.\\n\\nDo not wait for a weekly review to preserve high-signal evidence. Do wait for repeated evidence or a clearly missing capability before drafting a proposal.\\n\\n## Scope\\n\\nChoose `project` scope when the learning depends on:\\n\\n- repo architecture\\n- team workflow\\n- project tooling\\n- local testing or verification behavior\\n\\nChoose `global` scope when the learning is reusable across projects.\\n\\nPromote from project to global only after repeated reuse or strong evidence.\\n\\n## Writeback Kinds\\n\\nCommon kinds:\\n\\n- `weak_verification`\\n- `false_positive`\\n- `missing_context`\\n- `reusable_pattern`\\n- `capability_gap`\\n- `bad_default`\\n\\nEvery good writeback should try to include:\\n\\n- a concrete summary\\n- the best target asset if known\\n- the right scope\\n- domain or tags when useful\\n\\nGood target examples:\\n\\n- `instruction:BUN` when shared Bun guidance is stale or missing\\n- `@project/instructions/TESTING.md` when repo test policy needs project-scoped evolution\\n- `snippet:global/lang/bun` when a repeated rendered block should be fixed or extracted\\n- `skill:capability-evolution` when a workflow skill is missing steps or examples\\n- `automation:evolution-review` when the scheduled review loop is noisy or incomplete\\n\\n## Operator Flow\\n\\nTypical workflow:\\n\\n```bash\\nfclt ai writeback add --kind weak_verification --summary \\"Checks were too shallow\\" --asset instruction:VERIFICATION\\nfclt ai writeback group --by asset\\nfclt ai writeback summarize --by domain\\nfclt ai evolve propose\\nfclt ai evolve draft EV-00001\\nfclt ai evolve accept EV-00001\\nfclt ai evolve apply EV-00001\\n```\\n\\nUse `fclt ai evolve draft <id> --append \\"...\\"` to revise a draft while preserving draft history.\\n\\nReview surfaces:\\n\\n- open `~/.ai/writebacks/` and `~/.ai/evolution/` in a Markdown editor for frontmatter-rich global and project-scoped review artifacts\\n- `fclt status --json` for queue/proposal paths, review artifact paths, counts, and active scope\\n- `fclt ai writeback list|show|group|summarize` for raw and clustered signal\\n- `fclt ai evolve list|show|review` for proposal state without applying changes\\n- `fclt templates init automation learning-review` for recurring capture/review\\n- `fclt templates init automation evolution-review` for recurring proposal review\\n- `fclt templates init automation tool-call-audit` for repeated tool-friction review\\n\\nEvolution proposal metadata, markdown drafts, patch artifacts, writeback queues,\\nand journals are runtime state. `fclt` stores JSON queues, proposal records,\\ndraft refs, patches, and journals in machine-local `fclt` state. It mirrors\\nhuman-readable review artifacts into global `~/.ai/writebacks/...` and\\n`~/.ai/evolution/...`, including project-scoped artifacts under\\n`projects/<slug-hash>/` with cwd/project metadata in frontmatter. Canonical\\nassets in `~/.ai` or `<repo>/.ai` should only change when a proposal is applied.\\n\\n## Default Agent Behavior\\n\\nUse the smallest action that fits the signal:\\n\\n1. record one strong writeback when there is a clear durable learning\\n2. use `writeback-curator` when the target, kind, or scope is ambiguous\\n3. use `capability-evolution` or `evolution-planner` when repeated signal should become a proposal\\n4. do not draft or apply proposals just because a writeback exists; require repeated evidence or a clearly missing capability\\n\\nAvoid creating writeback/evolution noise for one-off nits, vague preferences, or speculative ideas without evidence.\\n\\nWhen the friction is executable product/tooling work that needs ownership,\\npriority, state, or implementation follow-through, create or update a real task\\nsystem item instead of forcing it into capability evolution. Use evolution for\\nthe reusable operating-layer change.\\n\\n## Proposal Kinds\\n\\nCurrent supported proposal kinds:\\n\\n- `update_asset`\\n- `create_asset`\\n- `extract_snippet`\\n- `add_skill`\\n- `promote_asset`\\n\\nUse the smallest durable change that fits the evidence.\\n\\nExamples:\\n\\n- `update_asset`: fix a stale instruction, snippet, agent, or automation markdown asset.\\n- `create_asset`: add a missing instruction such as `BUN.md` or `RUST.md`.\\n- `extract_snippet`: move repeated guidance out of several docs into one snippet.\\n- `add_skill`: create a workflow when instructions are not enough.\\n- `promote_asset`: move a proven project instruction/snippet/skill toward global reuse.\\n\\n## Review And Apply Rules\\n\\n- draft before apply\\n- accept before apply\\n- prefer the smallest safe change\\n- keep reviewable evidence tied to source writebacks\\n- do not globalize project behavior too early\\n- do not apply high-risk global instruction, skill, plugin, or shared-tool changes without explicit review/approval\\n\\nApply is for markdown canonical assets only. If the target is wrong, revise the proposal rather than forcing it through.\\n","instructions/INTEGRATION.md":"---\\ndescription: Detect where local success can still fail at integration boundaries.\\ntags: [facult, integration, verification]\\n---\\n\\n# Integration\\n\\nDistinguish local correctness from system correctness. Check hidden dependencies, rollout order, and operational constraints before calling work done.\\n","instructions/LEARNING_AND_WRITEBACK.md":"---\\ndescription: Preserve durable signal and record writeback when the operating layer should learn.\\ntags: [facult, learning, writeback]\\n---\\n\\n# Learning And Writeback\\n\\nUse this when work produces a durable decision, failure, success pattern, or missing guardrail that should outlive the current task.\\n\\nThis is the capture side of the feedback loop. The goal is to let normal agent work produce reusable signal without requiring a human to manually restate every friction point later.\\n\\n## Default Behavior\\n\\nThe normal path should be agent-driven.\\n\\nIf you can clearly answer:\\n\\n- what was learned\\n- why it matters\\n- where it should land\\n- whether it belongs in `project` or `global`\\n\\nthen record the writeback instead of only suggesting that someone should do it later.\\n\\nUse:\\n\\n```bash\\nfclt ai writeback add --kind <kind> --summary \\"<summary>\\" --asset <asset-selector>\\n```\\n\\nThe writeback queue is runtime state, not canonical source. `fclt` stores JSON\\nqueue state in machine-local `fclt` state so sandboxed agents can record durable\\nfriction without mutating canonical assets unless an evolution proposal is later\\nreviewed and applied.\\n\\nEvery writeback also refreshes a Markdown review artifact under the global\\n`~/.ai/writebacks/...` tree. Global signal lands in `~/.ai/writebacks/global/`;\\nproject-scoped signal lands in `~/.ai/writebacks/projects/<slug-hash>/` with\\nfrontmatter for scope, project root, cwd, target asset, status, tags, evidence,\\nand timestamps. Do not write writeback review artifacts into a repo-local `.ai`;\\nrepo-local state should contribute project metadata and evidence, not bundled\\nprivate review files.\\n\\nProject-scoped writebacks should usually be recorded from the repo that produced\\nthe evidence. Global writebacks should be reserved for shared doctrine, shared\\nskills, shared agents, tool behavior, or cross-project capability gaps.\\n\\nTarget the smallest composable unit that explains the friction:\\n\\n- instruction: domain guidance, preferences, verification rules, or review doctrine\\n- snippet: repeated markdown block used by more than one rendered doc\\n- skill: workflow execution steps or examples\\n- agent: delegated role behavior\\n- MCP/tool config: tool interface, auth shape, or rendered integration\\n- automation: scheduled review loop, cadence, prompt, or memory\\n\\n## Record Writeback When\\n\\n- the same failure or weak loop appears again\\n- a reusable success pattern shows up\\n- guidance is clearly stale or missing\\n- a repo-local behavior probably belongs in project capability\\n- a cross-project behavior probably belongs in global capability\\n- a skill, tool, MCP, plugin, automation, or instruction gap repeatedly slows work down\\n- an agent has to restate the same workaround, verification rule, or review rule\\n- a repeated preference should become an atomic instruction such as `BUN.md`, `RUST.md`, or a project-specific testing policy\\n\\n## Do Not Record Writeback For\\n\\n- one-off annoyance with no durable value\\n- weak commentary with no target\\n- speculative ideas without evidence\\n- duplicate noise with no new signal\\n\\n## Follow Through\\n\\n- prefer one strong writeback over many weak ones\\n- mention the writeback id when summarizing what changed\\n- escalate to `capability-evolution` or `fclt ai evolve ...` only when the signal is repeated or clearly points at a durable capability change\\n- use `fclt ai writeback group --by asset` or `fclt ai writeback summarize --by domain` to review accumulated signal before proposing broad changes\\n- use scheduled `learning-review`, `evolution-review`, or `tool-call-audit` automations when the signal should be reviewed in the background\\n","instructions/PROJECT_CAPABILITY.md":"---\\ndescription: Decide what belongs in repo-local .ai versus the global store.\\ntags: [facult, project, scope]\\n---\\n\\n# Project Capability\\n\\nPrefer project scope when the guidance depends on repo architecture, team workflow, or colocated tooling. Promote to global only after repeated cross-project reuse.\\n\\n## Project First\\n\\nDefault to `<repo>/.ai` when the capability is about:\\n\\n- local architecture\\n- repo-specific testing or verification\\n- team conventions\\n- project tools and workflows\\n\\n## Promote Carefully\\n\\nPromote to `~/.ai` only when:\\n\\n- the same pattern succeeds in more than one repo\\n- the capability is not coupled to local architecture\\n- the global version will not create noise for unrelated projects\\n\\nUse:\\n\\n```bash\\nfclt ai evolve promote EV-00001 --to global --project\\n```\\n\\nThat creates a new global proposal for review. It does not auto-apply the promotion.\\n","instructions/WORK_UNITS.md":"---\\ndescription: \\"Define work units so agent tasks have a clear goal, evidence path, artifact, and writeback target.\\"\\ntags: [\\"work-units\\", \\"planning\\", \\"verification\\", \\"writeback\\"]\\n---\\n\\n# Work Units\\n\\nA work unit is the smallest coherent unit of agent work that can be understood, verified, and preserved.\\n\\nIt is not just the user\'s latest sentence. It is the operational shape around that sentence: what is being changed, why it matters, what evidence is needed, what artifact should remain, and how future agents should benefit from the result.\\n\\n## Minimum Contract\\n\\nA well-formed work unit names:\\n\\n- goal: the outcome the user needs\\n- acceptance criteria: what must be true when the work is done\\n- required context: source files, docs, systems, messages, or prior decisions needed for correctness\\n- constraints: permissions, privacy, compatibility, deadlines, ownership, or scope limits\\n- signals or evidence: checks that can confirm progress or falsify assumptions\\n- output artifact: code, docs, proposal, issue, note, draft, or report\\n- verification path: commands, review surfaces, manual checks, or source-of-truth reads\\n- writeback target: where durable learning belongs if the work teaches something reusable\\n\\nIf one of these is missing and the gap blocks correctness, surface the gap early and recover it before moving faster.\\n\\n## Why It Exists\\n\\nWork-unit framing prevents shallow completion. It helps agents avoid:\\n\\n- changing files before understanding the target\\n- treating a weak green signal as proof\\n- losing reusable learning in chat\\n- creating duplicate tasks or proposals\\n- turning one-off preferences into global rules\\n- pushing project-specific details into global capability\\n\\n## How To Use It\\n\\nFor simple tasks, keep the work unit implicit but still verify the result.\\n\\nFor ambiguous, high-impact, or multi-step tasks, make the work unit explicit before executing. A compact form is enough:\\n\\n```text\\nGoal:\\nAcceptance:\\nContext:\\nConstraints:\\nEvidence:\\nArtifact:\\nVerification:\\nWriteback:\\n```\\n\\nUse the smallest framing that makes the task correct. Do not turn every request into paperwork.\\n\\n## Writeback\\n\\nWhen the work reveals durable friction, missing capability, stale guidance, or a repeatable workflow, prefer one strong writeback over many weak ones.\\n\\nUse `fclt ai writeback add ...` when the target asset, scope, and evidence are clear. Use `fclt ai evolve ...` only when repeated signal supports a concrete proposal.\\n","skills/capability-evolution/SKILL.md":"---\\ndescription: Convert repeated writeback into concrete fclt capability proposals.\\ntags: [facult, evolution, writeback]\\n---\\n\\n# capability-evolution\\n\\n## When To Use\\nUse this skill when the same missing guidance, weak loop, or recurring win appears often enough that the AI system itself should probably change.\\n\\nDo not wait for a human operator by default if the signal is already clear and the environment permits local AI runtime state to be updated.\\n\\nUse writeback first when the signal is useful but not yet repeated. Use evolution when accumulated writebacks, repeated tool friction, or a clearly missing capability point at a specific target asset or new capability.\\n\\n## Scope Decision\\n\\nChoose `project` when the behavior depends on repo-local architecture or workflow.\\n\\nChoose `global` when the behavior is broadly reusable.\\n\\nIf unsure, start at project scope and promote later with evidence.\\n\\n## Working Flow\\n\\n1. record the strongest writeback\\n2. group or summarize repeated signal\\n3. choose the smallest valid proposal kind\\n4. draft the proposal\\n5. accept only after the target and scope are correct\\n6. apply only when the markdown target is the intended canonical asset\\n\\nUse:\\n\\n```bash\\nfclt ai writeback add ...\\nfclt ai writeback group --by asset\\nfclt ai writeback summarize --by domain\\nfclt ai evolve propose\\nfclt ai evolve draft EV-00001\\nfclt ai evolve draft EV-00001 --append \\"tighten the rule with a concrete verification step\\"\\nfclt ai evolve accept EV-00001\\nfclt ai evolve apply EV-00001\\n```\\n\\nFor background review loops, use:\\n\\n```bash\\nfclt templates init automation learning-review\\nfclt templates init automation evolution-review\\nfclt templates init automation tool-call-audit\\n```\\n\\nIf there is not yet enough repeated signal for evolution, record the writeback and stop there.\\n\\n## Proposal Kind Selection\\n\\n- `update_asset` for tightening existing guidance\\n- `create_asset` for missing instructions or docs\\n- `extract_snippet` for reusable partial guidance\\n- `add_skill` for reusable workflow instruction\\n- `promote_asset` for project-to-global promotion\\n\\nUse task tracking instead of evolution when the main work is an executable tool or product fix that needs an owner, priority, state, or delivery plan. Use evolution for the reusable instruction, skill, or operating-model change that should survive that fix.\\n\\n## Output Contract\\n- repeated signal\\n- proposed asset change\\n- target scope\\n- evidence\\n- smallest useful next step\\n","skills/project-operating-layer-design/SKILL.md":"---\\ndescription: Design or improve a repo-local .ai operating layer.\\ntags: [facult, project, design]\\n---\\n\\n# project-operating-layer-design\\n\\n## When To Use\\nUse this skill when a project needs its own `.ai/` structure, repo-specific instructions, or local bootstrap guidance.\\n\\n## Output Contract\\n- recommended `.ai/` layout\\n- what stays project-local\\n- what stays global\\n- what should remain generated runtime output only\\n"}'
5
5
  ) as Record<string, string>;
package/src/doctor.ts CHANGED
@@ -21,6 +21,7 @@ import {
21
21
  legacyAiIndexPath,
22
22
  } from "./ai-state";
23
23
  import { repairAutosyncServices } from "./autosync";
24
+ import { facultBuiltinPackRoot } from "./builtin";
24
25
  import { parseCliContextArgs, resolveCliContextRoot } from "./cli-context";
25
26
  import { loadManagedState } from "./manage";
26
27
  import { extractServersObject } from "./mcp-config";
@@ -43,10 +44,12 @@ import {
43
44
  import { packageVersion } from "./status";
44
45
 
45
46
  const TOML_FILE_SUFFIX_RE = /\.toml$/;
47
+ const ISO_MILLIS_SUFFIX_RE = /\..+$/;
46
48
 
47
49
  type DoctorHealthState =
48
50
  | "healthy"
49
51
  | "uninitialized"
52
+ | "canonical_source_attention"
50
53
  | "partial_global_config"
51
54
  | "project_generated_only"
52
55
  | "project_policy_attention"
@@ -99,6 +102,8 @@ export interface DoctorReport {
99
102
  generatedGraphExists: boolean;
100
103
  writebackReviewDirExists: boolean;
101
104
  evolutionReviewDirExists: boolean;
105
+ canonicalGlobalDocsValid: boolean;
106
+ canonicalGlobalDocsIssueCodes: string[];
102
107
  projectSyncRepairNeeded: boolean;
103
108
  projectSyncRepairTools: string[];
104
109
  };
@@ -168,6 +173,15 @@ async function pathExists(pathValue: string): Promise<boolean> {
168
173
  }
169
174
  }
170
175
 
176
+ async function pathEntryExists(pathValue: string): Promise<boolean> {
177
+ try {
178
+ await lstat(pathValue);
179
+ return true;
180
+ } catch {
181
+ return false;
182
+ }
183
+ }
184
+
171
185
  async function hasCanonicalSource(rootDir: string): Promise<boolean> {
172
186
  const fileCandidates = [
173
187
  "config.toml",
@@ -252,7 +266,7 @@ async function moveSymlinkIfPossible(
252
266
  ): Promise<boolean> {
253
267
  const sourceTarget = await readlink(src);
254
268
  await mkdir(dirname(dst), { recursive: true });
255
- if (!(await pathExists(dst))) {
269
+ if (!(await pathEntryExists(dst))) {
256
270
  await symlink(sourceTarget, dst);
257
271
  await rm(src, { force: true });
258
272
  return true;
@@ -384,6 +398,13 @@ function normalizeCodexMarketplaceText(text: string): string {
384
398
  }
385
399
  }
386
400
 
401
+ function timestampForBackup(): string {
402
+ return new Date()
403
+ .toISOString()
404
+ .replace(/[-:]/g, "")
405
+ .replace(ISO_MILLIS_SUFFIX_RE, "Z");
406
+ }
407
+
387
408
  async function repairLegacyCodexAuthoringLayout(args: {
388
409
  home: string;
389
410
  rootDir: string;
@@ -425,6 +446,33 @@ async function repairLegacyCodexAuthoringLayout(args: {
425
446
  return { changed, conflicts };
426
447
  }
427
448
 
449
+ async function repairCanonicalGlobalDocs(args: {
450
+ home: string;
451
+ rootDir: string;
452
+ }): Promise<{ changed: boolean; backupPath?: string }> {
453
+ const inspected = await inspectCanonicalGlobalDocs(args.rootDir);
454
+ if (!(inspected.exists && !inspected.valid)) {
455
+ return { changed: false };
456
+ }
457
+
458
+ const sourcePath = join(facultBuiltinPackRoot(), "AGENTS.global.md");
459
+ const targetPath = join(args.rootDir, "AGENTS.global.md");
460
+ const backupPath = join(
461
+ args.rootDir,
462
+ ".facult",
463
+ "backups",
464
+ "doctor",
465
+ `AGENTS.global.${timestampForBackup()}.md`
466
+ );
467
+ await mkdir(dirname(backupPath), { recursive: true });
468
+ await copyFile(targetPath, backupPath);
469
+ await mkdir(dirname(targetPath), { recursive: true });
470
+ await writeFile(targetPath, await readFile(sourcePath, "utf8"), "utf8");
471
+ await ensureAiIndexPath({ homeDir: args.home, rootDir: args.rootDir });
472
+ await ensureAiGraphPath({ homeDir: args.home, rootDir: args.rootDir });
473
+ return { changed: true, backupPath };
474
+ }
475
+
428
476
  async function listProjectSkillNames(rootDir: string): Promise<string[]> {
429
477
  const skillsDir = join(rootDir, "skills");
430
478
  const entries = await readdir(skillsDir, { withFileTypes: true }).catch(
@@ -498,6 +546,57 @@ async function hasProjectGlobalDocs(rootDir: string): Promise<boolean> {
498
546
  );
499
547
  }
500
548
 
549
+ const UNRESOLVED_TEMPLATE_REF_RE = /\$\{[^}\n]+\}/g;
550
+ const FCLTY_BLOCK_RE =
551
+ /<!--\s*fclty:([^>]+?)\s*-->([\s\S]*?)<!--\s*\/fclty:\1\s*-->/g;
552
+
553
+ async function inspectCanonicalGlobalDocs(rootDir: string): Promise<{
554
+ exists: boolean;
555
+ valid: boolean;
556
+ issues: DoctorIssue[];
557
+ }> {
558
+ const pathValue = join(rootDir, "AGENTS.global.md");
559
+ if (!(await pathExists(pathValue))) {
560
+ return { exists: false, valid: true, issues: [] };
561
+ }
562
+
563
+ const text = await readFile(pathValue, "utf8");
564
+ const issues: DoctorIssue[] = [];
565
+ const unresolvedRefs = new Set(text.match(UNRESOLVED_TEMPLATE_REF_RE) ?? []);
566
+ if (unresolvedRefs.size > 0) {
567
+ issues.push({
568
+ severity: "warning",
569
+ code: "canonical-global-docs-unresolved-template",
570
+ message:
571
+ "Canonical AGENTS.global.md contains unresolved template references.",
572
+ fix: "Review the file or refresh the built-in operating model with `fclt templates init operating-model --global --force`.",
573
+ });
574
+ }
575
+
576
+ const emptySections: string[] = [];
577
+ for (const match of text.matchAll(FCLTY_BLOCK_RE)) {
578
+ const key = match[1]?.trim();
579
+ const body = match[2]?.trim();
580
+ if (key && !body) {
581
+ emptySections.push(key);
582
+ }
583
+ }
584
+ if (emptySections.length > 0) {
585
+ issues.push({
586
+ severity: "warning",
587
+ code: "canonical-global-docs-empty-managed-sections",
588
+ message: `Canonical AGENTS.global.md has empty fclty managed sections: ${emptySections.join(", ")}.`,
589
+ fix: "Review the file or refresh the built-in operating model with `fclt templates init operating-model --global --force`.",
590
+ });
591
+ }
592
+
593
+ return {
594
+ exists: true,
595
+ valid: issues.length === 0,
596
+ issues,
597
+ };
598
+ }
599
+
501
600
  async function hasProjectToolRules(
502
601
  rootDir: string,
503
602
  tool: string
@@ -678,6 +777,7 @@ export async function buildDoctorReport(opts?: {
678
777
  generatedGraphExists,
679
778
  writebackReviewDirExists,
680
779
  evolutionReviewDirExists,
780
+ canonicalGlobalDocs,
681
781
  projectSyncPlan,
682
782
  ] = await Promise.all([
683
783
  pathExists(rootDir),
@@ -687,6 +787,7 @@ export async function buildDoctorReport(opts?: {
687
787
  pathExists(generatedGraph),
688
788
  pathExists(writebackReviewDir),
689
789
  pathExists(evolutionReviewDir),
790
+ inspectCanonicalGlobalDocs(rootDir),
690
791
  planProjectSyncPolicyRepair({ home, rootDir }),
691
792
  ]);
692
793
 
@@ -723,6 +824,18 @@ export async function buildDoctorReport(opts?: {
723
824
  });
724
825
  }
725
826
 
827
+ for (const issue of canonicalGlobalDocs.issues) {
828
+ issues.push(issue);
829
+ }
830
+ if (canonicalGlobalDocs.exists && !canonicalGlobalDocs.valid) {
831
+ actions.push({
832
+ id: "refresh-global-operating-model",
833
+ label: "Refresh global operating model",
834
+ command: "fclt templates init operating-model --global --force",
835
+ risk: "canonical_write",
836
+ });
837
+ }
838
+
726
839
  if (generatedOnlyProjectRoot) {
727
840
  issues.push({
728
841
  severity: "error",
@@ -814,6 +927,8 @@ export async function buildDoctorReport(opts?: {
814
927
  state = "project_generated_only";
815
928
  } else if (!(rootExists && canonicalSourceExists)) {
816
929
  state = "uninitialized";
930
+ } else if (!canonicalGlobalDocs.valid) {
931
+ state = "canonical_source_attention";
817
932
  } else if (!(writebackReviewDirExists && evolutionReviewDirExists)) {
818
933
  state = "partial_global_config";
819
934
  } else if (projectSyncPlan.needed) {
@@ -852,6 +967,10 @@ export async function buildDoctorReport(opts?: {
852
967
  generatedGraphExists,
853
968
  writebackReviewDirExists,
854
969
  evolutionReviewDirExists,
970
+ canonicalGlobalDocsValid: canonicalGlobalDocs.valid,
971
+ canonicalGlobalDocsIssueCodes: canonicalGlobalDocs.issues.map(
972
+ (issue) => issue.code
973
+ ),
855
974
  projectSyncRepairNeeded: projectSyncPlan.needed,
856
975
  projectSyncRepairTools,
857
976
  },
@@ -908,6 +1027,16 @@ export async function doctorCommand(argv: string[]) {
908
1027
  let autosyncRepaired = false;
909
1028
  let codexAuthoringRepaired = false;
910
1029
  let codexAuthoringConflicts: string[] = [];
1030
+ let canonicalGlobalDocsRepaired = false;
1031
+ let canonicalGlobalDocsBackupPath: string | undefined;
1032
+ let reviewArtifactsRefreshed:
1033
+ | {
1034
+ writebackCount: number;
1035
+ proposalCount: number;
1036
+ writebackReviewDir: string;
1037
+ evolutionReviewDir: string;
1038
+ }
1039
+ | undefined;
911
1040
  let projectSyncRepairNeeded = false;
912
1041
  let projectSyncRepaired = false;
913
1042
  let projectSyncRepairTools: string[] = [];
@@ -926,6 +1055,17 @@ export async function doctorCommand(argv: string[]) {
926
1055
  });
927
1056
  codexAuthoringRepaired = authoringRepair.changed;
928
1057
  codexAuthoringConflicts = authoringRepair.conflicts;
1058
+ const globalDocsRepair = await repairCanonicalGlobalDocs({
1059
+ home,
1060
+ rootDir,
1061
+ });
1062
+ canonicalGlobalDocsRepaired = globalDocsRepair.changed;
1063
+ canonicalGlobalDocsBackupPath = globalDocsRepair.backupPath;
1064
+ const { refreshAiReviewArtifacts } = await import("./ai");
1065
+ reviewArtifactsRefreshed = await refreshAiReviewArtifacts({
1066
+ homeDir: home,
1067
+ rootDir,
1068
+ });
929
1069
  const projectSyncRepair = await repairProjectSyncPolicy({
930
1070
  home,
931
1071
  rootDir,
@@ -987,6 +1127,16 @@ export async function doctorCommand(argv: string[]) {
987
1127
  console.log(`- ${conflict}`);
988
1128
  }
989
1129
  }
1130
+ if (canonicalGlobalDocsRepaired) {
1131
+ console.log(
1132
+ `Repaired canonical AGENTS.global.md from the built-in operating model. Backup: ${canonicalGlobalDocsBackupPath}`
1133
+ );
1134
+ }
1135
+ if (reviewArtifactsRefreshed) {
1136
+ console.log(
1137
+ `Refreshed AI review artifacts: ${reviewArtifactsRefreshed.writebackCount} writebacks in ${reviewArtifactsRefreshed.writebackReviewDir}, ${reviewArtifactsRefreshed.proposalCount} proposals in ${reviewArtifactsRefreshed.evolutionReviewDir}.`
1138
+ );
1139
+ }
990
1140
  if (projectSyncRepaired && projectSyncRepairPath) {
991
1141
  console.log(
992
1142
  `Materialized explicit project sync policy in ${projectSyncRepairPath} for: ${projectSyncRepairTools.join(", ")}`
@@ -997,6 +1147,16 @@ export async function doctorCommand(argv: string[]) {
997
1147
  `Project sync is still implicit for managed tools (${projectSyncRepairTools.join(", ")}). Run \`fclt doctor --repair\` to write explicit [project_sync.<tool>] entries.`
998
1148
  );
999
1149
  }
1150
+ const canonicalGlobalDocs = await inspectCanonicalGlobalDocs(rootDir);
1151
+ if (canonicalGlobalDocs.exists && !canonicalGlobalDocs.valid) {
1152
+ for (const issue of canonicalGlobalDocs.issues) {
1153
+ console.log(`${issue.message} ${issue.fix ?? ""}`.trim());
1154
+ }
1155
+ if (!repair) {
1156
+ process.exitCode = 1;
1157
+ return;
1158
+ }
1159
+ }
1000
1160
  if (await isGeneratedOnlyProjectRoot({ home, rootDir })) {
1001
1161
  console.log(
1002
1162
  "Project .ai root contains generated state only. Canonical project source is missing, so managed project sync should be treated as unsafe until source is initialized, restored, or management is detached."