doubled-graph 2.0.0__tar.gz → 2.0.1__tar.gz

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 (102) hide show
  1. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/PKG-INFO +2 -2
  2. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/README.md +1 -1
  3. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/CHANGELOG.md +61 -0
  4. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/TOOLS.md +3 -3
  5. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/upgrade-methodology/00-entry-prompt.md +2 -0
  6. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/upgrade-methodology/01-content-gaps.md +71 -1
  7. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/pyproject.toml +1 -1
  8. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/declared.py +67 -8
  9. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/migrations/grace3_to_grace4.py +358 -46
  10. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_migration_grace3_to_grace4.py +285 -0
  11. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/.gitignore +0 -0
  12. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/ARTICLE.md +0 -0
  13. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/INDEX.md +0 -0
  14. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/LICENSE +0 -0
  15. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/HOOKS.md +0 -0
  16. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/SPEC.md +0 -0
  17. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-existing-project/README.md +0 -0
  18. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-maintenance/README.md +0 -0
  19. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-new-project/README.md +0 -0
  20. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/FAQ.md +0 -0
  21. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/deep-dive.md +0 -0
  22. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/getting-started.md +0 -0
  23. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/README.md +0 -0
  24. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/approval-checkpoints.md +0 -0
  25. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/artifacts.md +0 -0
  26. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/auto-scaling.md +0 -0
  27. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/drift-and-priority.md +0 -0
  28. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/_template.md +0 -0
  29. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/csharp.md +0 -0
  30. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/go.md +0 -0
  31. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/java.md +0 -0
  32. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/python.md +0 -0
  33. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/rust.md +0 -0
  34. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/swift.md +0 -0
  35. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/typescript.md +0 -0
  36. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/principles.md +0 -0
  37. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/roles.md +0 -0
  38. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/_template.md +0 -0
  39. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/claude-code.md +0 -0
  40. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/codex.md +0 -0
  41. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/continue.md +0 -0
  42. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/cursor.md +0 -0
  43. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/local-llm.md +0 -0
  44. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/windsurf.md +0 -0
  45. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/tools.md +0 -0
  46. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/workflow.md +0 -0
  47. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-after-edit.md +0 -0
  48. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-before-edit.md +0 -0
  49. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-drift-detected.md +0 -0
  50. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-user-manual-edit.md +0 -0
  51. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/00-entry-prompt.md +0 -0
  52. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/01-discover.md +0 -0
  53. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/02-reconstruct-intent.md +0 -0
  54. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/03-draft-plan.md +0 -0
  55. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/04-markup-codebase.md +0 -0
  56. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/05-verification-and-logs.md +0 -0
  57. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/06-validation-gates.md +0 -0
  58. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/00-entry-prompt.md +0 -0
  59. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/01-intent-interview.md +0 -0
  60. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/02-requirements.md +0 -0
  61. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/03-technology.md +0 -0
  62. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/04-development-plan.md +0 -0
  63. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/05-verification-plan.md +0 -0
  64. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/06-generate.md +0 -0
  65. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/07-post-init-sync.md +0 -0
  66. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/__init__.py +0 -0
  67. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/__main__.py +0 -0
  68. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/cli.py +0 -0
  69. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/config.py +0 -0
  70. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-after-edit/SKILL.md +0 -0
  71. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-before-edit/SKILL.md +0 -0
  72. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-drift/SKILL.md +0 -0
  73. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-guide/SKILL.md +0 -0
  74. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-manual-edit/SKILL.md +0 -0
  75. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-refactor/SKILL.md +0 -0
  76. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-verify/SKILL.md +0 -0
  77. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/__init__.py +0 -0
  78. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/crossref.py +0 -0
  79. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/grace4.py +0 -0
  80. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/hooks_installer.py +0 -0
  81. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/__init__.py +0 -0
  82. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/cgc.py +0 -0
  83. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/grace_cli.py +0 -0
  84. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/migrations/__init__.py +0 -0
  85. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/policy/__init__.py +0 -0
  86. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/policy/phase.py +0 -0
  87. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/server.py +0 -0
  88. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/setup.py +0 -0
  89. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/storage/__init__.py +0 -0
  90. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/storage/paths.py +0 -0
  91. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/__init__.py +0 -0
  92. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/_meta.py +0 -0
  93. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/analyze.py +0 -0
  94. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/context.py +0 -0
  95. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/detect_changes.py +0 -0
  96. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/impact.py +0 -0
  97. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/__init__.py +0 -0
  98. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_cgc_contract.py +0 -0
  99. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_grace4_artifacts.py +0 -0
  100. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_grace_cli_engine.py +0 -0
  101. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_setup_artifacts.py +0 -0
  102. {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_smoke.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: doubled-graph
3
- Version: 2.0.0
3
+ Version: 2.0.1
4
4
  Summary: MCP facade over CodeGraphContext + GRACE 4 (.grace artifacts): impact, context, drift-detect, migration for LLM-driven codebases.
5
5
  Project-URL: Homepage, https://github.com/rour/doubled-graph-mcp
6
6
  Project-URL: Repository, https://github.com/rour/doubled-graph-mcp
@@ -154,7 +154,7 @@ head -1 "$(which doubled-graph)" # вывести shebang — это и ест
154
154
  ### 4. Проверить, что всё встало
155
155
 
156
156
  ```bash
157
- doubled-graph --version # 2.0.0
157
+ doubled-graph --version # 2.0.1
158
158
  doubled-graph status --repo . # artifacts.project_kind = grace4, grace_engine.upgrade_required = false
159
159
  doubled-graph lint --repo . # summary.errors = 0
160
160
  doubled-graph migrate check --repo . # map_pending / placeholders — что осталось заполнить
@@ -119,7 +119,7 @@ head -1 "$(which doubled-graph)" # вывести shebang — это и ест
119
119
  ### 4. Проверить, что всё встало
120
120
 
121
121
  ```bash
122
- doubled-graph --version # 2.0.0
122
+ doubled-graph --version # 2.0.1
123
123
  doubled-graph status --repo . # artifacts.project_kind = grace4, grace_engine.upgrade_required = false
124
124
  doubled-graph lint --repo . # summary.errors = 0
125
125
  doubled-graph migrate check --repo . # map_pending / placeholders — что осталось заполнить
@@ -2,6 +2,67 @@
2
2
 
3
3
  All notable changes to doubled-graph. Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). Versioning: [SemVer](https://semver.org/).
4
4
 
5
+ ## [2.0.1] — 2026-10-23
6
+
7
+ Migration-fidelity fixes on top of 2.0.0 (already published). No artifact-format
8
+ change: upgrade is additive, `pipx upgrade doubled-graph` and a re-run of
9
+ `doubled-graph setup` (or `migrate apply` on an unmigrated repo) is enough.
10
+
11
+ **Why:** 2.0.0 could migrate a real GRACE 3 repository into a project where
12
+ `grace lint` is green but the context documents are empty (`unknown`,
13
+ `Migrated project`, one generic goal) and `TestingStack` cannot produce a
14
+ `<Command>` for `V-<M>`. The checks below make that state visible and prevent
15
+ silent data loss during the copy.
16
+
17
+ ### Fixed — legacy format edge cases (all lose data silently if missed)
18
+ - Namespaced legacy XML: tags arrive as `{urn}M-AUTH`, every `^M-` match failed
19
+ and a project migrated with **zero modules**. Namespaces are stripped before
20
+ matching (elements and attributes).
21
+ - UTF-16/UTF-32/BOM legacy documents: `read_text("utf-8")` raised
22
+ `UnicodeDecodeError`; reads now try `utf-8-sig` → `utf-16` → `utf-32` → `cp1251`.
23
+ - `CDATA` values (`<Language><![CDATA[C#]]></Language>`) were stripped to empty;
24
+ `CDATA` is unwrapped before tag removal.
25
+ - Module `NAME` / `STATUS` / `TYPE` attributes were dropped (the migrator wrote
26
+ `Name` from the id and `Status=existing`); they are preserved (`Type` as a
27
+ free child tag, which GRACE 4 does not validate).
28
+ - Several GRACE 3 verification records for one module (`V-M-AUTH-1`, `-2`, …)
29
+ collapsed into one canonical `V-M-AUTH` and only the first record's evidence
30
+ survived; commands, scenarios, markers, trace assertions and test files are
31
+ now merged.
32
+ - Lowercase/mixed-case contract keys (`role:`, `map_mode:`, `links:`) are
33
+ silently ignored by the engine's field parser and produced
34
+ `markup.invalid-role`; the markup repair normalises keys to UPPERCASE and
35
+ ROLE values to upper case.
36
+ - Windows-style paths (`src\\auth\\a.py`) are normalised to forward slashes, so
37
+ `LINKS`-based file-to-module linkage matches.
38
+ - Self-closing `<UC-1 ... />` entries were skipped by the use-case extractor;
39
+ they are now rendered as goals.
40
+ - `migration.context-skeleton` was emitted even when the repository has no
41
+ artifacts at all (kind `none`); the warning is now gated to migrated projects.
42
+
43
+ ### Fixed — context documents are copied, not skeletonised
44
+ - The migrator now **copies** the legacy context documents instead of shipping a
45
+ skeleton: `technology.xml` fields (`Language`, `Runtime`, `Framework`,
46
+ `TestingStack`, `Observability`) and `requirements.xml` sections
47
+ (`ProjectName`, `Goals` from `UC-*` as text, `Users` from `ACTOR`, `Constraints`)
48
+ are taken from the old document; undeclared fields are marked
49
+ `not specified` (never guessed), and `LegacySource` keeps the archive copy.
50
+ - New `context_gaps` in the migration report plus `migration.context-skeleton`
51
+ warning. The check is gated: it fires when the copied documents are a skeleton
52
+ (0–1 real field) **or** when `TestingStack` is still a placeholder — a richer
53
+ document is trusted as-is. It reads the current `.grace/context`, not the
54
+ legacy documents (which stay on disk forever), so a completed migration
55
+ reports no gaps. Recognised placeholders include the old migrator/bootstrap
56
+ prose (`unknown`, `Migrated project`, `Keep the project governed…`,
57
+ `Describe the primary…`, `Restore the product intent…`), and the bootstrap
58
+ path now writes `not specified` instead of `unknown`.
59
+ - `01-content-gaps.md` gained **Шаг 0 «context-документы»** (before
60
+ `MODULE_MAP`) and a mandatory **Шаг 4a «context gate»**: a `grep` over
61
+ `.grace/context/*.xml` for placeholders plus `context_gaps == []` from
62
+ `migrate check`, with `deployment`/`ux` `not-applicable` explicitly allowed.
63
+ `<Command>` for `V-<M>` must not be derived while `TestingStack` is a
64
+ placeholder.
65
+
5
66
  ## [2.0.0] — 2026-10-23
6
67
 
7
68
  **Breaking: GRACE 4.** The methodology now runs on the `.grace` artifact model
@@ -55,7 +55,7 @@
55
55
  "warnings": [
56
56
  {"code": "HASKELL_PARSER_UNSTABLE", "file": "src/foo.hs", "detail": "…"}
57
57
  ],
58
- "meta": {"phase": "post_migration", "tool_version": "2.0.0", "timestamp_iso": "2026-10-23T10:22:04Z", "trace_id": "…"}
58
+ "meta": {"phase": "post_migration", "tool_version": "2.0.1", "timestamp_iso": "2026-10-23T10:22:04Z", "trace_id": "…"}
59
59
  }
