@mrciphersmith/keryx 0.2.69 → 0.2.71

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 (183) hide show
  1. package/dist/cli.js +11136 -4863
  2. package/docs/README.md +54 -0
  3. package/docs/requirements/shared-agent-context/README.md +104 -0
  4. package/package.json +3 -2
  5. package/src/gdgraph/build-lang.test.ts +10 -3
  6. package/src/gdgraph/build.ts +54 -9
  7. package/src/gdgraph/import-kind.test.ts +205 -0
  8. package/src/gdgraph/query.ts +6 -1
  9. package/src/gdgraph/types.ts +34 -0
  10. package/src/gdskills/bundled/rules/core/model-selection.mdc +184 -31
  11. package/src/gdskills/bundled/rules/core/skills-storage-workflow.mdc +36 -0
  12. package/src/gdskills/bundled/rules/core/subagent-status-protocol.md +27 -1
  13. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.codex.md +1 -1
  14. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.cursor.md +1 -1
  15. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.md +2 -1
  16. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.opencode.md +1 -1
  17. package/src/gdskills/bundled/skills/orchestration/code-verifier/SKILL.zed.md +1 -1
  18. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.codex.md +1 -1
  19. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.cursor.md +1 -1
  20. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.md +1 -1
  21. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.opencode.md +1 -1
  22. package/src/gdskills/bundled/skills/orchestration/context-collector/SKILL.zed.md +1 -1
  23. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.codex.md +1 -1
  24. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.cursor.md +1 -1
  25. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.md +1 -1
  26. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.opencode.md +1 -1
  27. package/src/gdskills/bundled/skills/orchestration/feature-analyzer/SKILL.zed.md +1 -1
  28. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.codex.md +1 -1
  29. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.cursor.md +1 -1
  30. package/src/gdskills/bundled/skills/orchestration/feature-dev/SKILL.md +1 -1
  31. package/src/gdskills/bundled/skills/orchestration/flow-orchestrator/SKILL.md +159 -20
  32. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.codex.md +1 -1
  33. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.cursor.md +1 -1
  34. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.md +1 -1
  35. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.opencode.md +1 -1
  36. package/src/gdskills/bundled/skills/orchestration/issue-analyzer/SKILL.zed.md +1 -1
  37. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.codex.md +1 -1
  38. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.cursor.md +1 -1
  39. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.md +2 -1
  40. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.opencode.md +1 -1
  41. package/src/gdskills/bundled/skills/orchestration/job-documenter/SKILL.zed.md +1 -1
  42. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.codex.md +28 -3
  43. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.cursor.md +28 -3
  44. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.md +28 -3
  45. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.opencode.md +28 -3
  46. package/src/gdskills/bundled/skills/orchestration/job-orchestrator/SKILL.zed.md +28 -3
  47. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.codex.md +20 -2
  48. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.cursor.md +20 -2
  49. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.md +22 -3
  50. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.opencode.md +20 -2
  51. package/src/gdskills/bundled/skills/orchestration/task-implementer/SKILL.zed.md +20 -2
  52. package/src/gdskills/bundled/skills/planning/autodoc-analyst/SKILL.md +2 -1
  53. package/src/gdskills/bundled/skills/planning/autodoc-architect/SKILL.md +3 -1
  54. package/src/gdskills/bundled/skills/planning/autodoc-assembler/SKILL.md +2 -1
  55. package/src/gdskills/bundled/skills/planning/autodoc-orchestrator/SKILL.md +2 -1
  56. package/src/gdskills/bundled/skills/planning/autodoc-scanner/SKILL.md +2 -1
  57. package/src/gdskills/bundled/skills/planning/autodoc-writer/SKILL.md +2 -1
  58. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.codex.md +1 -1
  59. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.cursor.md +1 -1
  60. package/src/gdskills/bundled/skills/planning/brainstorm/SKILL.md +1 -1
  61. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.codex.md +1 -1
  62. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.cursor.md +1 -1
  63. package/src/gdskills/bundled/skills/planning/consistency-checker/SKILL.md +2 -1
  64. package/src/gdskills/bundled/skills/planning/docpack-orchestrator/SKILL.md +1 -1
  65. package/src/gdskills/bundled/skills/planning/docpack-review/SKILL.md +1 -1
  66. package/src/gdskills/bundled/skills/planning/interview/SKILL.codex.md +1 -1
  67. package/src/gdskills/bundled/skills/planning/interview/SKILL.cursor.md +1 -1
  68. package/src/gdskills/bundled/skills/planning/interview/SKILL.md +1 -1
  69. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.codex.md +1 -1
  70. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.cursor.md +1 -1
  71. package/src/gdskills/bundled/skills/planning/interviewer/SKILL.md +1 -1
  72. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.codex.md +1 -1
  73. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.cursor.md +1 -1
  74. package/src/gdskills/bundled/skills/planning/patterns-researcher/SKILL.md +2 -1
  75. package/src/gdskills/bundled/skills/planning/planner/SKILL.codex.md +1 -1
  76. package/src/gdskills/bundled/skills/planning/planner/SKILL.cursor.md +1 -1
  77. package/src/gdskills/bundled/skills/planning/planner/SKILL.md +2 -1
  78. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.codex.md +1 -1
  79. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.cursor.md +1 -1
  80. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.md +1 -1
  81. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.opencode.md +1 -1
  82. package/src/gdskills/bundled/skills/planning/prd-creator/SKILL.zed.md +1 -1
  83. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.codex.md +1 -1
  84. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.cursor.md +1 -1
  85. package/src/gdskills/bundled/skills/planning/problem-definer/SKILL.md +2 -1
  86. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.codex.md +1 -1
  87. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.cursor.md +1 -1
  88. package/src/gdskills/bundled/skills/planning/project-discovery/SKILL.md +2 -1
  89. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.codex.md +1 -1
  90. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.cursor.md +1 -1
  91. package/src/gdskills/bundled/skills/planning/spec-writer/SKILL.md +2 -1
  92. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.codex.md +1 -1
  93. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.cursor.md +1 -1
  94. package/src/gdskills/bundled/skills/planning/stack-advisor/SKILL.md +2 -1
  95. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.codex.md +1 -1
  96. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.cursor.md +1 -1
  97. package/src/gdskills/bundled/skills/platform/claude-md-management/SKILL.md +1 -1
  98. package/src/gdskills/bundled/skills/platform/hookify/SKILL.codex.md +1 -1
  99. package/src/gdskills/bundled/skills/platform/hookify/SKILL.cursor.md +1 -1
  100. package/src/gdskills/bundled/skills/platform/hookify/SKILL.md +1 -1
  101. package/src/gdskills/bundled/skills/quality/changelog/SKILL.codex.md +1 -1
  102. package/src/gdskills/bundled/skills/quality/changelog/SKILL.cursor.md +1 -1
  103. package/src/gdskills/bundled/skills/quality/changelog/SKILL.md +1 -1
  104. package/src/gdskills/bundled/skills/quality/commit/SKILL.codex.md +1 -1
  105. package/src/gdskills/bundled/skills/quality/commit/SKILL.cursor.md +1 -1
  106. package/src/gdskills/bundled/skills/quality/commit/SKILL.md +1 -1
  107. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.codex.md +1 -1
  108. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.cursor.md +1 -1
  109. package/src/gdskills/bundled/skills/quality/db-migrate/SKILL.md +1 -1
  110. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.codex.md +1 -1
  111. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.cursor.md +1 -1
  112. package/src/gdskills/bundled/skills/quality/dependency-update/SKILL.md +1 -1
  113. package/src/gdskills/bundled/skills/quality/deploy/SKILL.codex.md +1 -1
  114. package/src/gdskills/bundled/skills/quality/deploy/SKILL.cursor.md +1 -1
  115. package/src/gdskills/bundled/skills/quality/deploy/SKILL.md +1 -1
  116. package/src/gdskills/bundled/skills/quality/metaproject-security/SKILL.md +1 -1
  117. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.codex.md +1 -1
  118. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.cursor.md +1 -1
  119. package/src/gdskills/bundled/skills/quality/perf-check/SKILL.md +1 -1
  120. package/src/gdskills/bundled/skills/quality/pr/SKILL.codex.md +1 -1
  121. package/src/gdskills/bundled/skills/quality/pr/SKILL.cursor.md +1 -1
  122. package/src/gdskills/bundled/skills/quality/pr/SKILL.md +1 -1
  123. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.codex.md +1 -1
  124. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.cursor.md +1 -1
  125. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.md +1 -1
  126. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.opencode.md +1 -1
  127. package/src/gdskills/bundled/skills/quality/pr-issue-documenter/SKILL.zed.md +1 -1
  128. package/src/gdskills/bundled/skills/quality/push/SKILL.codex.md +1 -1
  129. package/src/gdskills/bundled/skills/quality/push/SKILL.cursor.md +1 -1
  130. package/src/gdskills/bundled/skills/quality/push/SKILL.md +1 -1
  131. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.codex.md +1 -1
  132. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.cursor.md +1 -1
  133. package/src/gdskills/bundled/skills/quality/security-audit/SKILL.md +1 -1
  134. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.codex.md +1 -1
  135. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.cursor.md +1 -1
  136. package/src/gdskills/bundled/skills/quality/test-gen/SKILL.md +1 -1
  137. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.codex.md +1 -1
  138. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.cursor.md +1 -1
  139. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.md +1 -1
  140. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.opencode.md +1 -1
  141. package/src/gdskills/bundled/skills/quality/tests-creator/SKILL.zed.md +1 -1
  142. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.codex.md +1 -1
  143. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.cursor.md +1 -1
  144. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.md +1 -1
  145. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.opencode.md +1 -1
  146. package/src/gdskills/bundled/skills/review/code-ai-review/SKILL.zed.md +1 -1
  147. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.codex.md +1 -1
  148. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.cursor.md +1 -1
  149. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.md +1 -1
  150. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.opencode.md +1 -1
  151. package/src/gdskills/bundled/skills/review/code-b091-review/SKILL.zed.md +1 -1
  152. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.codex.md +1 -1
  153. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.cursor.md +1 -1
  154. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.md +2 -1
  155. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.opencode.md +1 -1
  156. package/src/gdskills/bundled/skills/review/code-mobx-store-review/SKILL.zed.md +1 -1
  157. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.codex.md +1 -1
  158. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.cursor.md +1 -1
  159. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.md +1 -1
  160. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.opencode.md +1 -1
  161. package/src/gdskills/bundled/skills/review/code-style-review/SKILL.zed.md +1 -1
  162. package/src/gdskills/bundled/skills/review/review-architecture/SKILL.md +37 -10
  163. package/src/gdskills/bundled/skills/review/review-backend/SKILL.md +48 -14
  164. package/src/gdskills/bundled/skills/review/review-clean-code/SKILL.md +49 -12
  165. package/src/gdskills/bundled/skills/review/review-core-boundaries/SKILL.md +34 -2
  166. package/src/gdskills/bundled/skills/review/review-flow-graph/SKILL.md +33 -2
  167. package/src/gdskills/bundled/skills/review/review-frontend/SKILL.md +70 -29
  168. package/src/gdskills/bundled/skills/review/review-frontend-conventions/SKILL.md +34 -3
  169. package/src/gdskills/bundled/skills/review/review-highload/SKILL.md +49 -15
  170. package/src/gdskills/bundled/skills/review/review-logic/SKILL.md +39 -11
  171. package/src/gdskills/bundled/skills/review/review-orchestrator/SKILL.md +659 -64
  172. package/src/gdskills/bundled/skills/review/review-orchestrator/reviewer-finding.schema.json +7 -0
  173. package/src/gdskills/bundled/skills/review/review-orchestrator/verification-claim.schema.json +78 -0
  174. package/src/gdskills/bundled/skills/review/review-performance/SKILL.md +43 -13
  175. package/src/gdskills/bundled/skills/review/review-pr-feedback/SKILL.md +8 -2
  176. package/src/gdskills/bundled/skills/review/review-regression/SKILL.md +185 -0
  177. package/src/gdskills/bundled/skills/review/review-security-code/SKILL.md +44 -13
  178. package/src/gdskills/bundled/skills/review/review-style/SKILL.md +26 -6
  179. package/src/gdskills/bundled/skills/review/review-testing-practices/SKILL.md +35 -3
  180. package/src/gdskills/bundled/skills/review/review-verifier/SKILL.md +276 -0
  181. package/src/gdskills/contracts/review-finding.schema.json +119 -1
  182. package/src/gdskills/contracts/subagent-dispatch.schema.json +59 -3
  183. package/src/gdskills/bundled/skills/review/review-strict/SKILL.md +0 -328
