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.
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/PKG-INFO +2 -2
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/README.md +1 -1
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/CHANGELOG.md +61 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/TOOLS.md +3 -3
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/upgrade-methodology/00-entry-prompt.md +2 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/upgrade-methodology/01-content-gaps.md +71 -1
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/pyproject.toml +1 -1
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/declared.py +67 -8
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/migrations/grace3_to_grace4.py +358 -46
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_migration_grace3_to_grace4.py +285 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/.gitignore +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/ARTICLE.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/INDEX.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/LICENSE +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/HOOKS.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/docs/SPEC.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-existing-project/README.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-maintenance/README.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-ai-new-project/README.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/FAQ.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/deep-dive.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/for-humans/getting-started.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/README.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/approval-checkpoints.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/artifacts.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/auto-scaling.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/drift-and-priority.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/_template.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/csharp.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/go.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/java.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/python.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/rust.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/swift.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/language-adapters/typescript.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/principles.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/roles.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/_template.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/claude-code.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/codex.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/continue.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/cursor.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/local-llm.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/runtime-adapters/windsurf.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/tools.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/methodology/workflow.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-after-edit.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-before-edit.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-drift-detected.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/maintenance/on-user-manual-edit.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/00-entry-prompt.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/01-discover.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/02-reconstruct-intent.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/03-draft-plan.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/04-markup-codebase.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/05-verification-and-logs.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/migrate-existing-project/06-validation-gates.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/00-entry-prompt.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/01-intent-interview.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/02-requirements.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/03-technology.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/04-development-plan.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/05-verification-plan.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/06-generate.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/prompts/new-project/07-post-init-sync.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/__main__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/cli.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/config.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-after-edit/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-before-edit/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-drift/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-guide/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-manual-edit/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-refactor/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/data/skills/doubled-graph-verify/SKILL.md +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/crossref.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/graphs/grace4.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/hooks_installer.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/cgc.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/integrations/grace_cli.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/migrations/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/policy/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/policy/phase.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/server.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/setup.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/storage/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/storage/paths.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/_meta.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/analyze.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/context.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/detect_changes.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/src/doubled_graph/tools/impact.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/__init__.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_cgc_contract.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_grace4_artifacts.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_grace_cli_engine.py +0 -0
- {doubled_graph-2.0.0 → doubled_graph-2.0.1}/tests/test_setup_artifacts.py +0 -0
- {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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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.
|
|
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
|
|
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("&", 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
|
|
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
|
|
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
|
|
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
|
|
286
|
-
tgt_id = cl
|
|
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)
|