60
60
  ```
61
61
 
@@ -139,7 +139,7 @@ Declared-сторона (`graphs/grace4.py`) при `analyze` не вызыва
139
139
  "phase=post_migration — contract drift check required"
140
140
  ]
141
141
  },
142
- "meta": {"phase": "post_migration", "tool_version": "2.0.0", "timestamp_iso": "...", "trace_id": "..."}
142
+ "meta": {"phase": "post_migration", "tool_version": "2.0.1", "timestamp_iso": "...", "trace_id": "..."}
143
143
  }
144
144
  ```
145
145
 
@@ -232,7 +232,7 @@ Declared-сторона (`graphs/grace4.py`) при `analyze` не вызыва
232
232
  "plan_status": "approved",
233
233
  "tasks": ["T-001", "T-002"]
234
234
  },
235
- "meta": {"phase": "...", "tool_version": "2.0.0", "...": "..."}
235
+ "meta": {"phase": "...", "tool_version": "2.0.1", "...": "..."}
236
236
  }
237
237
  ```
238
238
 
@@ -56,6 +56,8 @@ doubled-graph lint --repo . # summary.errors
56
56
  prompts/upgrade-methodology/01-content-gaps.md
57
57
  ```
58
58
 
59
+ Первым его шагом идёт **Шаг 0 — context-документы**: если `migrate check` показывает `context_gaps` (legacy объявил 0–1 поле), ИИ заполняет `technology.xml` (в первую очередь `TestingStack`) и `requirements.xml` из `<LegacySource>` / манифестов проекта, иначе `<Command>` для `V-<M>` вывести не из чего.
60
+
59
61
  Он детерминированно проходит `map_pending` (карты модулей), `placeholders` (пустые `V-<M>`), `markup_pending` (`LINKS`) и заканчивается финальными CLI-проверками. Выдуманные команды запрещены: если тест не найден или не проходит — запись остаётся пустой, ставится `Priority: low` и строка в `docs/DRIFT.md`.
