@aiwg/cli 2026.10.3 → 2026.10.5

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.
Files changed (279) hide show
  1. package/README.md +2 -2
  2. package/agentic/code/providers/model-capabilities.v1.json +96 -4
  3. package/bin/aiwg.mjs +50 -27
  4. package/dist/src/agents/grounding/knowledge-base.js +54 -6
  5. package/dist/src/artifacts/browser-export.js +6 -0
  6. package/dist/src/artifacts/checksum-manifest.js +1 -0
  7. package/dist/src/artifacts/citation-parser.js +173 -92
  8. package/dist/src/artifacts/citation-table-schema.js +59 -0
  9. package/dist/src/artifacts/cli.js +69 -18
  10. package/dist/src/artifacts/corpus-tools/snapshot.js +1 -1
  11. package/dist/src/artifacts/corpus-tools/vision-extract.js +2 -0
  12. package/dist/src/artifacts/corpus-views/ref-parser.js +3 -1
  13. package/dist/src/artifacts/corpus-views/renderers.js +3 -3
  14. package/dist/src/artifacts/discovery-eval.js +6 -4
  15. package/dist/src/artifacts/fortemi-core-query-adapter.js +36 -3
  16. package/dist/src/artifacts/fortemi-core-sync.js +166 -40
  17. package/dist/src/artifacts/framework-capabilities.js +171 -0
  18. package/dist/src/artifacts/framework-index-paths.js +73 -0
  19. package/dist/src/artifacts/framework-source-digest.js +75 -0
  20. package/dist/src/artifacts/index-builder.js +286 -131
  21. package/dist/src/artifacts/index-files.js +132 -18
  22. package/dist/src/artifacts/index-preparation.js +130 -0
  23. package/dist/src/artifacts/index-refresh.js +591 -0
  24. package/dist/src/artifacts/index-worker.mjs +11 -0
  25. package/dist/src/artifacts/prebuilt-build-lock.js +47 -4
  26. package/dist/src/artifacts/prebuilt-deployment.js +46 -0
  27. package/dist/src/artifacts/prebuilt-relations.js +80 -0
  28. package/dist/src/artifacts/query-engine.js +410 -88
  29. package/dist/src/artifacts/relations-builder.js +168 -0
  30. package/dist/src/artifacts/relations.js +148 -0
  31. package/dist/src/artifacts/stats.js +5 -2
  32. package/dist/src/artifacts/types.js +30 -10
  33. package/dist/src/artifacts/watcher.js +1 -1
  34. package/dist/src/auth/credential-store.js +2 -1
  35. package/dist/src/catalog/cli.d.mts +5 -0
  36. package/dist/src/channel/manager.d.mts +20 -0
  37. package/dist/src/channel/manager.mjs +19 -2
  38. package/dist/src/cli/command-log.js +2 -3
  39. package/dist/src/cli/handlers/artifacts.js +3 -0
  40. package/dist/src/cli/handlers/auth.js +3 -0
  41. package/dist/src/cli/handlers/decision.js +19 -12
  42. package/dist/src/cli/handlers/diagnose.js +4 -3
  43. package/dist/src/cli/handlers/effect.js +4 -2
  44. package/dist/src/cli/handlers/evidence.js +3 -0
  45. package/dist/src/cli/handlers/help.js +1 -0
  46. package/dist/src/cli/handlers/index.js +3 -1
  47. package/dist/src/cli/handlers/init.js +4 -1
  48. package/dist/src/cli/handlers/lint.js +5 -0
  49. package/dist/src/cli/handlers/models.js +104 -8
  50. package/dist/src/cli/handlers/ralph-launcher.js +2 -0
  51. package/dist/src/cli/handlers/refresh.js +12 -0
  52. package/dist/src/cli/handlers/resource-versions.js +3 -0
  53. package/dist/src/cli/handlers/rules.js +195 -0
  54. package/dist/src/cli/handlers/run.js +4 -0
  55. package/dist/src/cli/handlers/runtime-info.js +1 -2
  56. package/dist/src/cli/handlers/script-runner.js +3 -2
  57. package/dist/src/cli/handlers/session.js +19 -9
  58. package/dist/src/cli/handlers/sessions.js +137 -43
  59. package/dist/src/cli/handlers/setup-manifest.js +39 -44
  60. package/dist/src/cli/handlers/setup.js +41 -0
  61. package/dist/src/cli/handlers/steward.js +11 -0
  62. package/dist/src/cli/handlers/subcommands.js +57 -2
  63. package/dist/src/cli/handlers/uhp.js +3 -0
  64. package/dist/src/cli/handlers/use.js +294 -55
  65. package/dist/src/cli/handlers/utilities.js +26 -10
  66. package/dist/src/cli/handlers/version.js +8 -0
  67. package/dist/src/cli/router.js +15 -1
  68. package/dist/src/cli/services/deployment-verification.js +19 -9
  69. package/dist/src/cli/services/provider-prepare-coordinator.js +24 -0
  70. package/dist/src/cli/skill-usage.js +2 -3
  71. package/dist/src/config/aiwg-config.js +87 -37
  72. package/dist/src/config/cli.js +111 -5
  73. package/dist/src/config/decision-config.js +106 -0
  74. package/dist/src/config/dispatch-thresholds-runtime.d.mts +8 -0
  75. package/dist/src/config/gitignore.js +50 -0
  76. package/dist/src/config/index-graph-overrides.js +36 -0
  77. package/dist/src/config/project-artifacts-health.d.mts +37 -0
  78. package/dist/src/config/project-artifacts-runtime.d.mts +16 -0
  79. package/dist/src/config/user-config-dir.d.mts +9 -0
  80. package/dist/src/config/user-config.js +2 -1
  81. package/dist/src/config/user-registry.js +23 -15
  82. package/dist/src/config/user-state.js +7 -0
  83. package/dist/src/decision/ask.js +203 -43
  84. package/dist/src/decision/context-live-qualification.js +2 -2
  85. package/dist/src/decision/driver.js +13 -8
  86. package/dist/src/decision/ensemble/aggregate.js +23 -10
  87. package/dist/src/decision/ensemble-study/multifact-study.js +6 -2
  88. package/dist/src/decision/entry.js +2 -15
  89. package/dist/src/decision/graph-live-qualification.js +1 -1
  90. package/dist/src/decision/graph-run.js +9 -0
  91. package/dist/src/decision/heldout/collector.js +5 -13
  92. package/dist/src/decision/heldout/contract.js +30 -7
  93. package/dist/src/decision/jev-credentials.js +15 -1
  94. package/dist/src/decision/ledger.js +131 -28
  95. package/dist/src/decision/offload-activation.js +137 -0
  96. package/dist/src/decision/offload.js +550 -0
  97. package/dist/src/decision/qualification/load.js +4 -6
  98. package/dist/src/decision/savings.js +28 -0
  99. package/dist/src/effects/types.js +5 -1
  100. package/dist/src/effects/verifiers/git.js +2 -1
  101. package/dist/src/effects/verifiers/tracker.js +26 -14
  102. package/dist/src/extensions/commands/definitions.js +19 -0
  103. package/dist/src/extensions/project-local-remove.js +36 -1
  104. package/dist/src/gates/providers/loader.js +61 -17
  105. package/dist/src/installation/manager-command.d.mts +18 -0
  106. package/dist/src/installation/manager-command.mjs +14 -2
  107. package/dist/src/installation/manager.d.mts +49 -0
  108. package/dist/src/installation/manager.mjs +78 -3
  109. package/dist/src/issues/cli.js +4 -4
  110. package/dist/src/issues/local.js +24 -5
  111. package/dist/src/jobs/runner.js +1 -1
  112. package/dist/src/lint/cli.js +27 -1
  113. package/dist/src/lint/loader.js +2 -1
  114. package/dist/src/lint/reporters.js +25 -1
  115. package/dist/src/lint/rule-activation.js +43 -0
  116. package/dist/src/lint/runner.js +137 -62
  117. package/dist/src/mcp/adapters/codex-runtime.js +7 -1
  118. package/dist/src/mcp/claude-install.d.mts +4 -0
  119. package/dist/src/mcp/claude-install.mjs +41 -0
  120. package/dist/src/mcp/cli.d.mts +1 -0
  121. package/dist/src/mcp/cli.mjs +36 -11
  122. package/dist/src/mcp/config-file.d.mts +28 -0
  123. package/dist/src/mcp/config-layers.d.mts +36 -0
  124. package/dist/src/mcp/credentials.d.mts +51 -0
  125. package/dist/src/mcp/grok-build-config.d.mts +6 -0
  126. package/dist/src/mcp/grok-build-config.mjs +1 -1
  127. package/dist/src/mcp/helpers.mjs +3 -2
  128. package/dist/src/mcp/muse-native-config.d.mts +13 -0
  129. package/dist/src/mcp/muse-native-config.mjs +1 -0
  130. package/dist/src/mcp/omp-config.d.mts +4 -0
  131. package/dist/src/mcp/registry.js +3 -93
  132. package/dist/src/mcp/registry.mjs +3 -87
  133. package/dist/src/mcp/server-config.d.mts +3 -0
  134. package/dist/src/mcp/server-config.mjs +89 -0
  135. package/dist/src/mcp/server.mjs +37 -375
  136. package/dist/src/mcp/test-simple.mjs +2 -1
  137. package/dist/src/mcp/toml-editor.d.mts +4 -0
  138. package/dist/src/mcp/toml-string.d.mts +1 -0
  139. package/dist/src/mcp/toml-strip-mcp.d.mts +1 -0
  140. package/dist/src/mcp/tool-filters.d.mts +46 -0
  141. package/dist/src/mcp/tools/decision.mjs +12 -11
  142. package/dist/src/mcp/tools/discovery.mjs +62 -55
  143. package/dist/src/mcp/tools/orchestration.mjs +12 -2
  144. package/dist/src/mcp/tools/worker-invoke.mjs +1340 -23
  145. package/dist/src/mcp/tools/worker-shell-control.mjs +52 -21
  146. package/dist/src/mcp/tools/worker-stdin-runtime.mjs +3 -3
  147. package/dist/src/mission-protocol/codecs.js +8 -0
  148. package/dist/src/models/config-loader.js +7 -2
  149. package/dist/src/models/model-capabilities.v1.json +96 -4
  150. package/dist/src/models/model-discovery.js +76 -11
  151. package/dist/src/models/provider-policy.js +101 -7
  152. package/dist/src/models/qualification-digest.d.mts +2 -0
  153. package/dist/src/models/resolver.js +12 -10
  154. package/dist/src/models/worker-direct-readiness.js +65 -44
  155. package/dist/src/models/worker-dispatch-offer.js +3 -3
  156. package/dist/src/models/worker-health-readiness.js +16 -3
  157. package/dist/src/models/worker-installation.d.mts +39 -0
  158. package/dist/src/models/worker-installation.mjs +135 -28
  159. package/dist/src/process/bounded-child-process.d.mts +84 -0
  160. package/dist/src/process/bounded-child-process.mjs +43 -0
  161. package/dist/src/process/cli-command.d.mts +16 -0
  162. package/dist/src/process/signal-child.d.mts +6 -0
  163. package/dist/src/process/signal-child.mjs +7 -0
  164. package/dist/src/providers/deployed-skill-roots.js +20 -0
  165. package/dist/src/providers/grok-build-acp.js +3 -2
  166. package/dist/src/providers/grok-build-headless.js +12 -4
  167. package/dist/src/providers/grok-build-inspect.js +1 -0
  168. package/dist/src/providers/omp-agent.d.mts +8 -0
  169. package/dist/src/providers/omp-diagnostics.d.mts +1 -0
  170. package/dist/src/providers/omp-paths.d.mts +13 -0
  171. package/dist/src/providers/skill-ownership.d.mts +9 -0
  172. package/dist/src/providers/skill-ownership.mjs +88 -0
  173. package/dist/src/rules/activation.d.mts +21 -0
  174. package/dist/src/rules/activation.mjs +62 -0
  175. package/dist/src/rules/inline-budget.d.mts +26 -0
  176. package/dist/src/rules/inline-budget.mjs +66 -0
  177. package/dist/src/rules/inventory.js +129 -0
  178. package/dist/src/rules/pin-state.js +73 -0
  179. package/dist/src/rules/pins.d.mts +14 -0
  180. package/dist/src/rules/pins.mjs +112 -0
  181. package/dist/src/rules/rule-frontmatter.schema.json +197 -0
  182. package/dist/src/serve/mission-conductor.js +38 -12
  183. package/dist/src/serve/worker-invocation.js +30 -10
  184. package/dist/src/serve/worker-stdin-frame.d.mts +10 -0
  185. package/dist/src/sessions/adapter-registry.js +23 -19
  186. package/dist/src/sessions/adapters/claude.js +39 -54
  187. package/dist/src/sessions/adapters/codex.js +95 -67
  188. package/dist/src/sessions/adapters/copilot.js +4 -4
  189. package/dist/src/sessions/adapters/cursor.js +40 -57
  190. package/dist/src/sessions/adapters/deepseek-harness.js +15 -12
  191. package/dist/src/sessions/adapters/factory.js +31 -27
  192. package/dist/src/sessions/adapters/generic.js +10 -10
  193. package/dist/src/sessions/adapters/grok-build.js +11 -11
  194. package/dist/src/sessions/adapters/grokbot.js +24 -13
  195. package/dist/src/sessions/adapters/hermes.js +27 -29
  196. package/dist/src/sessions/adapters/muse-workspace-scan.js +31 -11
  197. package/dist/src/sessions/adapters/muse.js +150 -93
  198. package/dist/src/sessions/adapters/omp.js +28 -14
  199. package/dist/src/sessions/adapters/openclaw.js +89 -79
  200. package/dist/src/sessions/adapters/opencode.js +27 -24
  201. package/dist/src/sessions/adapters/openhuman.js +18 -60
  202. package/dist/src/sessions/adapters/pi.js +29 -14
  203. package/dist/src/sessions/adapters/warp.js +3 -3
  204. package/dist/src/sessions/adapters/windsurf.js +19 -48
  205. package/dist/src/sessions/batch-contracts.js +16 -8
  206. package/dist/src/sessions/batch-import.js +252 -12
  207. package/dist/src/sessions/contracts.js +50 -3
  208. package/dist/src/sessions/coverage-preflight.js +27 -2
  209. package/dist/src/sessions/export-plan.js +4 -3
  210. package/dist/src/sessions/import-exclusions.js +72 -0
  211. package/dist/src/sessions/import-lease.js +34 -18
  212. package/dist/src/sessions/import-limits.js +37 -0
  213. package/dist/src/sessions/importer.js +60 -38
  214. package/dist/src/sessions/lifecycle.js +45 -0
  215. package/dist/src/sessions/policy.js +145 -26
  216. package/dist/src/sessions/readers.js +106 -53
  217. package/dist/src/sessions/refresh.js +105 -0
  218. package/dist/src/sessions/repository.js +151 -54
  219. package/dist/src/sessions/workspace-discovery.js +292 -60
  220. package/dist/src/sessions/workspace-matching.js +107 -0
  221. package/dist/src/skills/importer.js +1 -0
  222. package/dist/src/smiths/context-pipeline/aiwg-md.js +11 -0
  223. package/dist/src/smiths/context-pipeline/claude-hook.js +3 -26
  224. package/dist/src/smiths/context-pipeline/finalization.js +6 -5
  225. package/dist/src/smiths/context-pipeline/generator.js +21 -4
  226. package/dist/src/smiths/context-pipeline/parallelism-section.js +7 -15
  227. package/dist/src/smiths/mcpsmith/generator.js +1 -1
  228. package/dist/src/smiths/toolsmith/runtime-discovery.d.mts +10 -0
  229. package/dist/src/smiths/toolsmith/runtime-discovery.mjs +76 -18
  230. package/dist/src/storage/backends/fs.js +18 -76
  231. package/dist/src/storage/backends/obsidian.js +1 -0
  232. package/dist/src/tracker/forge-http.js +2 -1
  233. package/dist/src/update/checker.d.mts +2 -0
  234. package/dist/src/update/service.d.mts +22 -0
  235. package/dist/src/utils/contained-filesystem.d.mts +37 -0
  236. package/dist/src/utils/contained-filesystem.mjs +683 -0
  237. package/dist/src/utils/temporary-directory.js +36 -0
  238. package/dist/src/writing/example-generator.js +1 -1
  239. package/package.json +15 -2
  240. package/schemas/models/model-catalog.v1.schema.json +1 -1
  241. package/schemas/models/provider-model-capabilities.v1.schema.json +6 -0
  242. package/tools/_resolve-impl.mjs +13 -15
  243. package/tools/agents/deploy-agents.mjs +49 -21
  244. package/tools/agents/providers/base.mjs +104 -134
  245. package/tools/agents/providers/claude.mjs +8 -24
  246. package/tools/agents/providers/codex.mjs +18 -11
  247. package/tools/agents/providers/cursor.mjs +19 -18
  248. package/tools/agents/providers/grok-build.mjs +3 -1
  249. package/tools/agents/providers/hermes.mjs +57 -47
  250. package/tools/agents/providers/openclaw.mjs +9 -4
  251. package/tools/agents/providers/pi.mjs +5 -0
  252. package/tools/agents/providers/provider-prepare-channel.mjs +5 -0
  253. package/tools/agents/providers/provider-prepare-coordinator.mjs +247 -0
  254. package/tools/agents/providers/provider-prepare-worker.mjs +84 -0
  255. package/tools/agents/providers/rule-delivery.mjs +1270 -0
  256. package/tools/agents/providers/warp.mjs +20 -26
  257. package/tools/cli/decision-offload-report.mjs +40 -0
  258. package/tools/cli/workspace-status.mjs +862 -0
  259. package/tools/plugin/package-plugins.mjs +186 -8
  260. package/tools/plugin/plugin-links.mjs +114 -0
  261. package/tools/providers/antigravity-transport.mjs +3 -1
  262. package/tools/providers/deepseek-harness-transport.mjs +6 -4
  263. package/tools/providers/omp-transport.mjs +4 -2
  264. package/tools/ralph-external/lib/consecutive-failure-circuit-breaker.mjs +1748 -0
  265. package/tools/ralph-external/lib/launcher-recovery-evidence.mjs +55 -0
  266. package/tools/ralph-external/lib/omp-adapter.mjs +1 -1
  267. package/tools/ralph-external/lib/pi-adapter.mjs +0 -4
  268. package/tools/ralph-external/lib/provider-adapter.mjs +59 -1
  269. package/tools/ralph-external/lib/recovery-probe.mjs +72 -0
  270. package/tools/ralph-external/lib/session-environment.mjs +28 -0
  271. package/tools/ralph-external/lib/systemd-client.mjs +49 -0
  272. package/tools/ralph-external/lib/worker-health.mjs +1 -1
  273. package/tools/ralph-external/lib/worker-isolation.mjs +717 -0
  274. package/tools/ralph-external/lib/worker-overflow.mjs +50 -0
  275. package/tools/ralph-external/process-monitor.mjs +1017 -0
  276. package/tools/ralph-external/session-launcher.mjs +5057 -0
  277. package/tools/scripts/resource-preflight.mjs +541 -0
  278. package/tools/skills/deploy-skills-codex.mjs +2 -1
  279. package/vscode-extension/schemas/aiwg.config.v1.json +1194 -231