package/docs/README.md ADDED
@@ -0,0 +1,54 @@
1
+ # Documentation
2
+
3
+ This directory separates documentation by purpose so current behavior, product
4
+ intent, implementation plans, and release evidence do not get mixed together.
5
+
6
+ ## Current behavior
7
+
8
+ - [Developer documentation](docs/README.md) — entry point for setup,
9
+ architecture, modules, CLI behavior, and workspace lifecycle.
10
+ - [Complete setup and agent workflows](docs/complete-setup-and-agent-workflows.md)
11
+ — global installation, project configuration, command reference, scripts, and
12
+ copy-ready agent prompts.
13
+ - [Agent installation playbook](docs/agent-installation-playbook.md) — autonomous
14
+ Gherkin scenarios for complete setup, validation, repair, and handoff.
15
+ - [Documentation index](docs/index.md) — compact navigation for the generated
16
+ current-behavior reference.
17
+
18
+ ## Product intent
19
+
20
+ - [Requirements roadmap](requirements/roadmap.md) — requirements packages and
21
+ their verified implementation state.
22
+ - [Managed Review Feedback Loop](requirements/managed-review-feedback-loop/README.md)
23
+ — requirements and contracts for managed review packages.
24
+ - [Keryx Memory Reliability](requirements/keryx-memory-reliability/README.md)
25
+ — corrective requirements and implementation tracking for side-effect-free
26
+ recall, accepted-only agent influence, durable lifecycle, and generated data.
27
+
28
+ ## Plans and reports
29
+
30
+ - [Shared Agent Context Improvements Program](requirements/shared-agent-context-improvements-program/README.md)
31
+ — dependency-ordered implementation waves for twelve SAC improvement
32
+ packages, with copy-ready phase prompts, evidence gates, rollback, and a
33
+ live progress/statistics dashboard.
34
+ - [Keryx Improvements 1 — SAC, memory, and orchestration analysis](analysis/keryx-improvements-1/2026-08-14/report/en/report.md)
35
+ — integrated audit of Shared Agent Context and its Context Operations, Flow,
36
+ Harness/session, MCP, Security, knowledge-owner, worktree, and policy seams;
37
+ includes twelve independently deliverable future requirement packages.
38
+ - [Implementation plans](plans/) — bounded plans that may become cleanup
39
+ candidates after their acceptance criteria are implemented and verified.
40
+ - [Release readiness — 2026-07-10](report/release-readiness-2026-07-10/release-readiness.md)
41
+ — verification results, release blockers, and the prioritized cleanup plan.
42
+ - [Implementation spec](report/release-readiness-2026-07-10/implementation-spec.md)
43
+ — approved scope and acceptance criteria for this documentation pass.
44
+
45
+ ## Documentation policy
46
+
47
+ - Repository documentation is English-only.
48
+ - `docs/docs/` describes current behavior and must be verified against source or
49
+ live CLI help.
50
+ - `docs/requirements/` describes intended behavior and must label implementation
51
+ status explicitly.
52
+ - Generated `.metaproject` artifacts are refreshed through the project CLI; raw
53
+ and reproducible outputs remain ignored according to the managed `.gitignore`
54
+ policy.
@@ -0,0 +1,104 @@
1
+ # Keryx Shared Agent Context
2
+ Version: 1.5.0
3
+
4
+ ## Назначение
5
+
6
+ Этот пакет описывает **текущий** local-first слой совместного контекста Keryx
7
+ (`src/sac/`, CLI `keryx workspace`, MCP `sac.*`, harness `workspace_*`).
8
+ Он даёт человеку и агенту воспроизводимый вход в работу от workspace, компонента
9
+ или flow: небольшой проверяемый обзор, адресное чтение деталей и безопасное
10
+ предложение нового знания по завершении работы.
11
+
12
+ ## Статус
13
+
14
+ `implemented` (механизмы 0–5), плюс фаза 6, разбитая на две части: **6a** —
15
+ runtime-guard opt-in (`resolvePolicySelection`), реализована и проверена
16
+ (AC1–AC6, вся SAC-сюита 88/88 зелёная); **6b** — операторский процесс готовности
17
+ для реальных данных, частично (read-only `keryx workspace policy-readiness` и
18
+ playbook; runtime re-ingestion сырых receipts/outcomes остаётся). Фазы 0–5 и 6a
19
+ влиты в `main` и выпущены в `v0.2.32`; код lifecycle/CLI/MCP/harness с тех пор
20
+ расширен на `main` (в т.ч. `v0.2.35`). Точный статус и evidence приведены в
21
+ [Implementation plan](implementation-plan.md).
22
+
23
+ **Документационная правда (1.5.0):** более ранние versioned-заголовки этого
24
+ пакета помечали CLI/MCP/schema-enforcement как `future/planned` и прямо писали,
25
+ что runtime «не реализует SAC contracts». Это устарело относительно `src/sac/`
26
+ (~4.3k строк production + тесты: propose/review/accept, guarded owner-writers
27
+ wiki/memory/skill, receipt-integrity, access-receipt ledger; плюс
28
+ `src/commands/workspace.ts`, MCP `sac.*`, harness `workspace_*`). Норматив
29
+ этого пакета теперь —
30
+ как устроено **сейчас**. Спутниковые пакеты RP-01…RP-12 остаются future /
31
+ spec-ready и **не** отменяют этот runtime. Валидатор, который сверяется с
32
+ заголовками `future` в старых ревизиях или в RP-пакетах, получит ложный drift.
33
+
34
+ Важно: Phase 5 (policy experiment) сейчас подтверждает корректность механизма на
35
+ synthetic offline evidence и по умолчанию не включает production эффектов.
36
+
37
+ ## Модель FWK
38
+
39
+ - **Facts** — evidence-linked, task-local и freshness-bound утверждения о
40
+ текущей работе. Fact не становится источником долгосрочного знания.
41
+ - **Work** — read-only проекция существующего Flow: выполненное, следующее,
42
+ блокировки и verification evidence. SAC никогда не создаёт второй tracker.
43
+ - **Know-how** — reviewed и reusable knowledge из memory, wiki и skills.
44
+ Необработанные транскрипты и скрытые рассуждения не являются Know-how.
45
+
46
+ ## Документы
47
+
48
+ - [PRD](prd.md) — проблема, пользователи, требования, риски и результаты.
49
+ - [Specification](specification.md) — границы, функциональная surface,
50
+ интеграции и acceptance criteria.
51
+ - [Agent protocol](agent-protocol.md) — обязательное поведение агента при
52
+ read, wrap-up и proposal lifecycle.
53
+ - [Artifact lifecycle](artifact-lifecycle.md) — источники истины, freshness,
54
+ retention, supersession и deletion policy.
55
+ - [Metrics and validation](metrics-and-validation.md) — baseline, evals,
56
+ rollout/rollback и измеримые gates.
57
+ - [Implementation plan](implementation-plan.md) — delivery status фаз 0–6b
58
+ и исторические exit criteria.
59
+ - [Phase execution prompts](phase-execution-prompts.md) — утверждённые промты
60
+ для запуска и delivery-protocol каждой implementation phase.
61
+ - [Phase 4 usability report](phase-4-usability-report.md) — contract-only
62
+ walkthrough evidence.
63
+ - [Phase 5 policy experiment report](phase-5-policy-experiment-report.md) —
64
+ synthetic offline experiment evidence.
65
+ - [Phase 6 readiness](phase-6-real-opt-in-readiness.md) — 6a/6b split and
66
+ remaining real-data work.
67
+ - [Phase 6b operator playbook](phase-6b-operator-playbook.md) — operator
68
+ readiness process.
69
+ - [Design rationale](design-rationale.md) — решения и ограничения модели FWK.
70
+ - [Schemas](schemas/README.md) — JSON Schema, semantic-validation boundary и
71
+ полный positive/negative/replay fixture corpus.
72
+
73
+ ## Scope
74
+
75
+ - Локальный workspace registry со ссылками на компоненты, repositories, flows,
76
+ evidence и approved knowledge; исходные артефакты не копируются.
77
+ - Bounded FWK overview и progressive retrieval через **существующие** CLI
78
+ (`keryx workspace overview|read`), MCP (`sac.overview`/`sac.read`, только
79
+ local stdio) и harness-tools (`workspace_overview`/`workspace_read`);
80
+ hash-chained access-receipt ledger.
81
+ - Evidence-linked session wrap-up, proposal queue, human review и guarded
82
+ promotion в существующие wiki/memory paths.
83
+ - Freshness, least disclosure, trusted ActorContext, local roles, redaction и
84
+ audit trail; proposal может быть принят только после проверяемого,
85
+ append-only review transition.
86
+
87
+ ## Non-goals
88
+
89
+ - Новый task manager, дубликат Flow или новый primary store для wiki/memory.
90
+ - Хранение raw transcripts, secrets, PII, hidden reasoning или unrestricted
91
+ environment snapshots как knowledge.
92
+ - Обязательная облачная база, multi-tenant service, SSO или внешний catalog.
93
+ - UI/IDE/terminal shell как prerequisite первой поставки.
94
+ - Обучаемая или self-modifying access policy до воспроизводимых offline evals.
95
+
96
+ ## Связанные модули
97
+
98
+ - [Keryx Context Operations](../keryx-context-operations/2026-07-12/README.md)
99
+ — владелец context assembly, retrieval trace и feedback lifecycle.
100
+ - [Keryx Project Agent Harness](../keryx-project-agent-harness/README.md) —
101
+ владелец сессий, approvals, worktrees и execution runtime.
102
+ - `src/flow`, `src/memory`, `src/wiki`, `src/gdgraph`, `src/mcp`,
103
+ `src/security`, `src/ctx`, `src/harness` — существующие интеграционные
104
+ границы; изменения в них требуют отдельных implementation flows.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mrciphersmith/keryx",
3
- "version": "0.2.69",
3
+ "version": "0.2.71",
4
4
  "description": "Version-controlled project context for AI coding agents: code graph, architecture wiki, project memory, relevant tests, quality signals, and task flows.",