60
62
 
61
63
  6. Вернуться к обычному циклу: `prompts/maintenance/on-before-edit.md` / `on-after-edit.md`. Legacy `docs/` можно заархивировать отдельным решением (бэкап + approval), это не обязательный шаг.
@@ -23,6 +23,35 @@ doubled-graph health --repo . # модули без verification
23
23
 
24
24
  ---
25
25
 
26
+ ## Шаг 0 — context-документы (до всего остального)
27
+
28
+ Мигратор копирует старые `requirements.xml`/`technology.xml` в `.grace/context/`, но если legacy объявлял 0–1 поле, документ остаётся скелетом: `migrate check` показывает `context_gaps` и warning `migration.context-skeleton`, а поля помечены `not specified`. Пока это не заполнено, из `technology.xml` нельзя вывести `<Command>` для `V-<M>` (шаг 2) — поэтому шаг 0 обязателен и идёт первым.
29
+
30
+ 1. Проверь маркеры командой:
31
+
32
+ ```bash
33
+ grep -n "not specified\|unknown\|Migrated project" .grace/context/*.xml
34
+ ```
35
+
36
+ 2. Заполняй поля в этом порядке (первый источник, который даёт факт, — истина):
37
+
38
+ | Поле | Источник 1 | Источник 2 | Источник 3 |
39
+ |---|---|---|---|
40
+ | `Language`, `Runtime`, `Framework`, `TestingStack`, `Observability` (`technology.xml`) | legacy-значения внутри `<LegacySource>` (там лежит старый `docs/technology.xml` целиком) | манифесты проекта: `*.csproj`/`*.sln`, `package.json`, `pyproject.toml`/`pytest.ini`, `go.mod`, `Cargo.toml`, `pom.xml`/`build.gradle`, `Gemfile`, `composer.json`, `Package.swift`, `mix.exs`, `CMakeLists.txt`/`Makefile` — найденные `find . -maxdepth 3 -name '<manifest>'` | README / CI (`.github/workflows/*`, `.gitlab-ci.yml`) |
41
+ | `ProjectName`, `Goals`, `Users`, `Constraints` (`requirements.xml`) | legacy `<LegacySource>`: `UC-*` → `Goal` текстом, `ACTOR`/`User` → `Users`, `Constraint`/`NFR` → `Constraints` | README (первый абзац/раздел «О проекте») и существующие issue/ADR | — |
42
+ | `deployment.xml` | манифесты инфраструктуры: `Dockerfile`, `docker-compose*.yml`, `k8s/*.yaml`, `helm/`, `Chart.yaml`, `Procfile`, `fly.toml`, `*.tf`, `serverless.yml`, `vercel.json` | README/CI deploy-шаг | `not-applicable` + `<Reason>infrastructure manifests not found</Reason>` |
43
+ | `ux-guidelines.xml` | манифесты фронтенда: `index.html`, `*.xaml`/`*.razor`/`*.vue`/`*.svelte`, `package.json` с react/vue/angular/svelte | README/скриншоты | `not-applicable` + `<Reason>frontend manifests not found</Reason>` |
44
+
45
+ 3. Правила:
46
+ - значения **копируются** из legacy/манифестов, не придумываются; язык — по манифестам проекта, а не по типу задачи;
47
+ - `TestingStack` — команда раннера из манифеста (`package.json → scripts.test`, `pyproject/pytest.ini → pytest`, `*proj/*sln → dotnet test`, `go.mod → go test ./...`, `Cargo.toml → cargo test`, `pom.xml → mvn test`, `build.gradle → ./gradlew test`, `Gemfile → bundle exec rspec`, `composer.json → phpunit`, `Package.swift → swift test`, `mix.exs → mix test`, `CMakeLists.txt/Makefile → ctest/make test`);
48
+ - если факт не найден ни в одном источнике — поле остаётся `not specified`, строка в `docs/DRIFT.md`; шаг 2 в этом случае не выводит `Command` для `V-<M>` (модуль получает `Priority: low`, как в fallback шага 2);
49
+ - `deployment`/`ux` как `not-applicable` с причиной — законное состояние, а не пробел.
50
+
51
+ 4. Проверь: `doubled-graph lint --repo .` (0 errors) и `doubled-graph migrate check --repo .` (`context_gaps == []`).
52
+
53
+ ---
54
+
26
55
  ## Шаг 1 — `MODULE_MAP` (устраняет `markup.module-map-missing`)