@@ -22,6 +22,7 @@
22
22
  * @tests @test/unit/artifacts/citation-parser.test.ts
23
23
  */
24
24
  import { parseFrontmatter } from './index-builder.js';
25
+ import { CITATION_HEADER_ALIASES, CITATION_SECTION_ALIASES } from './citation-table-schema.js';
25
26
  /**
26
27
  * Match a single node identifier (REF-* or PROF-*) anywhere in a string.
27
28
  * Used by `extractRefsFromTable` to pull every ID out of a table cell.
@@ -33,31 +34,71 @@ const NODE_ID_PATTERN = /(?:REF-\d+[a-z]?|PROF-[POFG]-[a-z0-9-]+)/g;
33
34
  * frontmatter `ref` values.
34
35
  */
35
36
  const NODE_ID_FULL = /^(?:REF-\d+[a-z]?|PROF-[POFG]-[a-z0-9-]+)$/;
36
- /** Explicit column aliases used by historical citation-sidecar variants. */
37
- const CITATION_REF_COLUMNS = [
38
- 'Inducted REF',
39
- 'REF',
40
- 'Corpus REF',
41
- 'In-corpus REF',
42
- ];
43
- /**
44
- * Preserve the corpus snapshot's legacy edge semantics: every node ID on a
45
- * pipe-delimited line inside an outgoing/incoming section is a declaration.
46
- * Explicit column parsing remains the primary path, while this compatibility
47
- * scan covers malformed rows and prose continuations that historical corpus
48
- * snapshots already count.
49
- */
50
- function extractSectionNodeIds(section) {
51
- const refs = [];
52
- for (const line of section.split('\n')) {
53
- if (!line.includes('|'))
54
- continue;
55
- NODE_ID_PATTERN.lastIndex = 0;
56
- const matches = line.match(NODE_ID_PATTERN);
57
- if (matches)
58
- refs.push(...matches);
37
+ /** An unnamed resolution cell must contain only one REF, optionally decorated. */
38
+ function standaloneRef(value) {
39
+ let text = value.trim();
40
+ const link = text.match(/^\[([^\]]+)\]\([^\s]+(?:\s+"[^"\n]*")?\)$/);
41
+ if (link)
42
+ text = link[1].trim();
43
+ const code = text.match(/^`([^`]+)`$/);
44
+ if (code)
45
+ text = code[1].trim();
46
+ return /^REF-\d+[a-z]?$/.test(text) ? text : null;
47
+ }
48
+ /** Resolution aliases are shared by every registered bibliographic form. */
49
+ const CITATION_REF_COLUMNS = CITATION_HEADER_ALIASES.ref;
50
+ const HEADER_ROLES = new Map(Object.entries(CITATION_HEADER_ALIASES).flatMap(([role, aliases]) => aliases.map(alias => [alias, role])));
51
+ function normalizeCell(value) {
52
+ return value.toLowerCase().replace(/[`*_]/g, '').replace(/\s+/g, ' ').trim();
53
+ }
54
+ /** Direction belongs to the heading, never to a mention in its section body. */
55
+ function citationDirection(heading) {
56
+ const text = normalizeCell(heading.replace(/[ \t]+#+[ \t]*$/, ''));
57
+ const segment = text.split(/[/—:(]/, 1)[0].trim();
58
+ // Keep the corpus's Incoming/Outgoing descriptions, with a word boundary
59
+ // so unrelated words such as "Incomingish" cannot declare citations.
60
+ if (/^incoming(?:\s|$)/.test(segment))
61
+ return 'incoming';
62
+ if (/^outgoing(?:\s|$)/.test(segment))
63
+ return 'outgoing';
64
+ for (const direction of ['incoming', 'outgoing']) {
65
+ if (CITATION_SECTION_ALIASES[direction].some(alias => text === alias
66
+ || (text.startsWith(alias) && /^\s*[/—:(]/.test(text.slice(alias.length)))))
67
+ return direction;
59
68
  }
60
- return [...new Set(refs)];
69
+ return null;
70
+ }
71
+ /** Explicitly non-bibliographic or uncertain declarations must not assert citations. */
72
+ function isNonCitation(value) {
73
+ const text = normalizeCell(value);
74
+ // An explicit disavowal wins even when the description also says "cites".
75
+ if (/\b(?:topical|thematic|non[- ]citations?|methodological cousin|unverified)\b/.test(text)
76
+ || /\bnot (?:(?:a|an) )?(?:(?:verified|formal|directly) )?(?:citations?|cites?|cited|verified)\b/.test(text)
77
+ || /\bno (?:formal |verified )?citations?\b/.test(text)
78
+ || /\b(?:if|unless|provided that|assuming)\s+(?:ref-\d+|prof-[pofg]-|present\b)/.test(text)
79
+ || /\bconditional (?:on|upon)\b/.test(text)
80
+ || /^(?:conditional|related)(?:\s+only)?\s*(?:[(:;—-]|$)/.test(text))
81
+ return true;
82
+ // "Cites X in Related Work", "cites-related-version", and citations to a
83
+ // paper named "Conditional Memory" describe real citations, not qualifiers.
84
+ if (/\b(?:cites?|cited|citing)\b/.test(text))
85
+ return false;
86
+ return /\brelated\b/.test(text);
87
+ }
88
+ /** Table/section declarations, rather than incidental mentions in audit prose. */
89
+ function isNonCitationContext(value) {
90
+ // Candidate-specific audit prose does not disavow the surrounding tables.
91
+ // Keep whole-table declarations separate from notes about particular REFs.
92
+ const text = value.replace(/<!--[\s\S]*?-->/g, '').toLowerCase().replace(/[`*_]/g, '').split('\n')
93
+ .filter(line => !/\b(?:search results|external .*?candidates?|historical records)\b/.test(line))
94
+ .filter(line => !/(?:ref-\d+|prof-[pofg]-)/.test(line)
95
+ || /\b(?:rows|tables?|relationships?|connections?) (?:below|are|only)\b/.test(line))
96
+ .join('\n');
97
+ return /(?:^|\n)\s*(?:related|topical|thematic) works[^\n]*\b(?:do|does) not (?:directly )?cite\b/.test(text)
98
+ || /\b(?:no (?:formal|verified) citations?|not (?:(?:asserted|verified) )?citation (?:edges?|rows?)|relationship notes|related corpus nodes)\b/.test(text)
99
+ || /\bno citations\s*(?:[.!;]|$)/.test(text)
100
+ || /\b(?:topical|thematic|related|conditional|non-citation) (?:relationships?|connections?|only)\b/.test(text)
101
+ || /(?:^|\n)\s*(?:topical|thematic|related|conditional|non-citation)(?:\s+(?:relationships?|connections?|notes|rows|only))?\s*(?:$|\n)/.test(text);
61
102
  }
62
103
  /**
63
104
  * Test whether a string is a valid sidecar node identifier.
@@ -73,75 +114,102 @@ export function isNodeId(value) {
73
114
  * Extract node identifiers from a markdown table column.
74
115
  *
75
116
  * Scans table rows for a column matching `columnName` (case-insensitive)
76
- * and extracts node IDs (REF-* or PROF-*), skipping empty/dash values.
117
+ * and extracts node IDs (REF-* or PROF-*) only from that cell. Malformed
118
+ * tables/rows and explicit non-citation qualifiers are skipped.
77
119
  *
78
120
  * @param tableText - Markdown table text (header + separator + rows)
79
121
  * @param columnName - Column header to extract from (e.g., "Inducted REF")
80
122
  * @returns Array of node identifiers found
81
123
  */
82
124
  export function extractRefsFromTable(tableText, columnName) {
83
- const aliases = (Array.isArray(columnName) ? columnName : [columnName])
84
- .map(name => name.trim().toLowerCase());
85
- const tableBlocks = [];
86
- let currentBlock = [];
87
- for (const line of tableText.split('\n')) {
88
- if (line.trim().startsWith('|')) {
89
- currentBlock.push(line);
90
- }
91
- else if (currentBlock.length > 0) {
92
- tableBlocks.push(currentBlock);
93
- currentBlock = [];
94
- }
95
- }
96
- if (currentBlock.length > 0)
97
- tableBlocks.push(currentBlock);
98
- const parseRow = (line) => {
99
- let row = line.trim();
100
- if (row.startsWith('|'))
101
- row = row.slice(1);
102
- if (row.endsWith('|'))
103
- row = row.slice(0, -1);
104
- return row.split('|').map(cell => cell.trim());
105
- };
125
+ return extractTableRefs(tableText, columnName, false);
126
+ }
127
+ function extractTableRefs(tableText, columnName, bibliographicOnly) {
128
+ const aliases = (typeof columnName === 'string' ? [columnName] : columnName)
129
+ .map(normalizeCell);
130
+ const parseRow = (line) => line.trim().replace(/^\|/, '').replace(/\|$/, '').split(/(?<!\\)\|/)
131
+ .map(cell => cell.replace(/\\\|/g, '|').trim());
132
+ const lines = tableText.split('\n');
133
+ const headings = [];
134
+ let preamble = [];
106
135
  const refs = [];
107
- for (const lines of tableBlocks) {
108
- const headerCells = parseRow(lines[0]);
109
- const colIndex = headerCells.findIndex(header => {
110
- const normalized = header.toLowerCase().replace(/[`*_]/g, '').trim();
111
- return aliases.some(alias => normalized === alias ||
112
- normalized.startsWith(`${alias} /`) ||
113
- normalized.startsWith(`${alias} (`));
114
- });
115
- if (lines.length < 3 || colIndex === -1) {
116
- // Legacy sidecars sometimes have headerless continuation rows or a
117
- // generic table whose rows were extended with a final REF cell. The
118
- // corpus snapshot has always treated every in-section table REF as an
119
- // outgoing/incoming declaration, so retain that compatibility fallback.
120
- const dataLines = lines.length >= 3 ? lines.slice(2) : lines;
121
- for (const line of dataLines) {
122
- NODE_ID_PATTERN.lastIndex = 0;
123
- const refMatches = line.match(NODE_ID_PATTERN);
124
- if (refMatches)
125
- refs.push(...refMatches);
126
- }
136
+ for (let i = 0; i < lines.length; i++) {
137
+ const heading = lines[i].match(/^ {0,3}(#{1,6})[ \t]+(.+)/);
138
+ if (heading) {
139
+ // A parent section's declaration also governs its nested tables.
140
+ const parent = headings[headings.length - 1];
141
+ if (parent && parent.level < heading[1].length)
142
+ parent.text += `\n${preamble.join('\n')}`;
143
+ while (headings.length && headings[headings.length - 1].level >= heading[1].length)
144
+ headings.pop();
145
+ headings.push({ level: heading[1].length, text: heading[2] });
146
+ preamble = [];
147
+ continue;
148
+ }
149
+ if (!lines[i].trim().startsWith('|')) {
150
+ preamble.push(lines[i]);
127
151
  continue;
128
152
  }
129
- // Skip this table's header and separator, preserving all interior cells.
130
- for (let i = 2; i < lines.length; i++) {
131
- const cells = parseRow(lines[i]);
132
- if (colIndex >= cells.length)
153
+ const table = [];
154
+ while (i < lines.length && lines[i].trim().startsWith('|'))
155
+ table.push(lines[i++]);
156
+ i--;
157
+ const context = [...headings.map(h => h.text), ...preamble];
158
+ preamble = [];
159
+ // A trailing note can explicitly disavow the preceding table's citations.
160
+ const following = [];
161
+ for (let j = i + 1; j < lines.length && !/^\s*[#|]/.test(lines[j]); j++)
162
+ following.push(lines[j]);
163
+ if (/^\s*note\b/im.test(following.join('\n')))
164
+ context.push(...following);
165
+ if (isNonCitationContext(context.join('\n')) || table.length < 3)
166
+ continue;
167
+ const headers = parseRow(table[0]).map(normalizeCell);
168
+ if (headers.some(isNonCitation))
169
+ continue;
170
+ const separator = parseRow(table[1]);
171
+ if (separator.length !== headers.length || !separator.every(cell => /^:?-{3,}:?$/.test(cell)))
172
+ continue;
173
+ const roles = headers.map(header => HEADER_ROLES.get(header));
174
+ // Incoming/Outgoing declares the direction; the named resolution header
175
+ // identifies the target when present. Otherwise whole-cell REF IDs
176
+ // resolve rows. Additional metadata and column order do not change
177
+ // those semantics. Multiple resolution columns are ambiguous.
178
+ if (bibliographicOnly && roles.filter(role => role === 'ref').length > 1)
179
+ continue;
180
+ // For graph extraction the headers register exactly one resolution
181
+ // column when present. The generic column helper still honors its explicit alias order.
182
+ const colIndex = bibliographicOnly
183
+ ? roles.indexOf('ref')
184
+ : aliases.map(alias => headers.indexOf(alias)).find(index => index !== -1) ?? -1;
185
+ if (colIndex === -1 && !bibliographicOnly)
186
+ continue;
187
+ for (const line of table.slice(2)) {
188
+ const cells = parseRow(line);
189
+ // Extra/missing cells have no unambiguous column semantics. Never guess.
190
+ if (cells.length !== headers.length)
191
+ continue;
192
+ // A resolution explicitly labelled as context/adjacency does not map
193
+ // the printed entry to that node (corpus Formal References variants).
194
+ if (colIndex !== -1
195
+ && /\b(?:REF-\d+[a-z]?|PROF-[POFG]-[a-z0-9-]+)\s+(?:(?:family )?context|adjacent)\b/i.test(normalizeCell(cells[colIndex])))
133
196
  continue;
134
- // Historical rows may expand the named REF cell into an issue/REF pair
135
- // or append placeholder cells without extending the header. Scan from
136
- // the named column through the remainder of the row; columns before the
137
- // alias (title/authors/year) remain excluded.
138
- const value = cells.slice(colIndex).join(' | ');
139
- if (!value || value === '—' || value === '-' || value === '–')
197
+ // A condition can also qualify the printed title, as in the downstream
198
+ // "Learning is Forgetting (if REF-828 in 6-benchmark suite)" row.
199
+ if (cells.some(cell => /\b(?:if|unless|provided that|assuming)\s+[`*_[\s]*(?:REF-\d+|PROF-[POFG]-)/i.test(cell)))
140
200
  continue;
141
- NODE_ID_PATTERN.lastIndex = 0;
142
- const refMatches = value.match(NODE_ID_PATTERN);
143
- if (refMatches)
144
- refs.push(...refMatches);
201
+ const qualifiers = cells.filter((_, index) => (bibliographicOnly && colIndex === -1) || index === colIndex
202
+ || ['notes', 'evidence', 'context', 'kind', 'relationship', 'status'].includes(roles[index] ?? '')
203
+ || (!bibliographicOnly && !['#', 'title', 'work', 'authors', 'year', 'doi/url', 'doi', 'url'].includes(headers[index])));
204
+ if (qualifiers.some(isNonCitation))
205
+ continue;
206
+ // Without a named resolution column, only whole-cell REF IDs resolve.
207
+ // Named columns always take precedence over incidental IDs elsewhere.
208
+ const matches = colIndex === -1
209
+ ? cells.map(standaloneRef).filter((ref) => ref !== null)
210
+ : cells[colIndex].match(NODE_ID_PATTERN);
211
+ if (matches)
212
+ refs.push(...matches);
145
213
  }
146
214
  }
147
215
  return [...new Set(refs)];
@@ -149,6 +217,12 @@ export function extractRefsFromTable(tableText, columnName) {
149
217
  /**
150
218
  * Parse a citation sidecar markdown file into structured edges.
151
219
  *
220
+ * Tables in recognized directional or reference sections declare citations through a unique
221
+ * registered resolution column, or whole-cell REF IDs when no such column
222
+ * exists, independent of other metadata columns.
223
+ * Topical, related, conditional and explicitly non-citation declarations are
224
+ * skipped; they do not produce citation edges or new relationship types.
225
+ *
152
226
  * @param content - Full markdown content of the sidecar file
153
227
  * @returns Parse result with ref ID and edge arrays, or null if not a valid sidecar
154
228
  */
@@ -158,22 +232,29 @@ export function parseCitationSidecar(content) {
158
232
  const ref = typeof data.ref === 'string' ? data.ref : null;
159
233
  if (!ref || !NODE_ID_FULL.test(ref))
160
234
  return null;
161
- // Split body into sections by ## headings
162
- const sections = body.split(/^## /m).filter(Boolean);
235
+ // ATX level-two headings allow indentation, tabs and closing hashes.
236
+ const sectionHeading = /^ {0,3}##[ \t]+([^\r\n]+)/m;
237
+ const sections = body.split(/(?=^ {0,3}##[ \t]+)/m).filter(Boolean);
238
+ const preamble = sectionHeading.test(sections[0] ?? '') ? '' : sections[0] ?? '';
239
+ if (isNonCitationContext(preamble.replace(/^#+ .*$/gm, '')))
240
+ return { ref, cites: [], citedBy: [] };
163
241
  let cites = [];
164
242
  let citedBy = [];
165
243
  for (const section of sections) {
166
- const sectionLower = section.toLowerCase();
167
- if (sectionLower.startsWith('outgoing')) {
168
- cites.push(...extractRefsFromTable(section, CITATION_REF_COLUMNS));
169
- cites.push(...extractSectionNodeIds(section));
244
+ const heading = section.match(sectionHeading);
245
+ const direction = heading ? citationDirection(heading[1]) : null;
246
+ if (direction === 'outgoing') {
247
+ cites.push(...extractTableRefs(section, CITATION_REF_COLUMNS, true));
170
248
  }
171
- else if (sectionLower.startsWith('incoming')) {
172
- citedBy.push(...extractRefsFromTable(section, CITATION_REF_COLUMNS));
173
- citedBy.push(...extractSectionNodeIds(section));
249
+ else if (direction === 'incoming') {
250
+ citedBy.push(...extractTableRefs(section, CITATION_REF_COLUMNS, true));
174
251
  }
175
252
  }
176
- return { ref, cites: [...new Set(cites)], citedBy: [...new Set(citedBy)] };
253
+ return {
254
+ ref,
255
+ cites: [...new Set(cites)].filter(target => target !== ref),
256
+ citedBy: [...new Set(citedBy)].filter(source => source !== ref),
257
+ };
177
258
  }
178
259
  /**
179
260
  * Convert a CitationParseResult into TypedEdge arrays for the dependency graph.
@@ -0,0 +1,59 @@
1
+ /**
2
+ * Registered column aliases for citation-sidecar edge extraction.
3
+ *
4
+ * Based on the research-papers corpus table contract
5
+ * (scripts/corpus/citation-sidecar.schema.json, schemaVersion 1). A unique
6
+ * resolution header identifies the target cell in Incoming/Outgoing tables.
7
+ * Without one, a whole-cell REF ID can resolve a row.
8
+ * Other columns supply metadata or explicit qualifiers that disavow citations;
9
+ * they do not need to follow an enumerated table shape.
10
+ */
11
+ /** Direction names used as whole headings or before /, —, :, or (. */
12
+ export const CITATION_SECTION_ALIASES = {
13
+ incoming: ['cited-by', 'cited by', 'papers that cite this work', 'sources that cite this work', 'works that cite this work'],
14
+ outgoing: [
15
+ 'cites', 'references', 'papers this work cites', 'sources this work cites', 'works this work cites',
16
+ 'formal / high-value references extracted',
17
+ 'formal / high-value references and benchmarks extracted',
18
+ 'formal references extracted from the card',
19
+ 'formal references extracted from the addendum / parent card',
20
+ 'high-value references and benchmarks',
21
+ 'foundational external citations',
22
+ ],
23
+ };
24
+ export const CITATION_HEADER_ALIASES = {
25
+ ordinal: ['#', 'bibliography #', 'label', 'row', 'original label', '# (arxiv)'],
26
+ title: [
27
+ 'title', 'citing work', 'cited work', 'citation', 'citation (extracted)', 'title / source',
28
+ 'title / work', 'title / source bibliographic entry', 'citation as printed (normalized whitespace)',
29
+ 'citation as printed or faithfully transcribed', 'cited source / concept',
30
+ 'mentioned work / cluster', 'work cited / discussed', 'source', 'artifact', 'title / referent',
31
+ 'work / cluster', 'source-reported reference', 'title / source label', 'title / target', 'entry',
32
+ 'reference', 'reference (as printed, cvpr 2026 camera-ready)', 'reference (as printed, arxiv v2)',
33
+ ],
34
+ authors: [
35
+ 'authors', 'authors / publisher', 'organization', 'party', 'authors / project', 'author(s)',
36
+ 'authors / attributed entity', 'authors (as printed)', 'author or owner', 'authors / org',
37
+ ],
38
+ year: ['year'],
39
+ locator: [
40
+ 'doi/url', 'doi / url', 'url', 'identifier', 'venue / doi / url', 'doi/url/venue', 'venue / url',
41
+ 'doi/url or venue', 'venue / id', 'venue / identifier', 'identifier / url', 'venue / doi',
42
+ ],
43
+ ref: [
44
+ 'inducted ref', 'corpus node', 'corpus ref', 'in-corpus ref', 'ref', 'citing ref', 'ref / issue',
45
+ 'in-corpus ref / issue', 'corpus id',
46
+ ],
47
+ context: ['context', 'where it appears'],
48
+ notes: ['notes', 'note', 'rationale', 'why the guarded matcher missed it'],
49
+ evidence: [
50
+ 'source evidence', 'in-text evidence', 'provenance', 'evidence', 'basis', 'verification',
51
+ 'confirmed by',
52
+ ],
53
+ 'their-row': ['their row', '[ref]', 'row in its sidecar'],
54
+ venue: ['venue'],
55
+ kind: ['kind', 'type', 'nature'],
56
+ relationship: ['relationship', 'relation', 'relationship (conceptual, not citation-grounded)'],
57
+ status: ['mapping status', 'status', 'mapping'],
58
+ };
59
+ //# sourceMappingURL=citation-table-schema.js.map
@@ -20,6 +20,7 @@ import path from 'node:path';
20
20
  import { readFileSync } from 'node:fs';
21
21
  import { fileURLToPath } from 'node:url';
22
22
  import { GRAPH_CONFIGS, OPERATIONAL_DISCOVERY_TYPES, OPERATIONAL_SHOW_TYPES, isOperationalShowType, loadUserGraphConfigs, loadGlobalGraphConfigs, orderedGraphEntries, } from './types.js';
23
+ import { resolveIndexWorkers } from './index-preparation.js';
23
24
  import { SUPPORTED_VIEWS } from './corpus-views/renderers.js';
24
25
  import { parseResourceSelector, readVerifiedRegularFile, } from '../resources/web-release.js';
25
26
  import { findPackageRoot } from '../cli/find-package-root.js';
@@ -213,6 +214,15 @@ export async function main(args) {
213
214
  process.exit(0);
214
215
  }
215
216
  switch (subcommand) {
217
+ case 'refresh': {
218
+ if (subcommandArgs.includes('--help') || subcommandArgs.includes('-h')) {
219
+ printIndexUsage();
220
+ break;
221
+ }
222
+ const { runIndexRefresh } = await import('./index-refresh.js');
223
+ await runIndexRefresh(subcommandArgs);
224
+ break;
225
+ }
216
226
  case 'build':
217
227
  await handleBuild(subcommandArgs);
218
228
  break;
@@ -288,15 +298,16 @@ export async function main(args) {
288
298
  break;
289
299
  default:
290
300
  console.error(`Error: Unknown index subcommand '${subcommand}'`);
291
- console.log('Available: build, query, discover, show, export, sync, migrate-legacy, deps, stats, status, list, neighbors, set, embed, similar, dedup-report, eval-discovery, watch');
301
+ console.log('Available: build, refresh, query, discover, show, export, sync, migrate-legacy, deps, stats, status, list, neighbors, set, embed, similar, dedup-report, eval-discovery, watch');
292
302
  process.exit(1);
293
303
  }
294
304
  }
295
- function printIndexUsage() {
305
+ export function printIndexUsage() {
296
306
  console.log('Usage: aiwg index <subcommand> [options]');
297
307
  console.log('');
298
308
  console.log('Available subcommands:');
299
309
  console.log(' build Build/rebuild the artifact index');
310
+ console.log(' refresh Safely refresh graphs, caches, sessions, runtime, models, and local issues');
300
311
  console.log(' query Search artifacts by keyword, type, phase, tags');
301
312
  console.log(' discover Capability search across operational asset types (#1214)');
302
313
  console.log(' show Print the full text of a specific operational asset');
@@ -315,12 +326,23 @@ function printIndexUsage() {
315
326
  console.log(' watch Start a filesystem watcher for automatic incremental index updates');
316
327
  console.log('');
317
328
  console.log('Options:');
329
+ console.log(' build [--force] [--verbose] [--all] [--scope <dir>] [--graph <name>]');
330
+ console.log(' refresh [--full] [--workspace <path>]... [--root <dir>]...');
331
+ console.log(' [--only graphs|sessions|caches|runtime|models|issues] [--dry-run] [--json] [--lock-wait-ms <n>]');
332
+ console.log(' [--max-record-bytes <n>] [--max-source-bytes <n>]');
333
+ console.log(' Session import defaults: 67108864 bytes per record, 1073741824 bytes (1 GiB) per source; source 0 is unlimited.');
334
+ console.log(' --exclude <id-or-glob> (repeatable): skip session IDs or absolute path globs; adds to sessions.import.exclude and AIWG_SESSIONS_IMPORT_EXCLUDE (comma-separated).');
335
+ console.log(' Import limits: CLI flag > AIWG_SESSIONS_IMPORT_MAX_RECORD_BYTES / AIWG_SESSIONS_IMPORT_MAX_SOURCE_BYTES > workspace config > default.');
336
+ console.log(' build/refresh --index-workers <n>: preparation processes (0 = serial); flag > AIWG_INDEX_WORKERS > automatic CPU/memory limits.');
337
+ console.log(' sync [--backend fortemi-core] [--graph <name> | --all] [--repo <name>]');
338
+ console.log(' [--privacy private|sanitized|public] [--generated-at <iso>] [--json]');
318
339
  console.log(' --graph <name> Target a specific graph (framework, project, codebase, source, user, or user-defined)');
319
340
  console.log(' --all Build all known graphs (including user-defined)');
320
341
  console.log('');
321
342
  console.log('Examples:');
322
343
  console.log(' aiwg index build');
323
344
  console.log(' aiwg index build --all');
345
+ console.log(' aiwg index refresh --root ~/dev --json');
324
346
  console.log(' aiwg index build --graph codebase --force');
325
347
  console.log(' aiwg index discover "create intake"');
326
348
  console.log(' aiwg index discover "deploy production" --limit 5 --json');
@@ -483,10 +505,13 @@ async function handleBuild(args) {
483
505
  console.log('Usage: aiwg index build [options]');
484
506
  console.log('');
485
507
  console.log('Options:');
508
+ console.log(' --index-workers <n> Preparation processes (0 = serial); overrides AIWG_INDEX_WORKERS');
486
509
  console.log(' --force Full rebuild (ignore checksums, re-index everything)');
487
510
  console.log(' --verbose Show detailed progress during indexing');
488
511
  console.log(' --all Build all known graphs (including user-defined)');
489
512
  console.log(' --scope <dir> Limit scan to a specific subdirectory');
513
+ console.log(' --include-ignored Include gitignored sources (runtime directories stay excluded)');
514
+ console.log(' Source discovery skips symlinks and .aiwg/{working,sessions,scratch,tmp,temp,cache,.index,.cache,.storage-cache}/.');
490
515
  console.log(' --graph <name> Build a specific graph only (built-in or user-defined)');
491
516
  console.log('');
492
517
  console.log('Built-in graph names: project, codebase, source, user, framework');
@@ -508,6 +533,15 @@ async function handleBuild(args) {
508
533
  console.log(' aiwg index sync --all');
509
534
  return;
510
535
  }
536
+ let workers;
537
+ try {
538
+ workers = resolveIndexWorkers(parseFlagValue(args, '--index-workers', 'Error: --index-workers requires a value'));
539
+ }
540
+ catch (error) {
541
+ console.error(`Error: ${error instanceof Error ? error.message : String(error)}`);
542
+ process.exitCode = 1;
543
+ return;
544
+ }
511
545
  // Dynamic import to keep the CLI router lightweight
512
546
  const { buildIndex } = await import('./index-builder.js');
513
547
  const cwd = process.cwd();
@@ -516,6 +550,7 @@ async function handleBuild(args) {
516
550
  const force = args.includes('--force');
517
551
  const verbose = args.includes('--verbose');
518
552
  const all = args.includes('--all');
553
+ const includeIgnored = args.includes('--include-ignored');
519
554
  const graph = parseGraphFlag(args);
520
555
  let scope;
521
556
  const scopeIdx = args.indexOf('--scope');
@@ -531,7 +566,7 @@ async function handleBuild(args) {
531
566
  // --graph X: build the JSON graph if X is one; otherwise X may be a
532
567
  // research-corpus markdown view, rendered below.
533
568
  if (graph in GRAPH_CONFIGS) {
534
- await buildIndex(cwd, { force, verbose, scope, graph, explicit: true });
569
+ await buildIndex(cwd, { force, verbose, scope, includeIgnored, workers, graph, explicit: true });
535
570
  jsonBuilt = true;
536
571
  builtGraphs.add(graph);
537
572
  }
@@ -539,7 +574,7 @@ async function handleBuild(args) {
539
574
  else if (all) {
540
575
  // Build all known graphs — user asked for everything, but don't hard-error on missing dirs
541
576
  for (const [name] of orderedGraphEntries(Object.entries(GRAPH_CONFIGS))) {
542
- await buildIndex(cwd, { force, verbose, graph: name, explicit: false });
577
+ await buildIndex(cwd, { force, verbose, includeIgnored, workers, graph: name, explicit: false });
543
578
  builtGraphs.add(name);
544
579
  }
545
580
  jsonBuilt = true;
@@ -548,7 +583,7 @@ async function handleBuild(args) {
548
583
  // Default: build graphs with defaultBuild=true; skip gracefully if their dirs don't exist
549
584
  for (const [name, config] of orderedGraphEntries(Object.entries(GRAPH_CONFIGS))) {
550
585
  if (config.defaultBuild) {
551
- await buildIndex(cwd, { force, verbose, scope: name === Object.keys(GRAPH_CONFIGS)[0] ? scope : undefined, graph: name, explicit: false });
586
+ await buildIndex(cwd, { force, verbose, includeIgnored, workers, scope: name === Object.keys(GRAPH_CONFIGS)[0] ? scope : undefined, graph: name, explicit: false });
552
587
  builtGraphs.add(name);
553
588
  }
554
589
  }
@@ -589,18 +624,18 @@ async function syncFortemiCoreCachesAfterBuild(cwd, graphs, verbose) {
589
624
  const { syncFortemiCoreIndex } = await import('./fortemi-core-sync.js');
590
625
  const { loadGraphIndexFile } = await import('./index-reader.js');
591
626
  for (const graph of graphs) {
592
- // Some default-build graphs skip cleanly when their scan roots are absent.
593
- // Only materialize a cache when this build left an actual source index.
594
- if (!loadGraphIndexFile(cwd, 'metadata.json', graph))
595
- continue;
596
627
  try {
628
+ // Some auto-selected graphs skip cleanly when their scan roots are absent.
629
+ if (!loadGraphIndexFile(cwd, 'metadata.json', graph))
630
+ continue;
597
631
  const manifest = syncFortemiCoreIndex(cwd, { graph });
598
632
  if (verbose) {
599
633
  console.log(`Fortemi Core ${graph} sync: ${manifest.status}, ${manifest.item_count} item(s) → ${manifest.export_path}`);
600
634
  }
601
635
  }
602
636
  catch (err) {
603
- console.error(`Warning: Fortemi Core ${graph} sync failed: ${err instanceof Error ? err.message : String(err)}`);
637
+ process.exitCode = 1;
638
+ console.error(`Error: Fortemi Core ${graph} sync failed: ${err instanceof Error ? err.message : String(err)}`);
604
639
  console.error(`Run \`aiwg index sync --graph ${graph}\` before using Fortemi-backed commands for '${graph}' artifacts.`);
605
640
  }
606
641
  }
@@ -1099,19 +1134,30 @@ async function handleSync(args) {
1099
1134
  if (all) {
1100
1135
  const { loadGraphIndexFile } = await import('./index-reader.js');
1101
1136
  const manifests = [];
1137
+ const failures = [];
1102
1138
  for (const [name] of orderedGraphEntries(Object.entries(GRAPH_CONFIGS))) {
1103
- if (!loadGraphIndexFile(cwd, 'metadata.json', name))
1104
- continue;
1105
- manifests.push(syncFortemiCoreIndex(cwd, { ...options, graph: name }));
1139
+ try {
1140
+ if (!loadGraphIndexFile(cwd, 'metadata.json', name))
1141
+ continue;
1142
+ manifests.push(syncFortemiCoreIndex(cwd, { ...options, graph: name }));
1143
+ }
1144
+ catch (err) {
1145
+ failures.push({ graph: name, status: 'failed', error: err instanceof Error ? err.message : String(err) });
1146
+ }
1106
1147
  }
1148
+ if (failures.length)
1149
+ process.exitCode = 1;
1107
1150
  if (json) {
1108
- console.log(JSON.stringify({ manifests }, null, 2));
1151
+ console.log(JSON.stringify({ manifests, failures }, null, 2));
1109
1152
  return;
1110
1153
  }
1111
- console.log(`Fortemi Core sync: ${manifests.length} graph(s)`);
1154
+ console.log(`Fortemi Core sync: ${manifests.length} graph(s) succeeded, ${failures.length} failed`);
1112
1155
  for (const manifest of manifests) {
1113
1156
  console.log(` ${manifest.graph}: ${manifest.status}, ${manifest.item_count} item(s) → ${manifest.export_path}`);
1114
1157
  }
1158
+ for (const failure of failures) {
1159
+ console.error(` ${failure.graph}: failed — ${failure.error}`);
1160
+ }
1115
1161
  return;
1116
1162
  }
1117
1163
  const manifest = syncFortemiCoreIndex(cwd, {
@@ -1480,7 +1526,7 @@ async function handleDiscover(args) {
1480
1526
  * artifact by type and name.
1481
1527
  *
1482
1528
  * Shape (#1218):
1483
- * aiwg show <type> <name> [--json] [--first] [--graph <name>] [--backend local|fortemi-core]
1529
+ * aiwg show <type> <name> [--json] [--first] [--bound-rule-bodies] [--graph <name>] [--backend local|fortemi-core]
1484
1530
  *
1485
1531
  * Type is positional (not a flag) so the verb reads as
1486
1532
  * "show <kind> <name>". `<type>` is one of the operational show types.
@@ -1504,8 +1550,8 @@ async function handleShow(args) {
1504
1550
  }
1505
1551
  const HELP_TEXT = [
1506
1552
  '',
1507
- 'Usage: aiwg show <type> <name> [--json] [--first] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]',
1508
- ' aiwg show metadata <id-or-name-or-path> [--json] [--first] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]',
1553
+ 'Usage: aiwg show <type> <name> [--json] [--first] [--bound-rule-bodies] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]',
1554
+ ' aiwg show metadata <id-or-name-or-path> [--json] [--first] [--bound-rule-bodies] [--graph <name>] [--backend local|fortemi-core] [--resource-source local|web|auto] [--aiwg-version <version|range|digest|channel>] [--offline]',
1509
1555
  ' aiwg index show <type> <name> ...',
1510
1556
  '',
1511
1557
  `Types: ${OPERATIONAL_SHOW_TYPES.join(' | ')}`,
@@ -1521,6 +1567,10 @@ async function handleShow(args) {
1521
1567
  ' aiwg show command discover',
1522
1568
  ' aiwg show skill intake-wizard --resource-source web --aiwg-version stable',
1523
1569
  '',
1570
+ 'Bound rules: summaries and show pointers are appended for bound skills, agents, and flows.',
1571
+ 'Fetch and follow every required_rule_checks entry before starting governed work; summaries do not replace full rules.',
1572
+ 'Use --bound-rule-bodies to include their full text (JSON: bound_rules[].content).',
1573
+ '',
1524
1574
  'Tip: use `aiwg discover "<phrase>" --json` first to find the stable id.',
1525
1575
  ].join('\n');
1526
1576
  if (positional.length === 0) {
@@ -1584,6 +1634,7 @@ async function handleShow(args) {
1584
1634
  : webReleaseOptionsFromEnvironment();
1585
1635
  const params = {
1586
1636
  name,
1637
+ boundRuleBodies: flags.includes('--bound-rule-bodies'),
1587
1638
  typeFilter: type ? [type] : undefined,
1588
1639
  json,
1589
1640
  first,
@@ -238,7 +238,7 @@ export function computeSnapshot(root, options = {}) {
238
238
  if (r.year)
239
239
  years.push(r.year);
240
240
  inc(sourceTypes, r.sourceType || 'other');
241
- const grade = r.radar?.gradeCurrent?.trim().toUpperCase();
241
+ const grade = r.grade?.trim().toUpperCase();
242
242
  if (!grade)
243
243
  grades.Missing++;
244
244
  else if (CONTROLLED_GRADES.includes(grade))
@@ -60,6 +60,7 @@ const codexAdapter = {
60
60
  // function's own documented default (see extractPages() below).
61
61
  timeout: positiveTimeoutMs(ctx.timeoutSec * 1000, 240_000),
62
62
  encoding: 'utf8',
63
+ killSignal: 'SIGKILL',
63
64
  });
64
65
  if (r.status === 0 && fs.existsSync(tmp)) {
65
66
  const text = fs.readFileSync(tmp, 'utf8');
@@ -174,6 +175,7 @@ function commandAdapter(template) {
174
175
  const r = spawnSync('sh', ['-c', cmd, 'aiwg-vision', imagePath, promptFile, out], {
175
176
  timeout: positiveTimeoutMs(ctx.timeoutSec * 1000, 240_000), encoding: 'utf8',
176
177
  env: { ...process.env, AIWG_VISION_IMAGE: imagePath, AIWG_VISION_PROMPT_FILE: promptFile, AIWG_VISION_OUT: out },
178
+ killSignal: 'SIGKILL',
177
179
  });
178
180
  let text;
179
181
  if (fs.existsSync(out))