@documentation-robotics/cli 0.2.0 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +36 -2
- package/dist/analysis/relationship-classifier.js +2 -9
- package/dist/analysis/relationship-classifier.js.map +2 -2
- package/dist/analyzers/call-graph-utils.js +2 -2
- package/dist/analyzers/call-graph-utils.js.map +2 -2
- package/dist/analyzers/cards/build.js +171 -0
- package/dist/analyzers/cards/build.js.map +7 -0
- package/dist/analyzers/cards/classify.js +42 -0
- package/dist/analyzers/cards/classify.js.map +7 -0
- package/dist/analyzers/cards/edge-index.js +44 -0
- package/dist/analyzers/cards/edge-index.js.map +7 -0
- package/dist/analyzers/cards/facts.js +46 -0
- package/dist/analyzers/cards/facts.js.map +7 -0
- package/dist/analyzers/cards/graph.js +42 -0
- package/dist/analyzers/cards/graph.js.map +7 -0
- package/dist/analyzers/cards/index.js +8 -0
- package/dist/analyzers/cards/index.js.map +7 -0
- package/dist/analyzers/cards/tier0.js +18 -0
- package/dist/analyzers/cards/tier0.js.map +7 -0
- package/dist/analyzers/cards/types.js +1 -0
- package/dist/analyzers/cards/types.js.map +7 -0
- package/dist/analyzers/cbm-analyzer.js +195 -171
- package/dist/analyzers/cbm-analyzer.js.map +2 -2
- package/dist/analyzers/cbm-responses.js +336 -0
- package/dist/analyzers/cbm-responses.js.map +7 -0
- package/dist/analyzers/edge-relationships.js +45 -0
- package/dist/analyzers/edge-relationships.js.map +7 -0
- package/dist/analyzers/mapping-loader.js +9 -0
- package/dist/analyzers/mapping-loader.js.map +2 -2
- package/dist/analyzers/tier0-rules.js +153 -0
- package/dist/analyzers/tier0-rules.js.map +7 -0
- package/dist/analyzers/types.js.map +1 -1
- package/dist/audit/relationships/ai/prompts.js +10 -10
- package/dist/audit/relationships/ai/prompts.js.map +2 -2
- package/dist/audit/relationships/analysis/layer-templates.js +52 -52
- package/dist/audit/relationships/analysis/layer-templates.js.map +1 -1
- package/dist/cli.js +12 -2
- package/dist/cli.js.map +2 -2
- package/dist/coding-agents/default-permissions.js +31 -1
- package/dist/coding-agents/default-permissions.js.map +2 -2
- package/dist/coding-agents/drbot-mcp.js +167 -0
- package/dist/coding-agents/drbot-mcp.js.map +7 -0
- package/dist/commands/add.js +3 -2
- package/dist/commands/add.js.map +2 -2
- package/dist/commands/analyzer.js +117 -14
- package/dist/commands/analyzer.js.map +2 -2
- package/dist/commands/changeset-verify.js +55 -0
- package/dist/commands/changeset-verify.js.map +7 -0
- package/dist/commands/changeset.js +43 -22
- package/dist/commands/changeset.js.map +2 -2
- package/dist/commands/delete.js +10 -4
- package/dist/commands/delete.js.map +2 -2
- package/dist/commands/farm.js +176 -60
- package/dist/commands/farm.js.map +2 -2
- package/dist/commands/mcp.js +19 -4
- package/dist/commands/mcp.js.map +2 -2
- package/dist/commands/relationship.js +207 -26
- package/dist/commands/relationship.js.map +2 -2
- package/dist/commands/update.js +42 -23
- package/dist/commands/update.js.map +2 -2
- package/dist/commands/upgrade.js +99 -11
- package/dist/commands/upgrade.js.map +3 -3
- package/dist/commands/version.js +1 -1
- package/dist/commands/visualize.js +4 -3
- package/dist/commands/visualize.js.map +2 -2
- package/dist/core/catalog-migration.js +33 -0
- package/dist/core/catalog-migration.js.map +7 -0
- package/dist/core/changeset-analysis.js +421 -0
- package/dist/core/changeset-analysis.js.map +7 -0
- package/dist/core/changeset-base-index.js +153 -0
- package/dist/core/changeset-base-index.js.map +7 -0
- package/dist/core/changeset-brief-delta.js +102 -0
- package/dist/core/changeset-brief-delta.js.map +7 -0
- package/dist/core/changeset-graph.js +281 -0
- package/dist/core/changeset-graph.js.map +7 -0
- package/dist/core/changeset-only-guard.js +32 -0
- package/dist/core/changeset-only-guard.js.map +7 -0
- package/dist/core/changeset-summary.js +68 -0
- package/dist/core/changeset-summary.js.map +7 -0
- package/dist/core/changeset-validator.js +66 -20
- package/dist/core/changeset-validator.js.map +2 -2
- package/dist/core/changeset-verify.js +518 -0
- package/dist/core/changeset-verify.js.map +7 -0
- package/dist/core/changeset.js +56 -9
- package/dist/core/changeset.js.map +2 -2
- package/dist/core/element-type-migration.js +145 -0
- package/dist/core/element-type-migration.js.map +7 -0
- package/dist/core/farm-manifest.js +12 -6
- package/dist/core/farm-manifest.js.map +2 -2
- package/dist/core/farm-sync-engine.js +1 -1
- package/dist/core/farm-sync-engine.js.map +2 -2
- package/dist/core/farm-sync-state.js +10 -2
- package/dist/core/farm-sync-state.js.map +2 -2
- package/dist/core/migration-registry.js +75 -46
- package/dist/core/migration-registry.js.map +2 -2
- package/dist/core/model-upgrade.js +102 -0
- package/dist/core/model-upgrade.js.map +7 -0
- package/dist/core/model.js +82 -30
- package/dist/core/model.js.map +2 -2
- package/dist/core/mutation-handler.js +71 -6
- package/dist/core/mutation-handler.js.map +2 -2
- package/dist/core/relationship-migration-table.js +6639 -0
- package/dist/core/relationship-migration-table.js.map +7 -0
- package/dist/core/relationship-migration.js +272 -0
- package/dist/core/relationship-migration.js.map +7 -0
- package/dist/core/relationship-prune.js +49 -0
- package/dist/core/relationship-prune.js.map +7 -0
- package/dist/core/relationship-staging.js +68 -0
- package/dist/core/relationship-staging.js.map +7 -0
- package/dist/core/relationships.js +26 -4
- package/dist/core/relationships.js.map +2 -2
- package/dist/core/report-data-model.js +7 -13
- package/dist/core/report-data-model.js.map +2 -2
- package/dist/core/staged-changeset-storage.js +64 -17
- package/dist/core/staged-changeset-storage.js.map +2 -2
- package/dist/core/staged-source-reference.js +28 -0
- package/dist/core/staged-source-reference.js.map +7 -0
- package/dist/core/staging-area.js +83 -18
- package/dist/core/staging-area.js.map +2 -2
- package/dist/core/virtual-projection.js +13 -5
- package/dist/core/virtual-projection.js.map +2 -2
- package/dist/export/archimate-exporter.js +1 -0
- package/dist/export/archimate-exporter.js.map +2 -2
- package/dist/generated/compiled-validators.js +2 -2
- package/dist/generated/compiled-validators.js.map +2 -2
- package/dist/generated/layer-registry.js +16 -16
- package/dist/generated/layer-registry.js.map +2 -2
- package/dist/generated/node-types.js +186 -168
- package/dist/generated/node-types.js.map +2 -2
- package/dist/generated/relationship-index.js +2236 -7790
- package/dist/generated/relationship-index.js.map +2 -2
- package/dist/integrations/claude_code/agents/dr-architect.md +16 -3
- package/dist/integrations/claude_code/agents/dr-extractor.md +1 -1
- package/dist/integrations/claude_code/commands/dr-analyzer.md +565 -0
- package/dist/integrations/claude_code/commands/dr-map.md +13 -0
- package/dist/integrations/claude_code/commands/dr-plan.md +40 -0
- package/dist/integrations/claude_code/commands/dr-relate.md +106 -96
- package/dist/integrations/claude_code/commands/dr-verify.md +45 -2
- package/dist/integrations/claude_code/docs/END_TO_END_WALKTHROUGH.md +14 -0
- package/dist/integrations/claude_code/docs/MCP_SERVER.md +26 -2
- package/dist/integrations/claude_code/docs/QUICK_START.md +37 -8
- package/dist/integrations/claude_code/docs/README.md +48 -1
- package/dist/integrations/claude_code/skills/dr_01_motivation_layer/SKILL.md +93 -72
- package/dist/integrations/claude_code/skills/dr_02_business_layer/SKILL.md +163 -126
- package/dist/integrations/claude_code/skills/dr_03_product_layer/SKILL.md +161 -126
- package/dist/integrations/claude_code/skills/dr_04_security_layer/SKILL.md +153 -133
- package/dist/integrations/claude_code/skills/dr_05_application_layer/SKILL.md +129 -100
- package/dist/integrations/claude_code/skills/dr_06_technology_layer/SKILL.md +111 -94
- package/dist/integrations/claude_code/skills/dr_07_api_layer/SKILL.md +143 -157
- package/dist/integrations/claude_code/skills/dr_08_data_model_layer/SKILL.md +117 -33
- package/dist/integrations/claude_code/skills/dr_09_dataset_layer/SKILL.md +74 -25
- package/dist/integrations/claude_code/skills/dr_10_ux_layer/SKILL.md +88 -12
- package/dist/integrations/claude_code/skills/dr_11_navigation_layer/SKILL.md +83 -24
- package/dist/integrations/claude_code/skills/dr_12_apm_layer/SKILL.md +57 -12
- package/dist/integrations/claude_code/skills/dr_13_testing_layer/SKILL.md +92 -15
- package/dist/integrations/claude_code/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/claude_code/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/claude_code/skills/dr_engineering_guide/SKILL.md +1 -1
- package/dist/integrations/github_copilot/agents/dr-architect.md +1 -1
- package/dist/integrations/github_copilot/skills/dr_01_motivation_layer/SKILL.md +182 -102
- package/dist/integrations/github_copilot/skills/dr_02_business_layer/SKILL.md +266 -148
- package/dist/integrations/github_copilot/skills/dr_03_product_layer/SKILL.md +547 -86
- package/dist/integrations/github_copilot/skills/dr_04_security_layer/SKILL.md +164 -169
- package/dist/integrations/github_copilot/skills/dr_05_application_layer/SKILL.md +420 -150
- package/dist/integrations/github_copilot/skills/dr_06_technology_layer/SKILL.md +331 -138
- package/dist/integrations/github_copilot/skills/dr_07_api_layer/SKILL.md +265 -201
- package/dist/integrations/github_copilot/skills/dr_08_data_model_layer/SKILL.md +181 -58
- package/dist/integrations/github_copilot/skills/dr_09_dataset_layer/SKILL.md +209 -57
- package/dist/integrations/github_copilot/skills/dr_10_ux_layer/SKILL.md +277 -112
- package/dist/integrations/github_copilot/skills/dr_11_navigation_layer/SKILL.md +219 -70
- package/dist/integrations/github_copilot/skills/dr_12_apm_layer/SKILL.md +189 -105
- package/dist/integrations/github_copilot/skills/dr_13_testing_layer/SKILL.md +222 -102
- package/dist/integrations/github_copilot/skills/dr_change_planning/SKILL.md +119 -0
- package/dist/integrations/github_copilot/skills/dr_changeset_reviewer/SKILL.md +3 -0
- package/dist/integrations/hash-utils.js.map +2 -2
- package/dist/mcp/api-key-manager.js +14 -1
- package/dist/mcp/api-key-manager.js.map +2 -2
- package/dist/mcp/changeset-only.js +34 -0
- package/dist/mcp/changeset-only.js.map +7 -0
- package/dist/mcp/tool-registry.js +19 -3
- package/dist/mcp/tool-registry.js.map +2 -2
- package/dist/mcp/tools/changeset-analysis-tools.js +78 -0
- package/dist/mcp/tools/changeset-analysis-tools.js.map +7 -0
- package/dist/mcp/tools/changeset-editing.js +184 -0
- package/dist/mcp/tools/changeset-editing.js.map +7 -0
- package/dist/mcp/tools/changeset-lifecycle.js +83 -0
- package/dist/mcp/tools/changeset-lifecycle.js.map +7 -0
- package/dist/mcp/tools/changeset-shared.js +31 -0
- package/dist/mcp/tools/changeset-shared.js.map +7 -0
- package/dist/mcp/tools/changeset-show.js +17 -1
- package/dist/mcp/tools/changeset-show.js.map +2 -2
- package/dist/mcp/tools/changeset-verify.js +43 -0
- package/dist/mcp/tools/changeset-verify.js.map +7 -0
- package/dist/mcp/tools/index.js +19 -1
- package/dist/mcp/tools/index.js.map +2 -2
- package/dist/mcp/tools/model-add.js +6 -5
- package/dist/mcp/tools/model-add.js.map +2 -2
- package/dist/mcp/tools/model-delete.js +2 -2
- package/dist/mcp/tools/model-delete.js.map +2 -2
- package/dist/mcp/tools/model-search.js +14 -0
- package/dist/mcp/tools/model-search.js.map +2 -2
- package/dist/mcp/tools/model-show.js +29 -2
- package/dist/mcp/tools/model-show.js.map +2 -2
- package/dist/mcp/tools/model-trace.js +93 -30
- package/dist/mcp/tools/model-trace.js.map +2 -2
- package/dist/mcp/tools/model-update.js +35 -19
- package/dist/mcp/tools/model-update.js.map +2 -2
- package/dist/mcp/tools/relationship-tools.js +98 -0
- package/dist/mcp/tools/relationship-tools.js.map +7 -0
- package/dist/mcp/tools/shared.js +25 -2
- package/dist/mcp/tools/shared.js.map +2 -2
- package/dist/mcp/tools/spec-type.js +90 -0
- package/dist/mcp/tools/spec-type.js.map +7 -0
- package/dist/schemas/bundled/analyzers/cbm.json +1186 -68
- package/dist/schemas/bundled/analyzers/manifest.json +2 -2
- package/dist/schemas/bundled/api.json +1029 -1658
- package/dist/schemas/bundled/apm.json +499 -734
- package/dist/schemas/bundled/application.json +735 -371
- package/dist/schemas/bundled/base.json +3 -220
- package/dist/schemas/bundled/business.json +452 -366
- package/dist/schemas/bundled/data-model.json +395 -411
- package/dist/schemas/bundled/data-store.json +334 -558
- package/dist/schemas/bundled/manifest.json +18 -18
- package/dist/schemas/bundled/motivation.json +349 -368
- package/dist/schemas/bundled/navigation.json +415 -731
- package/dist/schemas/bundled/product.json +325 -159
- package/dist/schemas/bundled/security.json +864 -1232
- package/dist/schemas/bundled/technology.json +574 -703
- package/dist/schemas/bundled/testing.json +718 -1643
- package/dist/schemas/bundled/ux.json +739 -1352
- package/dist/server/schemas.js +8 -0
- package/dist/server/schemas.js.map +2 -2
- package/dist/server/server.js +218 -34
- package/dist/server/server.js.map +2 -2
- package/dist/server-entry.js +2 -1
- package/dist/server-entry.js.map +2 -2
- package/dist/telemetry/index.js +1 -1
- package/dist/utils/ansi-suppressor.js +6 -8
- package/dist/utils/ansi-suppressor.js.map +2 -2
- package/dist/utils/attribute-patch.js +41 -0
- package/dist/utils/attribute-patch.js.map +7 -0
- package/dist/utils/error-hints.js +29 -0
- package/dist/utils/error-hints.js.map +7 -0
- package/dist/utils/errors.js +9 -3
- package/dist/utils/errors.js.map +2 -2
- package/dist/utils/project-paths.js +26 -1
- package/dist/utils/project-paths.js.map +2 -2
- package/dist/utils/source-reference.js +14 -12
- package/dist/utils/source-reference.js.map +2 -2
- package/dist/utils/spec-version.js +2 -2
- package/dist/utils/spec-version.js.map +1 -1
- package/dist/validators/composed-reference-validator.js +5 -27
- package/dist/validators/composed-reference-validator.js.map +2 -2
- package/dist/validators/reference-validator.js +0 -7
- package/dist/validators/reference-validator.js.map +2 -2
- package/dist/validators/relationship-schema-validator.js +3 -3
- package/dist/validators/relationship-schema-validator.js.map +2 -2
- package/dist/validators/schema-validator.js +27 -9
- package/dist/validators/schema-validator.js.map +2 -2
- package/dist/validators/types.js.map +1 -1
- package/dist/viewer/README.md +6 -6
- package/dist/viewer/assets/main-CxzcuU_o.js +84 -0
- package/dist/viewer/assets/main-j-7ULYpm.css +1 -0
- package/dist/viewer/index.html +2 -2
- package/dist/viewer/manifest.json +10 -10
- package/dist/viewer/version.json +1 -1
- package/package.json +2 -1
- package/dist/viewer/assets/main-B7AOQXXX.js +0 -81
- package/dist/viewer/assets/main-vdrFrG7q.css +0 -1
|
@@ -11,13 +11,13 @@ triggers:
|
|
|
11
11
|
"test strategy",
|
|
12
12
|
"coverage model"
|
|
13
13
|
]
|
|
14
|
-
version: 0.8.
|
|
14
|
+
version: 0.8.3
|
|
15
15
|
---
|
|
16
16
|
|
|
17
17
|
# Testing Layer Skill
|
|
18
18
|
|
|
19
19
|
**Layer Number:** 12
|
|
20
|
-
**Specification:** Metadata Model Spec v0.8.
|
|
20
|
+
**Specification:** Metadata Model Spec v0.8.3
|
|
21
21
|
**Purpose:** Defines test coverage using ISP (Input Space Partitioning) Coverage Model, specifying coverage requirements, test cases, and input partitions.
|
|
22
22
|
|
|
23
23
|
---
|
|
@@ -30,7 +30,7 @@ The Testing Layer captures **test coverage strategy**:
|
|
|
30
30
|
- **COVERAGE TARGETS** - What needs testing (APIs, features, data)
|
|
31
31
|
- **INPUT PARTITIONS** - Partition input space for systematic coverage
|
|
32
32
|
- **TEST CASES** - Concrete test case sketches
|
|
33
|
-
- **CONTEXT VARIATIONS** -
|
|
33
|
+
- **CONTEXT VARIATIONS** - Test strategy variation types (functional, load, security, regression, smoke, exploratory)
|
|
34
34
|
- **COVERAGE GAPS** - Identified gaps in coverage
|
|
35
35
|
|
|
36
36
|
This layer uses **ISP Coverage Model** (systematic input space partitioning).
|
|
@@ -44,26 +44,86 @@ This layer uses **ISP Coverage Model** (systematic input space partitioning).
|
|
|
44
44
|
> **CLI Introspection:** Run `dr schema types testing` for the authoritative, always-current list of node types.
|
|
45
45
|
> Run `dr schema node <type-id>` for full attribute details on any type.
|
|
46
46
|
|
|
47
|
-
### Core Testing Entities (17
|
|
48
|
-
|
|
49
|
-
| Entity Type | Description
|
|
50
|
-
| --------------------------- |
|
|
51
|
-
| **TestCoverageModel** | Root coverage model for system/component
|
|
52
|
-
| **TestCoverageTarget** | What needs testing (API, feature, data)
|
|
53
|
-
| **InputSpacePartition** | Partition of input space
|
|
54
|
-
| **PartitionValue** | Value/range within partition
|
|
55
|
-
| **InputPartitionSelection** | Selected partition values for coverage
|
|
56
|
-
| **
|
|
57
|
-
| **
|
|
58
|
-
| **
|
|
59
|
-
| **
|
|
60
|
-
| **
|
|
61
|
-
| **
|
|
62
|
-
| **
|
|
63
|
-
| **
|
|
64
|
-
| **
|
|
65
|
-
| **
|
|
66
|
-
| **
|
|
47
|
+
### Core Testing Entities (17 types)
|
|
48
|
+
|
|
49
|
+
| Entity Type | Description |
|
|
50
|
+
| --------------------------- | ------------------------------------------------------------------------------ |
|
|
51
|
+
| **TestCoverageModel** | Root coverage model for system/component |
|
|
52
|
+
| **TestCoverageTarget** | What needs testing (API, feature, data) |
|
|
53
|
+
| **InputSpacePartition** | Partition of input space |
|
|
54
|
+
| **PartitionValue** | Value/range within partition |
|
|
55
|
+
| **InputPartitionSelection** | Selected partition values for coverage |
|
|
56
|
+
| **InputSelection** | Concrete resolved input value chosen from a partition for a specific test case |
|
|
57
|
+
| **CoverageRequirement** | Specific coverage requirement |
|
|
58
|
+
| **TestCaseSketch** | High-level test case description |
|
|
59
|
+
| **OutcomeCategory** | Expected outcome categories |
|
|
60
|
+
| **ContextVariation** | Environmental/contextual variation |
|
|
61
|
+
| **EnvironmentFactor** | Environment-specific factor |
|
|
62
|
+
| **PartitionDependency** | Dependencies between partitions |
|
|
63
|
+
| **CoverageGap** | Identified gap in coverage |
|
|
64
|
+
| **CoverageExclusion** | Explicitly excluded coverage |
|
|
65
|
+
| **CoverageSummary** | Summary of coverage status |
|
|
66
|
+
| **TargetCoverageSummary** | Coverage summary for target |
|
|
67
|
+
| **TargetInputField** | Input field for target |
|
|
68
|
+
|
|
69
|
+
---
|
|
70
|
+
|
|
71
|
+
## Type Decision Tree
|
|
72
|
+
|
|
73
|
+
Use this decision tree **before assigning a type** to any testing concept.
|
|
74
|
+
|
|
75
|
+
```
|
|
76
|
+
IS this the root coverage plan for a system, service, or feature?
|
|
77
|
+
→ testing.testcoveragemodel
|
|
78
|
+
|
|
79
|
+
IS this a specific thing being tested (an API operation, feature, workflow, data field)?
|
|
80
|
+
→ testing.testcoveragetarget
|
|
81
|
+
|
|
82
|
+
IS this a division of an input field's possible values (valid/invalid/boundary ranges)?
|
|
83
|
+
→ testing.inputspacepartition
|
|
84
|
+
|
|
85
|
+
IS this one specific value or boundary within a partition?
|
|
86
|
+
→ testing.partitionvalue
|
|
87
|
+
|
|
88
|
+
IS this a constraint that links partition values to other partitions (e.g., field B depends on field A)?
|
|
89
|
+
→ testing.partitiondependency
|
|
90
|
+
|
|
91
|
+
IS this a declarative selection of which partition values to exercise in a test run?
|
|
92
|
+
→ testing.inputpartitionselection
|
|
93
|
+
|
|
94
|
+
IS this a single concrete resolved value chosen from a partition for a specific test case?
|
|
95
|
+
→ testing.inputselection
|
|
96
|
+
|
|
97
|
+
IS this an overall coverage requirement (criteria like "each-choice", "pairwise")?
|
|
98
|
+
→ testing.coveragerequirement
|
|
99
|
+
|
|
100
|
+
IS this a concrete high-level description of a single test case?
|
|
101
|
+
→ testing.testcasesketch
|
|
102
|
+
|
|
103
|
+
IS this a category of expected outcomes (success, error, edge-case)?
|
|
104
|
+
→ testing.outcomecategory
|
|
105
|
+
|
|
106
|
+
IS this a test strategy variation type — functional, load, security, regression, smoke, or exploratory testing?
|
|
107
|
+
→ testing.contextvariation
|
|
108
|
+
|
|
109
|
+
IS this a concrete environmental setting with a specific value (e.g., os=Linux, locale=en-US, network=low-bandwidth)?
|
|
110
|
+
→ testing.environmentfactor
|
|
111
|
+
|
|
112
|
+
IS this an input field on a target that is mapped to partitions?
|
|
113
|
+
→ testing.targetinputfield
|
|
114
|
+
|
|
115
|
+
IS this an identified gap — something that should be tested but is not?
|
|
116
|
+
→ testing.coveragegap
|
|
117
|
+
|
|
118
|
+
IS this a deliberate decision to NOT test something, with documented rationale?
|
|
119
|
+
→ testing.coverageexclusion
|
|
120
|
+
|
|
121
|
+
IS this a rollup of coverage metrics across all targets?
|
|
122
|
+
→ testing.coveragesummary
|
|
123
|
+
|
|
124
|
+
IS this a per-target rollup of sketch/implementation/automation counts?
|
|
125
|
+
→ testing.targetcoveragesummary
|
|
126
|
+
```
|
|
67
127
|
|
|
68
128
|
---
|
|
69
129
|
|
|
@@ -79,22 +139,85 @@ Activate when the user:
|
|
|
79
139
|
|
|
80
140
|
---
|
|
81
141
|
|
|
82
|
-
##
|
|
83
|
-
|
|
84
|
-
|
|
142
|
+
## Intra-Layer Relationships
|
|
143
|
+
|
|
144
|
+
Each testing type has one composing parent. The model composes targets, partitions, context
|
|
145
|
+
variations, environment factors, exclusions and the summary; a target composes its input fields,
|
|
146
|
+
outcome categories, coverage requirements and test case sketches. A sketch `validates` the one
|
|
147
|
+
requirement it exercises and `references` its context variation and outcome category. Only target
|
|
148
|
+
input fields reference the field under test, so partitions stay reusable. Summaries report with
|
|
149
|
+
`references` and `aggregates`; they never `validates`.
|
|
150
|
+
|
|
151
|
+
| Source Type | Predicate | Target Type | Example |
|
|
152
|
+
| ----------------------- | ---------- | ----------------------- | ------------------------------------------------------------------------- |
|
|
153
|
+
| TestCoverageModel | composes | TestCoverageTarget | "Login API Coverage Model" composes "Login Endpoint Coverage" |
|
|
154
|
+
| TestCoverageModel | composes | ContextVariation | "Login API Coverage Model" composes "Security Testing" |
|
|
155
|
+
| TestCoverageModel | composes | EnvironmentFactor | "Login API Coverage Model" composes "Rate Limiter Enabled" |
|
|
156
|
+
| TestCoverageModel | composes | InputSpacePartition | "Login API Coverage Model" composes "Email Format Partition" |
|
|
157
|
+
| TestCoverageModel | composes | CoverageExclusion | "Login API Coverage Model" composes "SSO Login Out Of Scope" |
|
|
158
|
+
| TestCoverageModel | composes | CoverageSummary | "Login API Coverage Model" composes "Login Coverage Summary" |
|
|
159
|
+
| TestCoverageTarget | composes | TargetInputField | "Login Endpoint Coverage" composes "email field" |
|
|
160
|
+
| TestCoverageTarget | composes | OutcomeCategory | "Login Endpoint Coverage" composes "Invalid Credentials (401)" |
|
|
161
|
+
| TestCoverageTarget | composes | CoverageRequirement | "Login Endpoint Coverage" composes "Pairwise Credentials" |
|
|
162
|
+
| TestCoverageTarget | composes | TestCaseSketch | "Login Endpoint Coverage" composes "Valid Login Test Case" |
|
|
163
|
+
| TargetInputField | references | InputSpacePartition | "email field" references "Email Format Partition" |
|
|
164
|
+
| InputSpacePartition | composes | PartitionValue | "Email Format Partition" composes "Malformed Email" |
|
|
165
|
+
| PartitionDependency | references | PartitionValue | "Locked Account Requires Valid Email" references "Locked Account" |
|
|
166
|
+
| PartitionDependency | references | InputSpacePartition | "Locked Account Requires Valid Email" references "Email Format Partition" |
|
|
167
|
+
| CoverageRequirement | composes | InputPartitionSelection | "Pairwise Credentials" composes "Email x Password Selection" |
|
|
168
|
+
| CoverageRequirement | references | OutcomeCategory | "Pairwise Credentials" references "Invalid Credentials (401)" |
|
|
169
|
+
| CoverageRequirement | references | ContextVariation | "Pairwise Credentials" references "Security Testing" |
|
|
170
|
+
| InputPartitionSelection | references | PartitionValue | "Email x Password Selection" references "Malformed Email" |
|
|
171
|
+
| TestCaseSketch | validates | CoverageRequirement | "Valid Login Test Case" validates "Pairwise Credentials" |
|
|
172
|
+
| TestCaseSketch | composes | InputSelection | "Valid Login Test Case" composes "Valid Email + Valid Password" |
|
|
173
|
+
| InputSelection | references | PartitionValue | "Valid Email + Valid Password" references "Well-Formed Email" |
|
|
174
|
+
| TestCaseSketch | references | OutcomeCategory | "Valid Login Test Case" references "Success (200 + JWT)" |
|
|
175
|
+
| TestCaseSketch | references | ContextVariation | "Brute Force Lockout Test" references "Security Testing" |
|
|
176
|
+
| ContextVariation | references | EnvironmentFactor | "Security Testing" references "Rate Limiter Enabled" |
|
|
177
|
+
| CoverageGap | references | CoverageRequirement | "No MFA Cases" references "Pairwise Credentials" |
|
|
178
|
+
| CoverageExclusion | references | TestCoverageTarget | "SSO Login Out Of Scope" references "Login Endpoint Coverage" |
|
|
179
|
+
| CoverageExclusion | references | CoverageGap | "SSO Login Out Of Scope" references "No SSO Cases" |
|
|
180
|
+
| TargetCoverageSummary | references | TestCoverageTarget | "Login Endpoint Rollup" references "Login Endpoint Coverage" |
|
|
181
|
+
| TargetCoverageSummary | references | CoverageGap | "Login Endpoint Rollup" references "No MFA Cases" |
|
|
182
|
+
| CoverageSummary | aggregates | TargetCoverageSummary | "Login Coverage Summary" aggregates "Login Endpoint Rollup" |
|
|
85
183
|
|
|
86
|
-
|
|
87
|
-
- `motivation.fulfills-requirements` → Motivation Layer (test requirements)
|
|
88
|
-
- `business.covers-process` → Business Layer (process coverage)
|
|
89
|
-
- `api.tests-operation` → API Layer (API endpoint testing)
|
|
90
|
-
- `data.validates-schema` → Data Model Layer (data validation)
|
|
91
|
-
- `ux.tests-view` → UX Layer (UI testing)
|
|
92
|
-
|
|
93
|
-
**Incoming (Other Layers → Testing):**
|
|
184
|
+
---
|
|
94
185
|
|
|
95
|
-
-
|
|
96
|
-
|
|
97
|
-
|
|
186
|
+
## Cross-Layer References
|
|
187
|
+
|
|
188
|
+
### Outgoing References (Testing → Other Layers)
|
|
189
|
+
|
|
190
|
+
A target `covers` the production artifact it names; a sketch `tests` what it exercises. The model
|
|
191
|
+
covers only the application component, UX application or API document it is for. Requirements and
|
|
192
|
+
summaries reach no other layer. Only the model and test case sketches link to motivation.
|
|
193
|
+
|
|
194
|
+
| Source Type | Predicate | Target Type | Example |
|
|
195
|
+
| ------------------ | ---------------- | ---------------------------------- | --------------------------------------------------------------------- |
|
|
196
|
+
| TestCoverageModel | covers | `application.applicationcomponent` | "Login API Coverage Model" covers "Auth Service" |
|
|
197
|
+
| TestCoverageModel | covers | `api.openapidocument` | "Login API Coverage Model" covers "Auth API" |
|
|
198
|
+
| TestCoverageModel | supports | `motivation.goal` | "Login API Coverage Model" supports "Secure Customer Access" |
|
|
199
|
+
| TestCoverageModel | satisfies | `motivation.requirement` | "Login API Coverage Model" satisfies "Auth Must Be Regression Tested" |
|
|
200
|
+
| TestCoverageModel | measures-outcome | `motivation.outcome` | "Login API Coverage Model" measures-outcome "Fewer Auth Incidents" |
|
|
201
|
+
| TestCoverageTarget | covers | `api.operation` | "Login Endpoint Coverage" covers "POST /auth/login" |
|
|
202
|
+
| TestCoverageTarget | covers | `product.feature` | "Password Reset Coverage" covers "Self-Service Password Reset" |
|
|
203
|
+
| TestCoverageTarget | covers | `ux.view` | "Login Screen Coverage" covers "Login View" |
|
|
204
|
+
| TestCoverageTarget | covers | `data-store.storedlogic` | "Order Total Trigger Coverage" covers "recalc_order_total" |
|
|
205
|
+
| TestCaseSketch | tests | `api.operation` | "Valid Login Test Case" tests "POST /auth/login" |
|
|
206
|
+
| TestCaseSketch | tests | `api.response` | "Invalid Password Test" tests "401 Unauthorized" |
|
|
207
|
+
| TestCaseSketch | tests | `business.businessprocess` | "Complete Checkout E2E" tests "Order Checkout" |
|
|
208
|
+
| TestCaseSketch | tests | `ux.view` | "Login Form Renders Errors" tests "Login View" |
|
|
209
|
+
| TestCaseSketch | tests | `navigation.route` | "Deep Link To Profile" tests "User Profile Route" |
|
|
210
|
+
| TestCaseSketch | tests | `security.threat` | "Brute Force Lockout Test" tests "Credential Stuffing" |
|
|
211
|
+
| TestCaseSketch | tests | `data-model.schemadefinition` | "Reject Unknown Order Fields" tests "Order Schema" |
|
|
212
|
+
| TestCaseSketch | satisfies | `motivation.requirement` | "Brute Force Lockout Test" satisfies "Lock After 5 Failures" |
|
|
213
|
+
| TestCaseSketch | accesses | `data-store.collection` | "Order History Test" accesses `orders` |
|
|
214
|
+
| TargetInputField | references | `api.parameter` | "email field" references "email parameter" |
|
|
215
|
+
| TargetInputField | references | `data-model.schemaproperty` | "email field" references "User.email" |
|
|
216
|
+
| EnvironmentFactor | references | `technology.systemsoftware` | "PostgreSQL 16" references "PostgreSQL" |
|
|
217
|
+
|
|
218
|
+
### Incoming References (Other Layers → Testing)
|
|
219
|
+
|
|
220
|
+
None. Testing is a leaf layer: other layers do not hold relationships pointing into it. Record traceability from the testing side (e.g. `TestCaseSketch tests api.operation`).
|
|
98
221
|
|
|
99
222
|
---
|
|
100
223
|
|
|
@@ -114,22 +237,22 @@ Activate when the user:
|
|
|
114
237
|
|
|
115
238
|
```bash
|
|
116
239
|
# Add coverage model
|
|
117
|
-
dr add testing
|
|
240
|
+
dr add testing testcoveragemodel "API Coverage Model"
|
|
118
241
|
|
|
119
242
|
# Add coverage target
|
|
120
|
-
dr add testing
|
|
243
|
+
dr add testing testcoveragetarget "Login API Coverage"
|
|
121
244
|
|
|
122
245
|
# Add test case sketch
|
|
123
|
-
dr add testing
|
|
246
|
+
dr add testing testcasesketch "Valid Login Test"
|
|
124
247
|
|
|
125
248
|
# List coverage models
|
|
126
|
-
dr list testing
|
|
249
|
+
dr list testing --type testcoveragemodel
|
|
127
250
|
|
|
128
251
|
# Validate testing layer
|
|
129
|
-
dr validate --
|
|
252
|
+
dr validate --layers testing
|
|
130
253
|
|
|
131
254
|
# Export coverage report
|
|
132
|
-
dr export --
|
|
255
|
+
dr export markdown --layers testing
|
|
133
256
|
```
|
|
134
257
|
|
|
135
258
|
---
|
|
@@ -137,22 +260,16 @@ dr export --layer testing --format markdown
|
|
|
137
260
|
## Example: Login API Coverage Model
|
|
138
261
|
|
|
139
262
|
```yaml
|
|
140
|
-
id: testing.
|
|
263
|
+
id: testing.testcoveragemodel.login-api
|
|
141
264
|
name: "Login API Coverage Model"
|
|
142
|
-
type:
|
|
265
|
+
type: testcoveragemodel
|
|
143
266
|
properties:
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
- device: [desktop, mobile, tablet]
|
|
151
|
-
motivation:
|
|
152
|
-
supports-goals:
|
|
153
|
-
- motivation.goal.quality-assurance
|
|
154
|
-
fulfills-requirements:
|
|
155
|
-
- motivation.requirement.comprehensive-testing
|
|
267
|
+
version: "1.0"
|
|
268
|
+
description: "ISP coverage model for the Login API"
|
|
269
|
+
```
|
|
270
|
+
|
|
271
|
+
```bash
|
|
272
|
+
dr relationship add testing.testcoveragemodel.login-api application.applicationcomponent.auth-service --predicate covers
|
|
156
273
|
```
|
|
157
274
|
|
|
158
275
|
---
|
|
@@ -160,33 +277,18 @@ properties:
|
|
|
160
277
|
## Example: Login Test Coverage Target
|
|
161
278
|
|
|
162
279
|
```yaml
|
|
163
|
-
id: testing.
|
|
280
|
+
id: testing.testcoveragetarget.login-endpoint
|
|
164
281
|
name: "Login Endpoint Coverage"
|
|
165
|
-
type:
|
|
282
|
+
type: testcoveragetarget
|
|
166
283
|
properties:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
partitions:
|
|
176
|
-
- valid-password: [StrongPass123!]
|
|
177
|
-
- weak-password: [123, abc]
|
|
178
|
-
- missing: [null, empty]
|
|
179
|
-
- too-long: [1000-char-string]
|
|
180
|
-
outcomeCategories:
|
|
181
|
-
- success: 200 OK with auth token
|
|
182
|
-
- invalid-credentials: 401 Unauthorized
|
|
183
|
-
- validation-error: 400 Bad Request
|
|
184
|
-
- rate-limited: 429 Too Many Requests
|
|
185
|
-
testCases:
|
|
186
|
-
- testing.test-case.valid-login
|
|
187
|
-
- testing.test-case.invalid-email
|
|
188
|
-
- testing.test-case.invalid-password
|
|
189
|
-
- testing.test-case.missing-credentials
|
|
284
|
+
targetType: api-endpoint
|
|
285
|
+
description: "POST /auth/login — validates credentials and returns a JWT"
|
|
286
|
+
priority: critical
|
|
287
|
+
```
|
|
288
|
+
|
|
289
|
+
```bash
|
|
290
|
+
dr relationship add testing.testcoveragemodel.login-api testing.testcoveragetarget.login-endpoint --predicate composes
|
|
291
|
+
dr relationship add testing.testcoveragetarget.login-endpoint api.operation.login --predicate covers
|
|
190
292
|
```
|
|
191
293
|
|
|
192
294
|
---
|
|
@@ -194,30 +296,48 @@ properties:
|
|
|
194
296
|
## Example: Test Case Sketch
|
|
195
297
|
|
|
196
298
|
```yaml
|
|
197
|
-
id: testing.
|
|
299
|
+
id: testing.testcasesketch.valid-login
|
|
198
300
|
name: "Valid Login Test Case"
|
|
199
|
-
type:
|
|
301
|
+
type: testcasesketch
|
|
200
302
|
properties:
|
|
201
|
-
|
|
202
|
-
description: "
|
|
203
|
-
|
|
204
|
-
email: valid-email
|
|
205
|
-
password: valid-password
|
|
206
|
-
contextSelection:
|
|
207
|
-
browser: Chrome
|
|
208
|
-
network: high-speed
|
|
209
|
-
device: desktop
|
|
210
|
-
expectedOutcome: success
|
|
211
|
-
expectedResponse:
|
|
212
|
-
status: 200
|
|
213
|
-
body:
|
|
214
|
-
token: JWT token present
|
|
215
|
-
user: User object with id and email
|
|
216
|
-
motivation:
|
|
217
|
-
supports-goals:
|
|
218
|
-
- motivation.goal.secure-authentication
|
|
303
|
+
status: draft
|
|
304
|
+
description: "Successful login with valid email and password returns 200 with JWT"
|
|
305
|
+
implementationFormat: automated
|
|
219
306
|
```
|
|
220
307
|
|
|
308
|
+
```bash
|
|
309
|
+
dr relationship add testing.testcoveragetarget.login-endpoint testing.testcasesketch.valid-login --predicate composes
|
|
310
|
+
dr relationship add testing.testcasesketch.valid-login testing.coveragerequirement.pairwise-credentials --predicate validates
|
|
311
|
+
dr relationship add testing.testcasesketch.valid-login api.operation.login --predicate tests
|
|
312
|
+
```
|
|
313
|
+
|
|
314
|
+
---
|
|
315
|
+
|
|
316
|
+
## Coverage Completeness Checklist
|
|
317
|
+
|
|
318
|
+
Before declaring testing layer extraction complete, verify each type was considered:
|
|
319
|
+
|
|
320
|
+
- [ ] **testcoveragemodel** — Root coverage model exists for each major system or component under test
|
|
321
|
+
- [ ] **testcoveragetarget** — All APIs, features, workflows, and data paths being tested are captured
|
|
322
|
+
- [ ] **inputspacepartition** — Input fields on each target have their value space partitioned
|
|
323
|
+
- [ ] **partitionvalue** — Each partition has concrete representative values and boundary cases
|
|
324
|
+
- [ ] **partitiondependency** — Cross-field constraints between partitions are documented
|
|
325
|
+
- [ ] **inputpartitionselection** — Coverage selections specify which partition values are exercised
|
|
326
|
+
- [ ] **inputselection** — Concrete resolved inputs are selected for each test case sketch
|
|
327
|
+
- [ ] **coveragerequirement** — Coverage criteria (each-choice, pairwise, etc.) are specified
|
|
328
|
+
- [ ] **testcasesketch** — High-level test case sketches cover the required combinations
|
|
329
|
+
- [ ] **outcomecategory** — Expected outcome categories (success, error, edge) are defined per target
|
|
330
|
+
- [ ] **contextvariation** — Test strategy variation types (functional, load, security, regression, smoke, exploratory) are captured
|
|
331
|
+
- [ ] **environmentfactor** — Specific factor values for each context variation are recorded
|
|
332
|
+
- [ ] **targetinputfield** — Input fields on coverage targets are explicitly mapped to partitions
|
|
333
|
+
- [ ] **coveragegap** — Known gaps in coverage are documented with severity and affected requirements
|
|
334
|
+
- [ ] **coverageexclusion** — Deliberate out-of-scope decisions are recorded with rationale and approver
|
|
335
|
+
- [ ] **coveragesummary** — An overall coverage summary exists if aggregated metrics are needed
|
|
336
|
+
- [ ] **targetcoveragesummary** — Per-target coverage rollups exist for targets with tracked implementation
|
|
337
|
+
|
|
338
|
+
If any type has ZERO elements, explicitly decide:
|
|
339
|
+
"This type doesn't apply to this codebase" with reasoning.
|
|
340
|
+
|
|
221
341
|
---
|
|
222
342
|
|
|
223
343
|
## Pitfalls to Avoid
|
|
@@ -0,0 +1,119 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: dr_change_planning
|
|
3
|
+
description: Plan a code change as a staged DR changeset through the dr MCP server, pressure-test it against the model, build from its brief, and verify the build against it. Use when asked to plan, design or scope a change in a project with a DR model.
|
|
4
|
+
triggers:
|
|
5
|
+
[
|
|
6
|
+
"plan this change",
|
|
7
|
+
"plan the change",
|
|
8
|
+
"plan a change",
|
|
9
|
+
"plan this feature",
|
|
10
|
+
"how should we build",
|
|
11
|
+
"scope this change",
|
|
12
|
+
"design this change",
|
|
13
|
+
"draft a changeset",
|
|
14
|
+
"changeset brief"
|
|
15
|
+
]
|
|
16
|
+
---
|
|
17
|
+
|
|
18
|
+
# Plan a change as a DR changeset
|
|
19
|
+
|
|
20
|
+
In a project with a DR model (`documentation-robotics/model/`), a plan is a staged changeset, not a
|
|
21
|
+
prose document. The changeset says what the change will build. The model says what is built. The
|
|
22
|
+
graph checks the plan for you. The full design is `docs/CHANGE_MODELING.md` in the DR repository.
|
|
23
|
+
|
|
24
|
+
## Before you start
|
|
25
|
+
|
|
26
|
+
- You need the `dr` MCP server (`dr mcp`, registered in Copilot's MCP configuration with the
|
|
27
|
+
`DR_MCP_API_KEY` it prints on start; see `integrations/claude_code/docs/MCP_SERVER.md`). The
|
|
28
|
+
tool names below are the server's own; Copilot may show them with a server prefix. If the tools
|
|
29
|
+
are missing, tell the user to register the server and stop. Don't fall back to prose.
|
|
30
|
+
- Staging writes files under `documentation-robotics/changesets/`.
|
|
31
|
+
- You never commit, apply or discard the plan. Those are the human's steps, and commit and apply are
|
|
32
|
+
not MCP tools.
|
|
33
|
+
|
|
34
|
+
## The loop
|
|
35
|
+
|
|
36
|
+
1. **Frame.** `changeset_list` first: if a changeset for this change exists, `changeset_activate` it
|
|
37
|
+
and continue. Otherwise `changeset_create` with a short kebab-case `name` and a `description`
|
|
38
|
+
that states the intent: what the change is for and what done looks like. Find the goal,
|
|
39
|
+
requirement or feature it serves (`model_search` in motivation, business, product). If there is
|
|
40
|
+
none, stage one, or ask the human what the change is for.
|
|
41
|
+
2. **Orient.** `model_search` / `model_show` / `model_trace` for the elements the change touches.
|
|
42
|
+
Inside a changeset these read the plan as it stands (each result carries a change status).
|
|
43
|
+
`spec_type` for a type you will add: its required attributes, enums, and the relationships the
|
|
44
|
+
catalog allows to and from it. Within reason, every connection the spec models for a type is
|
|
45
|
+
expected, though not every one will exist: treat the catalog's list as the checklist for each
|
|
46
|
+
element the change adds or alters.
|
|
47
|
+
3. **Draft.** Stage the change in every layer it reaches: `model_add`, `model_update`,
|
|
48
|
+
`model_delete`, `relationship_add`, `relationship_delete`. Relationships may join elements that
|
|
49
|
+
exist only in the changeset. Pass `sourceFile`, `sourceProvenance` (`manual` for a planned location) and `sourceSymbol` when you know where the code goes.
|
|
50
|
+
Change one attribute with `model_update` `attributePatch`, not by repeating every attribute. Fix a
|
|
51
|
+
mistake with `changeset_unstage` (one element, one relationship, or `source` or `target` alone for
|
|
52
|
+
every staged connection at that end), not by staging the reverse.
|
|
53
|
+
4. **Pressure-test.** `changeset_brief`, then `changeset_impact` for anything the brief questions.
|
|
54
|
+
Steps 3 and 4 are a loop: a plan comes into focus over many passes. Each pass, stage a little,
|
|
55
|
+
then:
|
|
56
|
+
- Pass the last brief's `revisionToken` as `since`; read the `delta` first. It covers unstages
|
|
57
|
+
too (`unstaged`). If it warns (a hand-edited `changes.yaml`, or a reach back past the last 200
|
|
58
|
+
unstages), read the brief itself.
|
|
59
|
+
- Work through `unwired` for each element you added or changed. Where `existingCount` is above
|
|
60
|
+
0, link an existing element; otherwise decide whether the change needs a new one. Use
|
|
61
|
+
`unwiredLayers` to see one layer in full, and `unwired: "none"` when you only need the gaps.
|
|
62
|
+
- Walk `changeset_impact` with `from` set to what you just added, not the whole changeset.
|
|
63
|
+
- Read the plan with `changeset_show` `detail: "summary"`, not the full snapshots.
|
|
64
|
+
|
|
65
|
+
Read the brief's gaps:
|
|
66
|
+
- `peer-pattern`: most elements like this one have a connection it lacks.
|
|
67
|
+
- `unconnected`: an added element with no relationships.
|
|
68
|
+
- Intent: a change that reaches no motivation, business or product element.
|
|
69
|
+
- Layers not reached: a new API operation with nothing in data model, testing or APM is a small
|
|
70
|
+
change or an unfinished plan. Decide which.
|
|
71
|
+
- Connections the brief can't flag: for each changed element, compare what `spec_type` says its
|
|
72
|
+
type can connect to with what the plan has. Stage the ones the change needs; leave the ones it
|
|
73
|
+
doesn't, but don't skip the comparison.
|
|
74
|
+
|
|
75
|
+
For each gap, either stage what is missing or accept it. Record an accepted gap where the human
|
|
76
|
+
will see it: an `annotation_create` on the element, or a line in the description
|
|
77
|
+
(`changeset_update`). `model_validate` should pass on the projected model.
|
|
78
|
+
|
|
79
|
+
5. **Review.** Stop and show the human the brief: changes by layer, gaps fixed and gaps accepted,
|
|
80
|
+
impact, code touched. Ask them to look at it in `dr visualize` and comment on elements there.
|
|
81
|
+
Read their comments with `annotation_list`, answer with `annotation_reply`, revise, and run the
|
|
82
|
+
brief again. Don't start building until they agree.
|
|
83
|
+
6. **Build.** Build from the brief. Its "code touched" list is what you will edit or must not
|
|
84
|
+
break. Keep the changeset staged and commit only the plan's own directory
|
|
85
|
+
(`documentation-robotics/changesets/<id>/`, with the id `changeset_show` reports) on the branch
|
|
86
|
+
with the code. Don't add the whole `changesets/` directory: the `.active` marker and any
|
|
87
|
+
`<name>-as-built` changeset stay out of the commit. If the build has to differ from the plan,
|
|
88
|
+
change the plan to match and say why.
|
|
89
|
+
7. **Verify.** Write down what was built, separately from the plan: `changeset_create`
|
|
90
|
+
`<name>-as-built`, stage what the diff actually contains (read the code, not the plan), then
|
|
91
|
+
`changeset_verify` with `planned: <name>` and `against: {"changesetId": "<name>-as-built"}`.
|
|
92
|
+
Fix each missing, unplanned or divergent item in the code or in the plan. Look for missing
|
|
93
|
+
connections in the as-built too: for each element, compare what the spec models for its type
|
|
94
|
+
with what was staged, and look for evidence of each absent one in the code (and the analyzer's
|
|
95
|
+
graph, `dr analyzer callers` / `callees`, when it is indexed). Confirm or reject
|
|
96
|
+
every `probable` match. `changeset_activate` the plan again afterwards. Tell the human the
|
|
97
|
+
as-built changeset can be discarded; discard it (`changeset_discard`, `confirm: true`) only when
|
|
98
|
+
they agree. Until then it stays out of git: when you commit fixes, add only the plan's
|
|
99
|
+
directory again. If the as-built changes are committed to the model instead of kept as a changeset,
|
|
100
|
+
`against: {"base": true}` compares the plan with the model.
|
|
101
|
+
8. **Land.** Hand over: the PR carries the code and the staged changeset. The human commits it,
|
|
102
|
+
in the viewer or with `dr changeset commit`.
|
|
103
|
+
|
|
104
|
+
## Tolerance
|
|
105
|
+
|
|
106
|
+
Tools report odd input and carry on instead of failing. Read the report each time:
|
|
107
|
+
|
|
108
|
+
- `relationship_add` reports a duplicate, or restores a relationship the plan had deleted.
|
|
109
|
+
- `changeset_unstage` reports `not-staged` when there was nothing to drop.
|
|
110
|
+
- In a changeset-only session (DrBot with `--with-changesets`), a write with no active changeset is
|
|
111
|
+
refused. Call `changeset_create` or `changeset_activate` and retry.
|
|
112
|
+
- DrBot cannot write annotations (`annotation_create` and `annotation_reply` are not among its
|
|
113
|
+
tools). There, record accepted gaps in the description with `changeset_update` and answer the
|
|
114
|
+
human's comments in the chat.
|
|
115
|
+
|
|
116
|
+
## Keep it short
|
|
117
|
+
|
|
118
|
+
Model at the grain of the change. One function, one operation and one test sketch beat a
|
|
119
|
+
speculative tree. Add detail as the build firms up.
|
|
@@ -134,6 +134,9 @@ For each addition, check:
|
|
|
134
134
|
- [ ] Layer-appropriate properties used
|
|
135
135
|
- [ ] References point to existing elements
|
|
136
136
|
- [ ] Status is valid for the element type
|
|
137
|
+
- [ ] Connections the spec models for its type (`dr schema relationship <layer>.<type>`) are present
|
|
138
|
+
where the change needs them; within reason every modeled connection is expected, though not
|
|
139
|
+
every one will exist
|
|
137
140
|
|
|
138
141
|
For each modification, check:
|
|
139
142
|
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"version": 3,
|
|
3
3
|
"sources": ["../../src/integrations/hash-utils.ts"],
|
|
4
|
-
"sourcesContent": ["/**\n * File Hashing Utilities\n *\n * Provides SHA256-based file hashing for change detection in integration files.\n * Uses streaming for memory efficiency when handling large files.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { createReadStream } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * Compute SHA256 hash of a file, truncated to 8 characters\n *\n * Uses streaming to efficiently handle files of any size without loading\n * them entirely into memory. Matches Python implementation using the same\n * truncation length (8 characters) for consistency.\n *\n * @param filePath - Absolute path to file to hash\n * @returns Promise resolving to 8-character hex hash\n * @throws Error if file cannot be read\n *\n * @example\n * const hash = await computeFileHash('/path/to/file.ts');\n * // Returns: 'a1b2c3d4'\n */\nexport async function computeFileHash(filePath: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash(\"sha256\");\n const stream = createReadStream(filePath);\n\n stream.on(\"data\", (data: Buffer) => {\n hash.update(data);\n });\n\n stream.on(\"end\", () => {\n const fullHash = hash.digest(\"hex\");\n resolve(fullHash.substring(0, 8));\n });\n\n stream.on(\"error\", (error: Error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Compute hashes for all files in a directory recursively\n *\n * Scans the directory tree and computes hashes for all files, optionally\n * filtering by a filename prefix. Returns a map of relative paths to hashes,\n * enabling efficient comparison of installed vs. source files.\n *\n * @param baseDir - Base directory to scan\n * @param prefix - Optional prefix filter (only include files matching prefix)\n * @returns Promise resolving to Map of relative paths to 8-character hashes\n * @throws Error if directory cannot be read\n *\n * @example\n * const hashes = await computeDirectoryHashes('/path/to/dir', 'dr-');\n * // Returns: Map { 'dr-file1.md' => 'a1b2c3d4', 'subdir/dr-file2.md' => 'e5f6g7h8' }\n */\nexport async function computeDirectoryHashes(\n baseDir: string,\n prefix?: string\n): Promise<Map<string, string>> {\n const hashes = new Map<string, string>();\n\n // Return empty map if directory doesn't exist\n if (!existsSync(baseDir)) {\n return hashes;\n }\n\n async function scanDir(dir: string, relativePrefix: string = \"\"): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n const relativePath = relativePrefix ? join(relativePrefix, entry.name) : entry.name;\n\n if (entry.isDirectory()) {\n // Recursively scan subdirectories\n await scanDir(fullPath, relativePath);\n } else if (entry.isFile()) {\n // Check prefix filter\n if (prefix && !entry.name.startsWith(prefix)) {\n continue;\n }\n\n // Compute hash for file\n try {\n const hash = await computeFileHash(fullPath);\n hashes.set(relativePath, hash);\n } catch (error) {\n // Log error but continue processing other files\n console.error(`Failed to hash file ${fullPath}:`, error);\n }\n }\n }\n } catch (error) {\n // Re-throw directory read errors\n throw error;\n }\n }\n\n await scanDir(baseDir);\n return hashes;\n}\n"],
|
|
5
|
-
"mappings": "AAOA,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAiB3B,eAAsB,gBAAgB,UAAgB;AACpD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAU;AACrC,UAAM,OAAO,WAAW,QAAQ,GAC1B,SAAS,iBAAiB,QAAQ;AAExC,WAAO,GAAG,QAAQ,CAAC,
|
|
4
|
+
"sourcesContent": ["/**\n * File Hashing Utilities\n *\n * Provides SHA256-based file hashing for change detection in integration files.\n * Uses streaming for memory efficiency when handling large files.\n */\n\nimport { createHash } from \"node:crypto\";\nimport { createReadStream } from \"node:fs\";\nimport { readdir } from \"node:fs/promises\";\nimport { join } from \"node:path\";\nimport { existsSync } from \"node:fs\";\n\n/**\n * Compute SHA256 hash of a file, truncated to 8 characters\n *\n * Uses streaming to efficiently handle files of any size without loading\n * them entirely into memory. Matches Python implementation using the same\n * truncation length (8 characters) for consistency.\n *\n * @param filePath - Absolute path to file to hash\n * @returns Promise resolving to 8-character hex hash\n * @throws Error if file cannot be read\n *\n * @example\n * const hash = await computeFileHash('/path/to/file.ts');\n * // Returns: 'a1b2c3d4'\n */\nexport async function computeFileHash(filePath: string): Promise<string> {\n return new Promise((resolve, reject) => {\n const hash = createHash(\"sha256\");\n const stream = createReadStream(filePath);\n\n stream.on(\"data\", (data: string | Buffer) => {\n hash.update(data);\n });\n\n stream.on(\"end\", () => {\n const fullHash = hash.digest(\"hex\");\n resolve(fullHash.substring(0, 8));\n });\n\n stream.on(\"error\", (error: Error) => {\n reject(error);\n });\n });\n}\n\n/**\n * Compute hashes for all files in a directory recursively\n *\n * Scans the directory tree and computes hashes for all files, optionally\n * filtering by a filename prefix. Returns a map of relative paths to hashes,\n * enabling efficient comparison of installed vs. source files.\n *\n * @param baseDir - Base directory to scan\n * @param prefix - Optional prefix filter (only include files matching prefix)\n * @returns Promise resolving to Map of relative paths to 8-character hashes\n * @throws Error if directory cannot be read\n *\n * @example\n * const hashes = await computeDirectoryHashes('/path/to/dir', 'dr-');\n * // Returns: Map { 'dr-file1.md' => 'a1b2c3d4', 'subdir/dr-file2.md' => 'e5f6g7h8' }\n */\nexport async function computeDirectoryHashes(\n baseDir: string,\n prefix?: string\n): Promise<Map<string, string>> {\n const hashes = new Map<string, string>();\n\n // Return empty map if directory doesn't exist\n if (!existsSync(baseDir)) {\n return hashes;\n }\n\n async function scanDir(dir: string, relativePrefix: string = \"\"): Promise<void> {\n try {\n const entries = await readdir(dir, { withFileTypes: true });\n\n for (const entry of entries) {\n const fullPath = join(dir, entry.name);\n const relativePath = relativePrefix ? join(relativePrefix, entry.name) : entry.name;\n\n if (entry.isDirectory()) {\n // Recursively scan subdirectories\n await scanDir(fullPath, relativePath);\n } else if (entry.isFile()) {\n // Check prefix filter\n if (prefix && !entry.name.startsWith(prefix)) {\n continue;\n }\n\n // Compute hash for file\n try {\n const hash = await computeFileHash(fullPath);\n hashes.set(relativePath, hash);\n } catch (error) {\n // Log error but continue processing other files\n console.error(`Failed to hash file ${fullPath}:`, error);\n }\n }\n }\n } catch (error) {\n // Re-throw directory read errors\n throw error;\n }\n }\n\n await scanDir(baseDir);\n return hashes;\n}\n"],
|
|
5
|
+
"mappings": "AAOA,SAAS,kBAAkB;AAC3B,SAAS,wBAAwB;AACjC,SAAS,eAAe;AACxB,SAAS,YAAY;AACrB,SAAS,kBAAkB;AAiB3B,eAAsB,gBAAgB,UAAgB;AACpD,SAAO,IAAI,QAAQ,CAAC,SAAS,WAAU;AACrC,UAAM,OAAO,WAAW,QAAQ,GAC1B,SAAS,iBAAiB,QAAQ;AAExC,WAAO,GAAG,QAAQ,CAAC,SAAyB;AAC1C,WAAK,OAAO,IAAI;IAClB,CAAC,GAED,OAAO,GAAG,OAAO,MAAK;AACpB,YAAM,WAAW,KAAK,OAAO,KAAK;AAClC,cAAQ,SAAS,UAAU,GAAG,CAAC,CAAC;IAClC,CAAC,GAED,OAAO,GAAG,SAAS,CAAC,UAAgB;AAClC,aAAO,KAAK;IACd,CAAC;EACH,CAAC;AACH;AAkBA,eAAsB,uBACpB,SACA,QAAe;AAEf,QAAM,SAAS,oBAAI,IAAG;AAGtB,MAAI,CAAC,WAAW,OAAO;AACrB,WAAO;AAGT,iBAAe,QAAQ,KAAa,iBAAyB,IAAE;AAC7D,QAAI;AACF,YAAM,UAAU,MAAM,QAAQ,KAAK,EAAE,eAAe,GAAI,CAAE;AAE1D,iBAAW,SAAS,SAAS;AAC3B,cAAM,WAAW,KAAK,KAAK,MAAM,IAAI,GAC/B,eAAe,iBAAiB,KAAK,gBAAgB,MAAM,IAAI,IAAI,MAAM;AAE/E,YAAI,MAAM,YAAW;AAEnB,gBAAM,QAAQ,UAAU,YAAY;iBAC3B,MAAM,OAAM,GAAI;AAEzB,cAAI,UAAU,CAAC,MAAM,KAAK,WAAW,MAAM;AACzC;AAIF,cAAI;AACF,kBAAM,OAAO,MAAM,gBAAgB,QAAQ;AAC3C,mBAAO,IAAI,cAAc,IAAI;UAC/B,SAAS,OAAO;AAEd,oBAAQ,MAAM,uBAAuB,QAAQ,KAAK,KAAK;UACzD;QACF;MACF;IACF,SAAS,OAAO;AAEd,YAAM;IACR;EACF;AAEA,eAAM,QAAQ,OAAO,GACd;AACT;",
|
|
6
6
|
"names": []
|
|
7
7
|
}
|
|
@@ -29,6 +29,16 @@ class ApiKeyManager {
|
|
|
29
29
|
throw new Error(`API key file at ${path} is empty or corrupt. Delete it and re-run to generate a new key, or restore a valid key file.`);
|
|
30
30
|
return key;
|
|
31
31
|
}
|
|
32
|
+
/** The key stored at `path`, or null when there is no usable key file there. */
|
|
33
|
+
async loadIfPresent(path) {
|
|
34
|
+
if (!existsSync(path))
|
|
35
|
+
return null;
|
|
36
|
+
try {
|
|
37
|
+
return await this.load(path);
|
|
38
|
+
} catch {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
41
|
+
}
|
|
32
42
|
/** Write a key to disk with owner-only permissions. */
|
|
33
43
|
async store(path, key) {
|
|
34
44
|
await mkdir(dirname(path), { recursive: !0 }), await writeFile(path, `${key}
|
|
@@ -68,7 +78,10 @@ class ApiKeyManager {
|
|
|
68
78
|
const config = await this.loadConfigOrThrow(), existingPath = config.mcp?.api_key_path;
|
|
69
79
|
if (existingPath && existsSync(existingPath))
|
|
70
80
|
return { key: await this.load(existingPath), path: existingPath, isNew: !1 };
|
|
71
|
-
const chosenPath = promptForPath ? await promptForPath(this.defaultKeyPath) : this.defaultKeyPath,
|
|
81
|
+
const chosenPath = promptForPath ? await promptForPath(this.defaultKeyPath) : this.defaultKeyPath, adopted = await this.loadIfPresent(chosenPath);
|
|
82
|
+
if (adopted)
|
|
83
|
+
return await saveDRConfig({ ...config, mcp: { ...config.mcp, api_key_path: chosenPath } }), { key: adopted, path: chosenPath, isNew: !1 };
|
|
84
|
+
const key = this.generate();
|
|
72
85
|
return await this.store(chosenPath, key), await saveDRConfig({
|
|
73
86
|
...config,
|
|
74
87
|
mcp: { ...config.mcp, api_key_path: chosenPath }
|