27
56
 
28
57
  Для каждого пути из `map_pending`, по порядку:
@@ -155,6 +184,28 @@ doubled-graph module health <M-*> --repo . # критичные моду
155
184
 
156
185
  ---
157
186
 
187
+ ## Шаг 4a — context gate: в context нет заглушек
188
+
189
+ Обязательная проверка перед отчётом: `context/*.xml` не должны содержать ни `unknown`, ни шаблонных фраз мигратора/бутстрапа. Проверяется командой, а не «на глаз»:
190
+
191
+ ```bash
192
+ grep -nE "not specified|unknown|Migrated project|Describe the primary|replace with the real product statement|Keep the project governed|Existing project maintainers|Legacy GRACE 3 documents stay in place|No operational constraints were declared|Users were not described|Restore the product intent|Not configured yet" .grace/context/*.xml
193
+ doubled-graph migrate check --repo . # context_gaps должен быть []
194
+ ```
195
+
196
+ Что означает каждая находка:
197
+
198
+ | Находка | Действие |
199
+ |---|---|
200
+ | `technology: … TestingStack is still a placeholder` | заполнить `TestingStack` из `<LegacySource>` или манифеста (шаг 0), иначе `<Command>` для `V-<M>` не выводится |
201
+ | `technology: 0–1/5 real field(s)` | заполнить Language/Runtime/Framework/Observability по шагу 0 |
202
+ | `requirements: … real section(s)` / `Goals are still a placeholder` | перенести `UC-*`/README-цели в `<Goals>` (шаг 0) |
203
+ | `deployment`/`ux` с `not-applicable` + `<Reason>` | не находка: это законное состояние, пропустить |
204
+
205
+ Если после заполнения `context_gaps` пуст, а `grep` всё ещё что-то находит — это строка внутри `<LegacySource>` (архив), её не трогай: проверяй только рабочие секции, не архив.
206
+
207
+ ---
208
+
158
209
  ## Шаг 5 — отчёт пользователю (формат фиксированный)