5
5
  "private": false,
6
6
  "publishConfig": {
@@ -41,6 +41,7 @@
41
41
  "test": "bun test",
42
42
  "check": "tsc --noEmit && bun test",
43
43
  "check:doc-links": "bun scripts/check-doc-links.ts",
44
+ "baseline:review-precision": "bun scripts/review-precision-baseline.ts",
44
45
  "test:guards": "bun test src/lib/config-dir.ast.test.ts src/lib/config-dir.readers.test.ts src/lib/production-graph.test.ts src/harness/policy/profiles.test.ts src/lib/serve-server.test.ts"
45
46
  },
46
47
  "files": [
@@ -72,4 +73,4 @@
72
73
  "protobufjs",
73
74
  "sharp"
74
75
  ]
75
- }
76
+ }
@@ -188,6 +188,13 @@ test("buildGraph resolves Python absolute, relative, and __init__.py imports", a
188
188
  // !!! NEVER regenerate these goldens to make a diff disappear. If this test
189
189
  // !!! fails, the TS/JS code path changed and AC4 is violated — fix the code,
190
190
  // !!! not the golden.
191
+ //
192
+ // EXCEPTION, recorded rather than silently applied: flow 140 (P1 — dynamic
193
+ // imports counted as load-order cycles) intentionally adds an `importKind`
194
+ // field to every edge record, TS/JS included — the whole point of that flow
195
+ // is that the field was missing. That is a deliberate schema change, not a
196
+ // TS/JS behavior drift from Java/Python work, so the AC4 invariant above
197
+ // still holds; only the pinned literal needed the new field appended.
191
198
  // ---------------------------------------------------------------------------
192
199
 
193
200
  const GOLDEN_NODES_JSONL =
@@ -197,9 +204,9 @@ const GOLDEN_NODES_JSONL =
197
204
  `{"id":"src/feature/value.ts","kind":"file","path":"src/feature/value.ts","language":"typescript"}\n`;
198
205
 
199
206
  const GOLDEN_EDGES_JSONL =
200
- `{"id":"edge:1","from":"src/feature/helper.js","to":"src/feature/value.ts","kind":"imports","specifier":"./value"}\n` +
201
- `{"id":"edge:2","from":"src/feature/index.ts","to":"src/feature/style.css","kind":"asset","specifier":"./style.css"}\n` +
202
- `{"id":"edge:3","from":"src/feature/index.ts","to":"src/feature/value.ts","kind":"imports","specifier":"./value"}\n`;
207
+ `{"id":"edge:1","from":"src/feature/helper.js","to":"src/feature/value.ts","kind":"imports","specifier":"./value","importKind":"import-statement"}\n` +
208
+ `{"id":"edge:2","from":"src/feature/index.ts","to":"src/feature/style.css","kind":"asset","specifier":"./style.css","importKind":"import-statement"}\n` +
209
+ `{"id":"edge:3","from":"src/feature/index.ts","to":"src/feature/value.ts","kind":"imports","specifier":"./value","importKind":"import-statement"}\n`;
203
210
 
204
211
  test("buildGraph output is byte-identical for a TS/JS-only project (AC4 guard)", async () => {
205
212
  const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-lang-regression");
@@ -1,7 +1,8 @@
1
1
  import { mkdir, readdir, readFile, writeFile } from "node:fs/promises";
2
2
  import { existsSync } from "node:fs";
3
3
  import path from "node:path";
4
- import type { GraphData, GraphEdge, GraphNode } from "./types";
4
+ import type { GraphData, GraphEdge, GraphNode, ImportKind, TranspilerImportKind } from "./types";
5
+ import { UNKNOWN_IMPORT_KIND } from "./types";
5
6
 
6
7
  const SOURCE_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".java", ".py"];
7
8
  const SOURCE_RESOLUTION_EXTENSIONS = [".ts", ".tsx", ".js", ".jsx", ".d.ts", ".java", ".py"];
@@ -111,9 +112,9 @@ export async function buildGraph(projectRoot: string): Promise<BuildResult> {
111
112
  // dropped), so the resolution metric is honest. TS/JS keeps the exact
112
113
  // original guard (relative + tsconfig alias only) ⇒ byte-identical output.
113
114
  const isLanguageAware = language === "java" || language === "python";
114
- const specifiers = extractImportSpecifiers(content, language);
115
+ const records = extractImportRecords(content, language);
115
116
 
116
- for (const specifier of specifiers) {
117
+ for (const { specifier, kind: importKind } of records) {
117
118
  const resolved = resolveImport(projectRoot, file, specifier, fileSet, resolver);
118
119
  const asset = resolved ? null : resolveAssetImport(projectRoot, file, specifier, resolver);
119
120
  const shouldTrackUnresolved =
@@ -136,6 +137,7 @@ export async function buildGraph(projectRoot: string): Promise<BuildResult> {
136
137
  to: resolved ?? asset ?? specifier,
137
138
  kind: resolved ? "imports" : asset ? "asset" : "unresolved",
138
139
  specifier,
140
+ importKind,
139
141
  });
140
142
  }
141
143
  }
@@ -200,14 +202,27 @@ async function collectSourceFiles(projectRoot: string): Promise<SourceCollection
200
202
  return { files: result.sort(), skippedDirectories: skippedDirectories.sort() };
201
203
  }
202
204
 
203
- function extractImportSpecifiers(content: string, language: string): string[] {
205
+ type ImportRecord = { specifier: string; kind: ImportKind };
206
+
207
+ // P1 remediation (flow 140): union the transpiler and fallback extractors as
208
+ // before, but keep the transpiler's per-specifier kind alive instead of
209
+ // collapsing everything to a bare specifier string. `build()`'s edge-writing
210
+ // loop reads `record.kind` straight onto the edge, so cycle detection can
211
+ // later tell a load-order `import-statement` from a call-time `dynamic-import`
212
+ // instead of the previous single "imports" bucket.
213
+ function extractImportRecords(content: string, language: string): ImportRecord[] {
204
214
  // Java/Python are not TS/JS syntax — the tsx transpiler cannot scan them
205
215
  // (it throws today, which is why they already reach the fallback). Route them
206
216
  // explicitly to the regex fallback that carries the java/python patterns,
207
217
  // rather than depending on the transpiler always throwing. TS/JS keep the
208
218
  // exact original transpiler-then-fallback path ⇒ byte-identical output.
219
+ // The fallback is a plain regex with no notion of import kind, so every
220
+ // Java/Python edge is marked UNKNOWN_IMPORT_KIND — never guessed (AC4).
209
221
  if (language === "java" || language === "python") {
210
- return extractImportSpecifiersFallback(content);
222
+ return extractImportSpecifiersFallback(content).map((specifier) => ({
223
+ specifier,
224
+ kind: UNKNOWN_IMPORT_KIND,
225
+ }));
211
226
  }
212
227
  // `Bun.Transpiler#scanImports` ERASES type-only imports: `import type {X} from
213
228
  // "./m"` and `export type {X} from "./m"` are compiled away, so the transpiler
@@ -219,16 +234,46 @@ function extractImportSpecifiers(content: string, language: string): string[] {
219
234
  // odd formatting), the fallback contributes the type-only ones.
220
235
  const scanned = scanImportsOrEmpty(content);
221
236
  const fallback = extractImportSpecifiersFallback(content);
222
- return [...new Set([...scanned, ...fallback])].sort();
237
+
238
+ const kindBySpecifier = new Map<string, ImportKind>();
239
+ for (const { specifier, kind } of scanned) {
240
+ const existing = kindBySpecifier.get(specifier);
241
+ // The same specifier can appear more than once in a file (a static import
242
+ // plus a separate `await import()` of the same path). A real static edge
243
+ // makes it a load-order dependency regardless of what else also imports
244
+ // it dynamically elsewhere in the file, so a non-dynamic kind always wins
245
+ // over a dynamic one already recorded for the same specifier.
246
+ if (!existing || (existing === "dynamic-import" && kind !== "dynamic-import")) {
247
+ kindBySpecifier.set(specifier, kind);
248
+ }
249
+ }
250
+ // Fallback-only specifiers (not seen by the transpiler at all — e.g.
251
+ // type-only imports) get the explicit unknown/static marker, never a
252
+ // guessed kind (AC4). A specifier the transpiler DID see keeps its real
253
+ // kind; the fallback never overrides it.
254
+ for (const specifier of fallback) {
255
+ if (!kindBySpecifier.has(specifier)) {
256
+ kindBySpecifier.set(specifier, UNKNOWN_IMPORT_KIND);
257
+ }
258
+ }
259
+
260
+ return [...kindBySpecifier.entries()]
261
+ .map(([specifier, kind]) => ({ specifier, kind }))
262
+ .sort((a, b) => a.specifier.localeCompare(b.specifier));
223
263
  }
224
264
 
225
- function scanImportsOrEmpty(content: string): string[] {
265
+ type ScannedImport = { specifier: string; kind: TranspilerImportKind };
266
+
267
+ function scanImportsOrEmpty(content: string): ScannedImport[] {
226
268
  try {
227
269
  const scanner = new Bun.Transpiler({ loader: "tsx" });
228
270
  return scanner
229
271
  .scanImports(content)
230
- .map((entry) => entry.path)
231
- .filter((specifier): specifier is string => typeof specifier === "string" && specifier.length > 0);
272
+ .filter(
273
+ (entry): entry is { path: string; kind: TranspilerImportKind } =>
274
+ typeof entry.path === "string" && entry.path.length > 0,
275
+ )
276
+ .map((entry) => ({ specifier: entry.path, kind: entry.kind }));
232
277
  } catch {
233
278
  // Unparseable source ⇒ the regex fallback alone still yields the imports.
234
279
  return [];
@@ -0,0 +1,205 @@
1
+ // P1 remediation (flow 140) — `keryx gdgraph query cycles` was folding
2
+ // `await import()` edges into the load-order cycle count. `Bun.Transpiler
3
+ // #scanImports` already reports whether a specifier is a static
4
+ // import-statement, a dynamic-import, a require-call, etc.; `build.ts` threw
5
+ // that classification away one line after receiving it. These tests are
6
+ // written FIRST (TDD RED) against the frozen acceptance criteria in
7
+ // `.metaproject/flows/140-2026-08-07-gdgraph-dynamic-import-edges/acceptance-criteria.md`.
8
+ //
9
+ // Conventions mirror build.test.ts / build-lang.test.ts: uniqueTestRoot(),
10
+ // reset(root), buildGraph(root), loadGraph(root).
11
+
12
+ import { mkdir, rm, writeFile } from "node:fs/promises";
13
+ import { tmpdir } from "node:os";
14
+ import path from "node:path";
15
+ import { expect, test } from "bun:test";
16
+ import { buildGraph } from "./build";
17
+ import { getCycles, loadGraph } from "./query";
18
+ import { uniqueTestRoot } from "../lib/test-tmp";
19
+
20
+ async function reset(root: string): Promise<void> {
21
+ await rm(root, { recursive: true, force: true });
22
+ await mkdir(root, { recursive: true });
23
+ }
24
+
25
+ // ---------------------------------------------------------------------------
26
+ // AC1 — every edge the transpiler produced carries the kind scanImports
27
+ // actually returned, and it survives to the written edge record.
28
+ // ---------------------------------------------------------------------------
29
+
30
+ test("AC1 — the transpiler's import kind survives onto the written edge record", async () => {
31
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-import-kind-ac1");
32
+ await reset(root);
33
+ await mkdir(path.join(root, "src"), { recursive: true });
34
+ await writeFile(
35
+ path.join(root, "src", "index.ts"),
36
+ [
37
+ "import { staticValue } from './static';",
38
+ "const loadDynamic = async () => { const { dynamicValue } = await import('./dynamic'); return dynamicValue; };",
39
+ "const required = require('./required');",
40
+ "export const result = { staticValue, loadDynamic, required };",
41
+ "",
42
+ ].join("\n"),
43
+ );
44
+ await writeFile(path.join(root, "src", "static.ts"), "export const staticValue = 1;\n");
45
+ await writeFile(path.join(root, "src", "dynamic.ts"), "export const dynamicValue = 2;\n");
46
+ await writeFile(path.join(root, "src", "required.ts"), "export const requiredValue = 3;\n");
47
+
48
+ await buildGraph(root);
49
+ const graph = await loadGraph(root);
50
+ const edgesFromIndex = graph.edges.filter(
51
+ (edge) => edge.from === "src/index.ts" && edge.kind === "imports",
52
+ );
53
+ const importKindByTarget = new Map(edgesFromIndex.map((edge) => [edge.to, edge.importKind]));
54
+
55
+ expect(importKindByTarget.get("src/static.ts")).toBe("import-statement");
56
+ expect(importKindByTarget.get("src/dynamic.ts")).toBe("dynamic-import");
57
+ expect(importKindByTarget.get("src/required.ts")).toBe("require-call");
58
+ });
59
+
60
+ test("a specifier imported both statically and dynamically in the same file keeps the static classification", async () => {
61
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-import-kind-mixed-specifier");
62
+ await reset(root);
63
+ await mkdir(path.join(root, "src"), { recursive: true });
64
+ await writeFile(
65
+ path.join(root, "src", "index.ts"),
66
+ [
67
+ "import './shared';",
68
+ "const loadAgain = async () => { await import('./shared'); };",
69
+ "export const result = loadAgain;",
70
+ "",
71
+ ].join("\n"),
72
+ );
73
+ await writeFile(path.join(root, "src", "shared.ts"), "export const shared = true;\n");
74
+
75
+ await buildGraph(root);
76
+ const graph = await loadGraph(root);
77
+ const edge = graph.edges.find((item) => item.from === "src/index.ts" && item.to === "src/shared.ts");
78
+
79
+ expect(edge?.importKind).toBe("import-statement");
80
+ });
81
+
82
+ // ---------------------------------------------------------------------------
83
+ // AC2 — a fixture reproducing the target's shape: one static edge, one
84
+ // dynamic-import edge back, no longer reported as a load-order cycle.
85
+ // ---------------------------------------------------------------------------
86
+
87
+ test("AC2 — a cycle closed only through a dynamic-import edge is no longer reported as load-order", async () => {
88
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-cycle-mixed");
89
+ await reset(root);
90
+ await mkdir(path.join(root, "bot", "commands"), { recursive: true });
91
+ // Mirrors the target's shape: commands/menu.ts statically imports
92
+ // callbacks.ts; callbacks.ts reaches back into menu.ts only via `await
93
+ // import()`.
94
+ await writeFile(
95
+ path.join(root, "bot", "commands", "menu.ts"),
96
+ "import { registerCallback } from '../callbacks';\nexport const menu = () => registerCallback();\n",
97
+ );
98
+ await writeFile(
99
+ path.join(root, "bot", "callbacks.ts"),
100
+ [
101
+ "export const registerCallback = () => 1;",
102
+ "export const handleMenuCallback = async () => {",
103
+ " const { menu } = await import('./commands/menu');",
104
+ " return menu();",
105
+ "};",
106
+ "",
107
+ ].join("\n"),
108
+ );
109
+
110
+ await buildGraph(root);
111
+ const graph = await loadGraph(root);
112
+ const cycles = getCycles(graph);
113
+
114
+ expect(
115
+ cycles.some((cycle) => cycle.includes("bot/callbacks.ts") && cycle.includes("bot/commands/menu.ts")),
116
+ ).toBe(false);
117
+ });
118
+
119
+ // ---------------------------------------------------------------------------
120
+ // AC3 — classification both ways, same two-file cycle shape.
121
+ // ---------------------------------------------------------------------------
122
+
123
+ test("AC3 — a two-file cycle formed by static imports IS reported", async () => {
124
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-cycle-static");
125
+ await reset(root);
126
+ await mkdir(path.join(root, "src"), { recursive: true });
127
+ await writeFile(path.join(root, "src", "a.ts"), "import { b } from './b';\nexport const a = () => b();\n");
128
+ await writeFile(path.join(root, "src", "b.ts"), "import { a } from './a';\nexport const b = () => a();\n");
129
+
130
+ await buildGraph(root);
131
+ const graph = await loadGraph(root);
132
+ const cycles = getCycles(graph);
133
+
134
+ expect(cycles.some((cycle) => cycle.includes("src/a.ts") && cycle.includes("src/b.ts"))).toBe(true);
135
+ });
136
+
137
+ test("AC3 — the same two-file cycle formed by await import() is NOT reported as load-order", async () => {
138
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-cycle-dynamic");
139
+ await reset(root);
140
+ await mkdir(path.join(root, "src"), { recursive: true });
141
+ await writeFile(
142
+ path.join(root, "src", "a.ts"),
143
+ "export const a = async () => { const { b } = await import('./b'); return b(); };\n",
144
+ );
145
+ await writeFile(
146
+ path.join(root, "src", "b.ts"),
147
+ "export const b = async () => { const { a } = await import('./a'); return a(); };\n",
148
+ );
149
+
150
+ await buildGraph(root);
151
+ const graph = await loadGraph(root);
152
+ const cycles = getCycles(graph);
153
+
154
+ expect(cycles.some((cycle) => cycle.includes("src/a.ts") && cycle.includes("src/b.ts"))).toBe(false);
155
+ });
156
+
157
+ // ---------------------------------------------------------------------------
158
+ // AC4 — an edge found only by the regex fallback (never seen by scanImports)
159
+ // is marked with an explicit unknown/static marker, never inferred dynamic.
160
+ // A type-only import is the real-world case: `Bun.Transpiler#scanImports`
161
+ // erases `import type {...}` entirely, so it reaches the graph only through
162
+ // `extractImportSpecifiersFallback`.
163
+ // ---------------------------------------------------------------------------
164
+
165
+ test("AC4 — a fallback-only edge (type-only import) is marked unknown-static, never inferred dynamic", async () => {
166
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-import-kind-ac4");
167
+ await reset(root);
168
+ await mkdir(path.join(root, "src"), { recursive: true });
169
+ await writeFile(
170
+ path.join(root, "src", "consumer.ts"),
171
+ "import type { Shape } from './types';\nexport const consumer: Shape = { ok: true } as Shape;\n",
172
+ );
173
+ await writeFile(path.join(root, "src", "types.ts"), "export interface Shape { ok: boolean }\n");
174
+
175
+ await buildGraph(root);
176
+ const graph = await loadGraph(root);
177
+ const edge = graph.edges.find((item) => item.from === "src/consumer.ts" && item.to === "src/types.ts");
178
+
179
+ expect(edge).toBeDefined();
180
+ expect(edge?.importKind).toBe("unknown-static");
181
+ expect(edge?.importKind).not.toBe("dynamic-import");
182
+ });
183
+
184
+ test("AC4 — Java imports (fallback-only language) are marked unknown-static, never dynamic", async () => {
185
+ const root = uniqueTestRoot(tmpdir(), "keryx-gdgraph-import-kind-java");
186
+ await reset(root);
187
+ const javaRoot = path.join(root, "src", "main", "java", "com", "example");
188
+ await mkdir(javaRoot, { recursive: true });
189
+ await writeFile(
190
+ path.join(javaRoot, "Consumer.java"),
191
+ "package com.example;\nimport com.example.Model;\npublic class Consumer {}\n",
192
+ );
193
+ await writeFile(path.join(javaRoot, "Model.java"), "package com.example;\npublic class Model {}\n");
194
+
195
+ await buildGraph(root);
196
+ const graph = await loadGraph(root);
197
+ const edge = graph.edges.find(
198
+ (item) =>
199
+ item.from === "src/main/java/com/example/Consumer.java" &&
200
+ item.to === "src/main/java/com/example/Model.java",
201
+ );
202
+
203
+ expect(edge).toBeDefined();
204
+ expect(edge?.importKind).toBe("unknown-static");
205
+ });
@@ -61,7 +61,12 @@ export function getCycles(graph: GraphData): string[][] {
61
61
  adjacency.set(node.path, []);
62
62
  }
63
63
  for (const edge of graph.edges) {
64
- if (edge.kind !== "imports") {
64
+ // A `dynamic-import` (`await import()`) resolves at call time, not
65
+ // module-load time, so a cycle closed only through one is not the
66
+ // load-order cycle this query answers (P1, flow 140). Excluding it here
67
+ // — rather than reclassifying `edge.kind` — leaves orphans/affected
68
+ // untouched (AC5): both still treat the edge as a normal import.
69
+ if (edge.kind !== "imports" || edge.importKind === "dynamic-import") {
65
70
  continue;
66
71
  }
67
72
  adjacency.get(edge.from)?.push(edge.to);
@@ -5,12 +5,46 @@ export type GraphNode = {
5
5
  language: "typescript" | "javascript" | "java" | "python" | "asset";
6
6
  };
7
7
 
8
+ // Import classification straight from `Bun.Transpiler#scanImports` (P1
9
+ // remediation, flow 140). A static `import`/`export ... from` statement is a
10
+ // load-order dependency; `dynamic-import` (`await import()`) resolves at call
11
+ // time and is not. The full union mirrors every literal `scanImports` can
12
+ // return (bun-types `ImportKind`), so a transpiler-found edge always carries
13
+ // the value the transpiler actually reported — never a guess.
14
+ export type TranspilerImportKind =
15
+ | "import-statement"
16
+ | "require-call"
17
+ | "require-resolve"
18
+ | "dynamic-import"
19
+ | "import-rule"
20
+ | "url-token"
21
+ | "internal"
22
+ | "entry-point-run"
23
+ | "entry-point-build";
24
+
25
+ // A specifier found ONLY by the regex fallback (`extractImportSpecifiersFallback`
26
+ // in build.ts) carries no real kind from the transpiler — the fallback is a
27
+ // plain regex with no notion of "static" vs "dynamic". Never infer one;
28
+ // `UNKNOWN_IMPORT_KIND` marks it explicitly and cycle detection treats it as
29
+ // load-order (the pre-fix behavior), so fallback-only edges are never
30
+ // silently excluded from a real cycle.
31
+ export const UNKNOWN_IMPORT_KIND = "unknown-static" as const;
32
+
33
+ export type ImportKind = TranspilerImportKind | typeof UNKNOWN_IMPORT_KIND;
34
+
8
35
  export type GraphEdge = {
9
36
  id: string;
10
37
  from: string;
11
38
  to: string;
12
39
  kind: "imports" | "asset" | "unresolved";
13
40
  specifier: string;
41
+ // Provenance/kind of the specifier that produced this edge (P1, flow 140).
42
+ // `buildGraph()` always sets this. Optional (not required) so edge literals
43
+ // constructed before this field existed — test fixtures elsewhere in the
44
+ // repo, or graphs persisted by an older `keryx gdgraph build` — stay valid;
45
+ // `getCycles` treats a missing value as load-order, matching pre-fix
46
+ // behavior rather than crashing or silently mis-classifying.
47
+ importKind?: ImportKind;
14
48
  };
15
49
 
16
50
  export type GraphData = {