159
210
 
160
211
  ```
@@ -173,7 +224,8 @@ health: модулей без verification=<N>
173
224
  | Проверка | Готово, когда |
174
225
  |---|---|
175
226
  | `doubled-graph lint` | `summary.errors == 0` |
176
- | `doubled-graph migrate check` | `map_pending == []`, `placeholders == []` |
227
+ | `doubled-graph migrate check` | `map_pending == []`, `placeholders == []`, `context_gaps == []` |
228
+ | `grep -nE "not specified\|unknown\|Migrated project\|Describe the primary\|Keep the project governed" .grace/context/*.xml` | пусто вне `<LegacySource>`, кроме `deployment`/`ux` с `not-applicable` и `<Reason>` |
177
229
  | `doubled-graph detect-changes --scope all` | `missing_verification` только у записей с `DRIFT.md` |
178
230
  | `doubled-graph health` | нет модулей без `V-<M>` |
179
231
  | `module health` критичных модулей | не `blocked` |
@@ -203,6 +255,19 @@ health: модулей без verification=<N>
203
255
  doubled-graph health --repo .
204
256
  Работай по спискам map_pending / placeholders / markup_pending из вывода.
205
257
 
258
+ Шаг 0. Context-документы (обязательно до MODULE_MAP и V-<M>):
259
+ - проверь маркеры: grep -n "not specified\|unknown\|Migrated project" .grace/context/*.xml
260
+ - technology.xml: Language/Runtime/Framework/TestingStack/Observability — из <LegacySource> (старый documents/technology.xml),
261
+ затем из манифестов проекта (find . -maxdepth 3: *.csproj/*.sln, package.json, pyproject.toml/pytest.ini, go.mod,
262
+ Cargo.toml, pom.xml/build.gradle, Gemfile, composer.json, Package.swift, mix.exs, CMakeLists.txt/Makefile), затем README/CI;
263
+ TestingStack = команда раннера из манифеста (не догадка);
264
+ - requirements.xml: ProjectName/Goals/Users/Constraints — из <LegacySource> (UC-* -> Goal текстом, ACTOR/User -> Users,
265
+ Constraint/NFR -> Constraints), затем README;
266
+ - deployment.xml/ux-guidelines.xml: по инфраструктурным/фронтенд-манифестам; если их нет — <Applicability>not-applicable</Applicability>
267
+ + <Reason>;
268
+ - если факт не найден — поле оставь "not specified" и добавь строку в docs/DRIFT.md; НЕ выдумывай стек.
269
+ - проверь: doubled-graph migrate check --repo . -> context_gaps = []
270
+
206
271
  Шаг 1. MODULE_MAP для каждого пути из map_pending:
207
272
  - прочитай контракт: doubled-graph file show <path> --repo .;
208
273
  - извлеки публичные символы командой `grep` по правилам для языка:
@@ -250,6 +315,11 @@ health: модулей без verification=<N>
250
315
  Если lint вернул project.grace3-detected — выполни doubled-graph migrate apply --repo . и повтори шаги 1–3.
251
316
  Если grace_engine.upgrade_required = true — npm install -g @osovv/grace-cli@4.2.1 и повтори setup.
252
317
 
318
+ Шаг 4a. Context gate (обязательно):
319
+ grep -nE "not specified|unknown|Migrated project|Describe the primary|replace with the real product statement|Keep the project governed|Existing project maintainers|Legacy GRACE 3 documents stay in place|No operational constraints were declared|Users were not described|Restore the product intent|Not configured yet" .grace/context/*.xml
320
+ doubled-graph migrate check --repo . (context_gaps = [])
321
+ Находки вне <LegacySource> исправляй по шагу 0; TestingStack-заглушка блокирует шаг 2.
322
+
253
323
  Шаг 5. Отчёт в фиксированном формате:
254
324
  MODULE_MAP: <N дополнено, K NONE/CONFIG>
255
325
  V-<M>: <N заполнено и прогоняется, K без команды>
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "doubled-graph"
7
- version = "2.0.0"
7
+ version = "2.0.1"
8
8
  description = "MCP facade over CodeGraphContext + GRACE 4 (.grace artifacts): impact, context, drift-detect, migration for LLM-driven codebases."
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -61,6 +61,11 @@ class DeclaredModule:
61
61
  paths: list[str] = field(default_factory=list)
62
62
  exports: list[str] = field(default_factory=list)
63
63
  crosslinks: list[str] = field(default_factory=list)
64
+ #: GRACE 3 declared these as attributes; losing them silently rewrites
65
+ #: module identity/status during migration, so keep them.
66
+ name: str = ""
67
+ status: str = ""
68
+ type: str = ""
64
69
 
65
70
 
66
71
  @dataclass
@@ -165,20 +170,66 @@ def _load_grace4(root: Path, scan_files: bool) -> DeclaredGraph:
165
170
  # ---------------------------------------------------------------------------
166
171
 
167
172
 
173
+ def _read_xml_text(path: Path) -> str:
174
+ """Read a legacy XML file whatever its encoding (UTF-8/UTF-16/BOM).
175
+
176
+ GRACE 3 projects were written by whatever editor the team used; a UTF-16
177
+ document is legal XML and must not abort a migration with a traceback.
178
+ """
179
+ raw = path.read_bytes()
180
+ for encoding in ("utf-8-sig", "utf-16", "utf-32", "cp1251"):
181
+ try:
182
+ return raw.decode(encoding)
183
+ except (UnicodeDecodeError, UnicodeError, LookupError):
184
+ continue
185
+ return raw.decode("utf-8", errors="replace")
186
+
187
+
188
+ def _strip_namespaces(root: ET.Element) -> ET.Element:
189
+ """Drop ``{namespace}`` prefixes so tag matching works on namespaced XML.
190
+
191
+ Legacy documents occasionally declare a default ``xmlns``; ElementTree then
192
+ reports tags as ``{urn}M-AUTH`` and every ``^M-`` regex misses, which
193
+ silently produced a zero-module migration.
194
+ """
195
+ for element in root.iter():
196
+ if isinstance(element.tag, str) and "}" in element.tag:
197
+ element.tag = element.tag.rsplit("}", 1)[-1]
198
+ for key in list(element.attrib):
199
+ if "}" in key:
200
+ element.attrib[key.rsplit("}", 1)[-1]] = element.attrib.pop(key)
201
+ return root
202
+
203
+
204
+ def _attribute(element: ET.Element, *names: str) -> str:
205
+ """Attribute value by any of ``names``, case-insensitively."""
206
+ for key, value in element.attrib.items():
207
+ if key.lower() in {name.lower() for name in names}:
208
+ text = (value or "").strip()
209
+ if text:
210
+ return text
211
+ return ""
212
+
213
+
214
+ def _normalize_path(value: str) -> str:
215
+ """Store paths in a single portable form (forward slashes)."""
216
+ return value.replace("\\", "/").strip()
217
+
218
+
168
219
  def _safe_parse(path: Path) -> tuple[ET.Element | None, str]:
169
220
  """Parse XML, escaping bare ``&`` on retry. Returns (root, error_msg)."""
170
221
  if not path.exists():
171
222
  return None, ""
172
- raw = path.read_text(encoding="utf-8")
223
+ raw = _read_xml_text(path)
173
224
  try:
174
- return ET.fromstring(raw), ""
225
+ return _strip_namespaces(ET.fromstring(raw)), ""
175
226
  except ET.ParseError:
176
227
  pass
177
228
  sanitized = _BARE_AMP.sub("&amp;", raw)
178
229
  if sanitized == raw:
179
230
  return None, f"XML parse error in {path.name}"
180
231
  try:
181
- return ET.fromstring(sanitized), ""
232
+ return _strip_namespaces(ET.fromstring(sanitized)), ""
182
233
  except ET.ParseError as exc:
183
234
  return None, f"XML parse error in {path.name}: {exc}"
184
235
 
@@ -215,12 +266,16 @@ def _merge_dev_plan_module(graph: DeclaredGraph, mod: ET.Element) -> None:
215
266
  if purpose and not existing.purpose:
216
267
  existing.purpose = purpose
217
268
 
269
+ existing.name = existing.name or _attribute(mod, "NAME", "Name")
270
+ existing.status = existing.status or _attribute(mod, "STATUS", "Status")
271
+ existing.type = existing.type or _attribute(mod, "TYPE", "Type")
272
+
218
273
  for src in mod.findall("target/source"):
219
274
  if src.text:
220
- _append_unique(existing.paths, src.text.strip())
275
+ _append_unique(existing.paths, _normalize_path(src.text))
221
276
  for tst in mod.findall("target/tests"):
222
277
  if tst.text:
223
- _append_unique(existing.paths, tst.text.strip())
278
+ _append_unique(existing.paths, _normalize_path(tst.text))
224
279
 
225
280
  for el in mod.findall("interface/*"):
226
281
  name = _export_name(el.tag or "")
@@ -238,9 +293,13 @@ def _merge_kg_module(graph: DeclaredGraph, mod: ET.Element) -> None:
238
293
  if purpose and not existing.purpose:
239
294
  existing.purpose = purpose
240
295
 
296
+ existing.name = existing.name or _attribute(mod, "NAME", "Name")
297
+ existing.status = existing.status or _attribute(mod, "STATUS", "Status")
298
+ existing.type = existing.type or _attribute(mod, "TYPE", "Type")
299
+
241
300
  for el in mod.findall("path"):
242
301
  if el.text:
243
- _append_unique(existing.paths, el.text.strip())
302
+ _append_unique(existing.paths, _normalize_path(el.text))
244
303
 
245
304
  for el in mod.findall("annotations/*"):
246
305
  name = _export_name(el.tag or "")
@@ -282,8 +341,8 @@ def _load_legacy(repo_path: Path) -> DeclaredGraph:
282
341
  for mod in _iter_module_tags(root):
283
342
  _merge_kg_module(graph, mod)
284
343
  for cl in root.iter("CrossLink"):
285
- src_id = cl.get("from") or ""
286
- tgt_id = cl.get("to") or ""
344
+ src_id = _attribute(cl, "from", "source", "src")
345
+ tgt_id = _attribute(cl, "to", "target", "dst")
287
346
  if not src_id or not tgt_id:
288
347
  continue
289
348
  mod = graph.modules.get(src_id)