controlm-chain-admin 0.1.0__tar.gz → 0.1.2__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 (272) hide show
  1. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/bootstrap/SKILL.md +40 -13
  2. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/BACKLOG.md +7 -0
  3. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CHANGELOG.md +49 -1
  4. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/DESIGN.md +36 -1
  5. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/PKG-INFO +8 -5
  6. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/README.md +7 -4
  7. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/__init__.py +1 -1
  8. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/cli.py +14 -4
  9. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/fixes.py +59 -1
  10. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/guide.py +4 -1
  11. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/intake.py +170 -20
  12. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/refactor.py +17 -7
  13. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/validate.py +64 -4
  14. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/intake.py +1 -1
  15. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/schemas.py +8 -2
  16. controlm_chain_admin-0.1.2/src/controlm_chain_admin/web/static/assets/index-M3NaeRfY.js +28 -0
  17. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/index.html +1 -1
  18. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_receive.py +41 -0
  19. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake_envs.py +1 -1
  20. controlm_chain_admin-0.1.2/tests/unit/test_intake_values_only.py +196 -0
  21. controlm_chain_admin-0.1.2/tests/unit/test_validate_similar.py +222 -0
  22. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/openapi.snapshot.json +13 -1
  23. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_intake.py +19 -0
  24. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/scripts/dump_fixtures.py +21 -0
  25. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/intake/ImportPage.test.tsx +64 -1
  26. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/intake/ImportPage.tsx +40 -4
  27. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/types.gen.ts +13 -1
  28. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake-full.json +1 -0
  29. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake-groups.json +1 -0
  30. controlm_chain_admin-0.1.2/web/src/test/fixtures/intake-values.json +253 -0
  31. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/intake.json +1 -0
  32. controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/index-DYc-B93N.js +0 -28
  33. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/ATTRIBUTION.md +0 -0
  34. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/fastapi-api/SKILL.md +0 -0
  35. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/oss-repo-standards/SKILL.md +0 -0
  36. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/oss-repo-standards/check_repo.sh +0 -0
  37. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/python-core-tdd/SKILL.md +0 -0
  38. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/react-frontend/SKILL.md +0 -0
  39. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/verify/SKILL.md +0 -0
  40. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.claude/skills/verify/verify.sh +0 -0
  41. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.editorconfig +0 -0
  42. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/FUNDING.yml +0 -0
  43. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  44. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/ISSUE_TEMPLATE/feature_request.md +0 -0
  45. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/PULL_REQUEST_TEMPLATE.md +0 -0
  46. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/workflows/ci.yml +0 -0
  47. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.github/workflows/release.yml +0 -0
  48. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.gitignore +0 -0
  49. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/.pre-commit-config.yaml +0 -0
  50. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CLAUDE.md +0 -0
  51. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CODE_OF_CONDUCT.md +0 -0
  52. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/CONTRIBUTING.md +0 -0
  53. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/LICENSE +0 -0
  54. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/SECURITY.md +0 -0
  55. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/pyproject.toml +0 -0
  56. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/__main__.py +0 -0
  57. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/__init__.py +0 -0
  58. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/attribute_groups.py +0 -0
  59. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/commit.py +0 -0
  60. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/compare.py +0 -0
  61. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/constants.py +0 -0
  62. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/errors.py +0 -0
  63. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/graph.py +0 -0
  64. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/history.py +0 -0
  65. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/inspect.py +0 -0
  66. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/jsonmodel.py +0 -0
  67. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/letter.py +0 -0
  68. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/links.py +0 -0
  69. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/membership.py +0 -0
  70. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/model.py +0 -0
  71. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/ops.py +0 -0
  72. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/overrides.py +0 -0
  73. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/package.py +0 -0
  74. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/relations.py +0 -0
  75. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/repo.py +0 -0
  76. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/selection.py +0 -0
  77. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/take.py +0 -0
  78. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/workspace.py +0 -0
  79. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/core/xmlio.py +0 -0
  80. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/py.typed +0 -0
  81. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/__init__.py +0 -0
  82. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/app.py +0 -0
  83. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/deps.py +0 -0
  84. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/errors.py +0 -0
  85. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/__init__.py +0 -0
  86. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/changes.py +0 -0
  87. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/environments.py +0 -0
  88. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/folders.py +0 -0
  89. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/history.py +0 -0
  90. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/jobs.py +0 -0
  91. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/package.py +0 -0
  92. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/repo.py +0 -0
  93. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/table.py +0 -0
  94. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/validate.py +0 -0
  95. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/routers/workspace.py +0 -0
  96. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/serve.py +0 -0
  97. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/settings.py +0 -0
  98. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/graph-CA0VXlpl.js +0 -0
  99. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/index-BA0sPihz.css +0 -0
  100. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/src/controlm_chain_admin/web/static/assets/react-DB7G9u3x.js +0 -0
  101. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/__init__.py +0 -0
  102. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/conftest.py +0 -0
  103. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/__init__.py +0 -0
  104. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_compare_real.py +0 -0
  105. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_constants_real.py +0 -0
  106. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_dedupe_real.py +0 -0
  107. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_environments.py +0 -0
  108. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_export_file.py +0 -0
  109. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_fixes_real.py +0 -0
  110. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_import_export.py +0 -0
  111. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_package_real.py +0 -0
  112. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_refactor_real.py +0 -0
  113. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_roundtrip.py +0 -0
  114. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_table_real.py +0 -0
  115. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/realdata/test_validate_real.py +0 -0
  116. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/__init__.py +0 -0
  117. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/anomalies.py +0 -0
  118. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/envexports.py +0 -0
  119. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/gitrepo.py +0 -0
  120. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/rawxml.py +0 -0
  121. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/support/samples.py +0 -0
  122. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/__init__.py +0 -0
  123. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_attribute_groups.py +0 -0
  124. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli.py +0 -0
  125. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_commands.py +0 -0
  126. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_commit.py +0 -0
  127. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_compare.py +0 -0
  128. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_distinct_filter.py +0 -0
  129. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_env.py +0 -0
  130. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_inspect.py +0 -0
  131. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_package.py +0 -0
  132. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_refactor.py +0 -0
  133. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_release.py +0 -0
  134. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_resolve.py +0 -0
  135. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_cli_serve.py +0 -0
  136. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_commit.py +0 -0
  137. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_compare.py +0 -0
  138. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants.py +0 -0
  139. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants_ops.py +0 -0
  140. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_constants_overview.py +0 -0
  141. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_dedupe.py +0 -0
  142. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_edit_ops.py +0 -0
  143. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_env_rule.py +0 -0
  144. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_fixes.py +0 -0
  145. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_graph.py +0 -0
  146. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_history.py +0 -0
  147. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_inspect.py +0 -0
  148. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake.py +0 -0
  149. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_intake_groups.py +0 -0
  150. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_jsonmodel.py +0 -0
  151. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_letter.py +0 -0
  152. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_links.py +0 -0
  153. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_links_truth.py +0 -0
  154. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_main_module.py +0 -0
  155. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_membership.py +0 -0
  156. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_overrides.py +0 -0
  157. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_package.py +0 -0
  158. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_packaging.py +0 -0
  159. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_rawxml.py +0 -0
  160. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_receive.py +0 -0
  161. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_refactor.py +0 -0
  162. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_relations.py +0 -0
  163. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_repo_ops.py +0 -0
  164. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_selection.py +0 -0
  165. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_take.py +0 -0
  166. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_validate.py +0 -0
  167. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_validate_names.py +0 -0
  168. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_workspace.py +0 -0
  169. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/unit/test_xmlio.py +0 -0
  170. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/__init__.py +0 -0
  171. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/conftest.py +0 -0
  172. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api.py +0 -0
  173. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_commit.py +0 -0
  174. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_compare.py +0 -0
  175. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_edit.py +0 -0
  176. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_environments.py +0 -0
  177. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_package.py +0 -0
  178. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_take.py +0 -0
  179. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_api_workspace.py +0 -0
  180. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_openapi.py +0 -0
  181. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/tests/web/test_static_ui.py +0 -0
  182. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/.prettierrc +0 -0
  183. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/eslint.config.js +0 -0
  184. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/index.html +0 -0
  185. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/package-lock.json +0 -0
  186. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/package.json +0 -0
  187. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/app/App.tsx +0 -0
  188. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/DiffView.tsx +0 -0
  189. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/EffectsTable.tsx +0 -0
  190. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/components/ui.tsx +0 -0
  191. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/chain-table/ChainTablePage.test.tsx +0 -0
  192. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/chain-table/ChainTablePage.tsx +0 -0
  193. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/ChangeProvider.test.tsx +0 -0
  194. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/ChangeProvider.tsx +0 -0
  195. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/TokenForm.tsx +0 -0
  196. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/changes/changes-context.ts +0 -0
  197. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitButton.test.tsx +0 -0
  198. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitButton.tsx +0 -0
  199. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/CommitDialog.tsx +0 -0
  200. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/commit/branch-name.ts +0 -0
  201. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/CommitPicker.tsx +0 -0
  202. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/ComparePage.test.tsx +0 -0
  203. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/ComparePage.tsx +0 -0
  204. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/CopyMarkdownButton.tsx +0 -0
  205. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/PackagePanel.tsx +0 -0
  206. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/TakeBar.tsx +0 -0
  207. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/refs.ts +0 -0
  208. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/compare/take-context.ts +0 -0
  209. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/AttributeForm.tsx +0 -0
  210. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/InlineAction.tsx +0 -0
  211. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RawJsonEditor.tsx +0 -0
  212. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RelationsEditor.test.tsx +0 -0
  213. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/RelationsEditor.tsx +0 -0
  214. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/VariablesEditor.tsx +0 -0
  215. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/editors.test.tsx +0 -0
  216. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/editing/labels.ts +0 -0
  217. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/environments/EnvironmentsPage.test.tsx +0 -0
  218. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/environments/EnvironmentsPage.tsx +0 -0
  219. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/ExceptionsPanel.test.tsx +0 -0
  220. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/ExceptionsPanel.tsx +0 -0
  221. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/exceptions/exception-count.ts +0 -0
  222. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderGraph.tsx +0 -0
  223. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderPage.test.tsx +0 -0
  224. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/FolderPage.tsx +0 -0
  225. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/flow.test.ts +0 -0
  226. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/folder-graph/flow.ts +0 -0
  227. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/job-detail/JobPage.test.tsx +0 -0
  228. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/job-detail/JobPage.tsx +0 -0
  229. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/repo/RepoBadge.test.tsx +0 -0
  230. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/repo/RepoBadge.tsx +0 -0
  231. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/FixOptions.tsx +0 -0
  232. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ResolvePanel.tsx +0 -0
  233. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ValidatePage.test.tsx +0 -0
  234. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/validate/ValidatePage.tsx +0 -0
  235. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/BranchButton.test.tsx +0 -0
  236. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/BranchButton.tsx +0 -0
  237. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/ProtectedBanner.tsx +0 -0
  238. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/ReadOnlyNotice.tsx +0 -0
  239. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/WorkspaceDialog.tsx +0 -0
  240. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/plural.ts +0 -0
  241. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/features/workspace/use-workspace-action.ts +0 -0
  242. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/index.css +0 -0
  243. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/client.ts +0 -0
  244. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/queries.ts +0 -0
  245. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/session.test.ts +0 -0
  246. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/api/session.ts +0 -0
  247. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/layout.ts +0 -0
  248. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/utils.test.ts +0 -0
  249. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/lib/utils.ts +0 -0
  250. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/main.tsx +0 -0
  251. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/compare-selected.json +0 -0
  252. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/compare.json +0 -0
  253. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/environments.json +0 -0
  254. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folder-P77_CHAIN.json +0 -0
  255. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folder-P77_INGEST_DAILY.json +0 -0
  256. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/folders.json +0 -0
  257. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/git.json +0 -0
  258. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/job-P77_CHAIN-P77_B.json +0 -0
  259. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/job-P77_INGEST_DAILY-P77_INGEST_LOAD.json +0 -0
  260. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/package.json +0 -0
  261. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/pending.json +0 -0
  262. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/preview.json +0 -0
  263. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/repo.json +0 -0
  264. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/table.json +0 -0
  265. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/validate.json +0 -0
  266. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/workspace-main.json +0 -0
  267. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/fixtures/workspace.json +0 -0
  268. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/render.tsx +0 -0
  269. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/server.ts +0 -0
  270. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/src/test/setup.ts +0 -0
  271. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/tsconfig.json +0 -0
  272. {controlm_chain_admin-0.1.0 → controlm_chain_admin-0.1.2}/web/vite.config.ts +0 -0
@@ -68,25 +68,32 @@ git add -A && git commit -m "chore: production baseline"
68
68
  This first commit is the only one made on `main` by hand: from now on `main` mirrors
69
69
  production and every change goes through a release.
70
70
 
71
- ### 3. The other environments, one release
71
+ ### 3. The values of the other environments, one release
72
+
73
+ The full exports of the other environments also carry the releases that are installed there
74
+ but not in production yet. Take only their *values* from them (machines, users, mails, the
75
+ environment's name in texts, variables): the jobs stay as production has them, and the
76
+ releases come later from their deliveries (step 6).
72
77
 
73
78
  ```bash
74
- ctmchain release start baseline
75
- ctmchain receive ../exports/uat/<uat>.xml --full # preview: the decisions
79
+ ctmchain release start environments
80
+ ctmchain receive ../exports/uat/<uat>.xml --values-only # preview: the decisions
76
81
  ```
77
82
 
78
- A full export of an environment other than production leaves production alone by default.
79
83
  Read the decisions with the person, by section, before `--apply`:
80
84
 
81
- - **groups** (the same difference in many jobs, or a machine, user, mail or variable): a new
85
+ - **groups** (a machine, user, mail or variable, or the environment's name in a text): a new
82
86
  constant with a value per environment. Give them readable names with
83
- `--choose 'group:...=new:<name>'`, or in the *Import* page of `ctmchain serve`.
84
- - **exceptions** (a value that differs in one job), **children** (conditions or ON blocks
85
- that differ: ignored by default), **removed / added / absent** (jobs and folders only one
86
- environment has or lacks).
87
+ `--choose 'group:...=new:<name>'`, or in the *Import* page of `ctmchain serve` (box *Only
88
+ the values of <env>*).
89
+ - everything else is left out by default: changes of jobs (**differences**, **children**),
90
+ jobs the environment lacks (**removed**: kept) and folders or jobs only it has
91
+ (**added**: left out). A difference the person knows is the environment's own (a job that
92
+ is Dummy there, another command line) may be made an **exception** instead.
87
93
 
88
- Then `--apply`, `ctmchain export --env uat` to look at the result, and commit. Same for bench
89
- and test. Receiving the same export again shows what still differs.
94
+ Then `--apply`, look at `ctmchain export --env uat`, and commit. Same for bench and test (they
95
+ only learn the values of the constants that exist by now). Production is untouched:
96
+ `ctmchain export --env prod --jobnames original` is still byte for byte the prod export.
90
97
 
91
98
  ### 4. The conventions agreed for these chains
92
99
 
@@ -104,13 +111,33 @@ and test. Receiving the same export again shows what still differs.
104
111
  environment, a letter in place of the application's first letter): not fixed on the way
105
112
  in. `ctmchain validate` lists them with suggested fixes, to correct in later releases.
106
113
 
107
- ### 5. Close the baseline
114
+ ### 5. Close the environments release
108
115
 
109
116
  ```bash
110
- ctmchain release installed prod # nothing to install: the baseline only describes them
117
+ ctmchain release installed prod # nothing to install: it only describes the environments
111
118
  ctmchain release close
112
119
  ```
113
120
 
121
+ ### 6. The releases already on their way, from their deliveries
122
+
123
+ For each release installed somewhere but not in production yet, ask, numbered: its name,
124
+ the delivery XML of each of its parts, and the environments it is installed in. Then, from
125
+ `main`, one release each:
126
+
127
+ ```bash
128
+ ctmchain release start <name>
129
+ ctmchain receive ../exports/deliveries/<name>.xml --stat # a delivery: its differences
130
+ ctmchain receive ../exports/deliveries/<name>.xml --apply # are the release's changes
131
+ ctmchain commit -m "feat: release <name>"
132
+ ctmchain release installed uat # each environment it is in
133
+ ```
134
+
135
+ A folder two releases both changed comes whole in each delivery: when the first one is closed
136
+ into `main`, *Update from main* in the other merges them. Check each release with
137
+ `ctmchain receive ../exports/<env>/<env>.xml` (preview only) on the release branch: what is
138
+ left is what differs for another reason (often conditions whose names differ per
139
+ environment).
140
+
114
141
  Then the everyday flow of the data repository's own `CLAUDE.md` (written by `init`):
115
142
  deliveries through `ctmchain receive`, a full prod export now and then to catch up with
116
143
  production, releases installed environment by environment.
@@ -213,6 +213,13 @@ or addresses anywhere in this public repository.
213
213
  take` and the boxes of the *Compare* page; one attribute, variable, condition, group, job,
214
214
  folder, condition name, constant value or exception at a time, conflicts value by value.
215
215
  13. **PyPI release 0.1.0 — DONE (2026-11-03)**: Trusted Publishing from the `v0.1.0` tag.
216
+ 14. **Similar condition names — DONE (2026-11-03), released in 0.1.1**, asked by the author:
217
+ `validate` reports names that differ only by a copy's suffix or by case (`similar-names`)
218
+ and offers to number them (`number-similar`: `…_OK1`, `…_OK2`).
219
+ 15. **Only the values of an environment — DONE (2026-11-03), released in 0.1.2**, asked by
220
+ the author for the company (uat and bench carry releases production does not have yet):
221
+ `receive --values-only`, `skip` for added folders and jobs, *adopt* decisions; the
222
+ `bootstrap` skill builds the releases on their way from their deliveries.
216
223
 
217
224
  Later, unordered: `envs` variants of sub-elements; second-environment import with learning; `diff`
218
225
  of environments and XML files with `xmldiffreport`; mapping editor; upstream graph explorer
@@ -6,6 +6,52 @@ All notable changes to this project are documented here. The format follows
6
6
 
7
7
  ## [Unreleased]
8
8
 
9
+ ## [0.1.2] - 2026-11-03
10
+
11
+ ### Added
12
+
13
+ - `receive --values-only` and the *Only the values of <env>* box of the Import page: an export
14
+ of an environment whose jobs carry releases production does not have yet gives its
15
+ machines, users, mails, variables and its name in texts (new constants or their values) and
16
+ nothing else by default (differences and children ignored, jobs and folders it lacks kept,
17
+ folders and jobs only it has left out).
18
+ - *added* decisions have the option `skip` ("Leave out"): the folder or job does not enter the
19
+ repository.
20
+ - *adopt* decisions: where the repository still has production's value of a constant and the
21
+ export has the constant (a job no earlier export of that environment had), use the
22
+ constant; production does not change. Also inside texts (`[P77][PROD]` and
23
+ `@{env.subject}`).
24
+ - The `bootstrap` skill takes only the values from the full exports, then builds the releases
25
+ on their way from their deliveries; the data repository's `CLAUDE.md` mentions
26
+ `--values-only`.
27
+
28
+ ### Fixed
29
+
30
+ - A job numbered by `--dedupe-jobnames` (`X2`) that a full export of another environment names
31
+ otherwise lost its original JOBNAME when kept: production's export changed.
32
+ - Values taken inside the children of a job (a mail of an ON block) were put back when the
33
+ job's children were kept as they were.
34
+
35
+ ## [0.1.1] - 2026-11-03
36
+
37
+ ### Added
38
+
39
+ - `validate` reports `similar-names` (warning): conditions whose names differ only by the
40
+ suffix a copy of a chain appended (`…_OK-U26`, `…_OK-U26-U31`) or by case (`_OK`, `_Ok`).
41
+ The fix `number-similar` gives them the common name with a number (`…_OK1`, `…_OK2`, …) in
42
+ every job that uses them; a condition with an end outside the repository keeps its name.
43
+ Names made of their own jobs (`A-TO-LOAD-S1`, `A-TO-LOAD-S2`) are not reported.
44
+
45
+ ### Changed
46
+
47
+ - A numbered standard name (`A-B_OK2`, `A-TO-B2`) is a standard name for `validate`.
48
+
49
+ ### Fixed
50
+
51
+ - Resolving several findings at once failed ("a batch cannot contain another batch") when a
52
+ fix was itself several changes (numbering repeated JOBNAMEs or similar names): a batch now
53
+ runs the changes of a batch inside it in its place.
54
+
9
55
  ## [0.1.0] - 2026-11-03
10
56
 
11
57
  First release on PyPI.
@@ -182,5 +228,7 @@ First release on PyPI.
182
228
  - On Windows, unknown `/api/...` paths and missing assets were answered with the app instead of
183
229
  404 (the static files handler sees `\` separators there).
184
230
 
185
- [Unreleased]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.0...HEAD
231
+ [Unreleased]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.2...HEAD
232
+ [0.1.2]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.1...v0.1.2
233
+ [0.1.1]: https://github.com/bilouro/controlm-chain-admin/compare/v0.1.0...v0.1.1
186
234
  [0.1.0]: https://github.com/bilouro/controlm-chain-admin/releases/tag/v0.1.0
@@ -287,6 +287,27 @@ still exports prod byte for byte; uat comes out identical in 257 of 329 folders
287
287
  production has them, listed by `validate`). Sub-element variants (an ON block for one
288
288
  environment only) remain open.
289
289
 
290
+ **Only the values of an environment** (2026-11-03, asked by the author for the company, where
291
+ the full exports of uat and bench carry releases production does not have yet):
292
+ `receive --values-only` (the *Only the values of <env>* box of the Import page) takes from an
293
+ export what makes a constant — machines, users, mails, variables, the environment's name in a
294
+ text — and leaves everything else out by default: differences and children ignored, jobs it
295
+ lacks kept (*removed*: `keep`), folders it lacks kept (*absent*: `keep`), folders and jobs only
296
+ it has left out (*added*: `skip`, also offered in any full export; with values only it is a
297
+ decision in a delivery too); a repeated difference of another attribute is ignored rather than
298
+ made exceptions. The releases then come from their deliveries, one release branch each (the
299
+ `bootstrap` skill, step 6). Two decisions came with it, for every receive: *adopt* (one per
300
+ constant) — a job no earlier export of the environment had still has production's value of a
301
+ constant where this export has the constant, in the whole value or inside a text
302
+ (`[P77][PROD]` for `[P77][@{env.subject}]`): using the constant leaves production as it was, so
303
+ it is the default (it replaces the exceptions a full export made there); and values taken
304
+ inside children whose shape is kept (a release's new condition on the job that sends the mail)
305
+ stay taken. A job numbered by the deduplication (`P77_F2`) that another environment names
306
+ otherwise keeps its original JOBNAME when it is kept. On the real exports: prod first, then
307
+ uat and bench with values only, give 42 + 15 constants, no exception, prod byte for byte, uat
308
+ identical in 234 of 329 folders (the rest are its releases and its own folders) and bench in
309
+ 287 of 341.
310
+
290
311
  ## 6. Import
291
312
 
292
313
  - Lossless by default: re-importing the XML produced by `export` yields no changes
@@ -363,6 +384,20 @@ environment only) remain open.
363
384
  back (`restore-word`). A MEMNAME that merely differs from its JOBNAME is not reported: on the
364
385
  real chain 647 of them are script names. On the uat data: 39 replaced words, 4 letters in
365
386
  place of the P, 1 foreign letter, 2 foreign resources (107 before `leading_only`).
387
+ - Similar names (implemented 2026-11-03, asked by the author: "detecta nomes similares no
388
+ validate e sugere nomes como nome1, nome2"): `similar-names` (warning, one finding per
389
+ family) groups the conditions whose names are equal once the suffixes a copy appended
390
+ (`-<letter><digits>`, repeated: `…_OK-U26-U31`) are removed and case is ignored; a family
391
+ whose every name is made of its own producer and consumer is left out (`A-TO-LOAD-S1`,
392
+ `A-TO-LOAD-S2` only look alike because the jobs do). The fix `number-similar` renames them,
393
+ shortest name first, to the stem of the first with a number (`…_OK1`, `…_OK2`; a number
394
+ another condition has is skipped) in every job that uses them; a condition with an end
395
+ outside the repository keeps its name, as a job outside may use it. `validate` accepts a
396
+ numbered standard name (`A-B_OK2`, `A-TO-B2`), so a numbered copy that still embeds the
397
+ original's jobs shows as `stale-name`, with its standard name offered. On the real chain:
398
+ 51 families, 115 conditions; one family is left after the fix (its other members only have
399
+ a producer). Fixes that are several changes may now be resolved together: a `batch` runs a
400
+ batch inside it in its place.
366
401
  - `diff A B`: plain `git diff` of the base is the first-class review tool. For XML↔XML semantic
367
402
  comparison the tool depends on `xmldiffreport` (key-aligned, N-way, Markdown/HTML report).
368
403
  - `show --envs <node>`: the node resolved in every environment, side by side, differences
@@ -686,7 +721,7 @@ the first import.
686
721
  Quality
687
722
  - `validate [--env <env>] [--strict]` — graph, naming, overlay and resolution checks; exit code = highest severity.
688
723
  - `resolve [CODE=KIND...] [--apply] [--stat]` — implemented: fixes for the findings (see §8); without arguments, the fix kinds per code.
689
- - `receive <xml> [--env ENV] [--full|--partial] [--choose ID=OPTION ...] [--apply] [--stat] [--format text|json]` — implemented: an export of one environment (some folders or all) taken into the checked-out release (§5): the decisions with their defaults, then the change set. A group's id holds `=`: `--choose 'group:RUN_AS=a>b=new:user.batch_user_proc'`.
724
+ - `receive <xml> [--env ENV] [--full|--partial] [--values-only] [--choose ID=OPTION ...] [--apply] [--stat] [--format text|json]` — implemented: an export of one environment (some folders or all) taken into the checked-out release (§5): the decisions with their defaults, then the change set. `--values-only`: the environment's values and nothing else by default. A group's id holds `=`: `--choose 'group:RUN_AS=a>b=new:user.batch_user_proc'`.
690
725
  - `env learn <xml> [--env ENV] [--only PREFIX ...] [--apply]` — implemented: only the values of the constants in that environment (`--only user.` keeps some constants, e.g. when the export is anonymised).
691
726
  - `set-exception <env> <node> [--set ATTR=VALUE] [--unset ATTR] [--var %%NAME=VALUE] [--drop NAME]` — implemented: an exception of one environment for one job or folder (§5), previewed, `--apply` writes `envs/<env>/overrides.json`.
692
727
  - `rename-constant <old> <new>` — implemented: placeholders, `chain.json`, environment values and exceptions follow.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: controlm-chain-admin
3
- Version: 0.1.0
3
+ Version: 0.1.2
4
4
  Summary: Manage BMC Control-M job chains as a git-friendly JSON repository: XML import/export, links between jobs, environment-neutral base, validation and refactorings.
5
5
  Project-URL: Homepage, https://github.com/bilouro/controlm-chain-admin
6
6
  Project-URL: Documentation, https://github.com/bilouro/controlm-chain-admin#readme
@@ -85,8 +85,9 @@ export into:
85
85
  other branch (one attribute, one job without its folder, one condition) can be taken into
86
86
  yours, conflicts shown value by value.
87
87
  - **Validation and refactorings** that the Control-M UI does not offer: dangling or stale
88
- conditions, chains broken by renames, renaming a sub-application across a folder, moving
89
- a job, inserting a job in a chain.
88
+ conditions, chains broken by renames, condition names that differ only by a copy's suffix
89
+ or by case (numbered as the fix), renaming a sub-application across a folder, moving a job,
90
+ inserting a job in a chain.
90
91
  - **Byte-identical export.** Untouched definitions export exactly as they were imported.
91
92
 
92
93
  ## Install
@@ -111,8 +112,9 @@ python -m venv .venv && .venv/bin/pip install -e ".[dev,web]"
111
112
  ```
112
113
 
113
114
  The `bootstrap` skill (`.claude/skills/bootstrap/SKILL.md`) takes it from there, for a person
114
- or Claude Code: the gate, then a new chain repository built from the full export of each
115
- environment, production first.
115
+ or Claude Code: the gate, then a new chain repository: production first, the values of the
116
+ other environments from their full exports, then the releases on their way from their
117
+ deliveries.
116
118
 
117
119
  ## Quickstart
118
120
 
@@ -136,6 +138,7 @@ ctmchain package main release/2026-11 --env uat --repo ./my-chains # import fil
136
138
  ctmchain release installed uat --repo ./my-chains # tag uat/2026-11-1
137
139
  ctmchain receive delivery.xml --repo ./my-chains # a team's folders, made neutral
138
140
  ctmchain receive uat_full.xml --full --repo ./my-chains # all of uat: constants, what only uat has
141
+ ctmchain receive uat_full.xml --values-only --repo ./my-chains # only uat's machines, users, mails
139
142
  ctmchain take release/2026-10 --list --repo ./my-chains # what another release changed
140
143
  ctmchain take release/2026-10 --item job:P77_CHAIN/P77_D --repo ./my-chains # one job of it; --apply
141
144
  ctmchain release close --repo ./my-chains # once installed in prod
@@ -39,8 +39,9 @@ export into:
39
39
  other branch (one attribute, one job without its folder, one condition) can be taken into
40
40
  yours, conflicts shown value by value.
41
41
  - **Validation and refactorings** that the Control-M UI does not offer: dangling or stale
42
- conditions, chains broken by renames, renaming a sub-application across a folder, moving
43
- a job, inserting a job in a chain.
42
+ conditions, chains broken by renames, condition names that differ only by a copy's suffix
43
+ or by case (numbered as the fix), renaming a sub-application across a folder, moving a job,
44
+ inserting a job in a chain.
44
45
  - **Byte-identical export.** Untouched definitions export exactly as they were imported.
45
46
 
46
47
  ## Install
@@ -65,8 +66,9 @@ python -m venv .venv && .venv/bin/pip install -e ".[dev,web]"
65
66
  ```
66
67
 
67
68
  The `bootstrap` skill (`.claude/skills/bootstrap/SKILL.md`) takes it from there, for a person
68
- or Claude Code: the gate, then a new chain repository built from the full export of each
69
- environment, production first.
69
+ or Claude Code: the gate, then a new chain repository: production first, the values of the
70
+ other environments from their full exports, then the releases on their way from their
71
+ deliveries.
70
72
 
71
73
  ## Quickstart
72
74
 
@@ -90,6 +92,7 @@ ctmchain package main release/2026-11 --env uat --repo ./my-chains # import fil
90
92
  ctmchain release installed uat --repo ./my-chains # tag uat/2026-11-1
91
93
  ctmchain receive delivery.xml --repo ./my-chains # a team's folders, made neutral
92
94
  ctmchain receive uat_full.xml --full --repo ./my-chains # all of uat: constants, what only uat has
95
+ ctmchain receive uat_full.xml --values-only --repo ./my-chains # only uat's machines, users, mails
93
96
  ctmchain take release/2026-10 --list --repo ./my-chains # what another release changed
94
97
  ctmchain take release/2026-10 --item job:P77_CHAIN/P77_D --repo ./my-chains # one job of it; --apply
95
98
  ctmchain release close --repo ./my-chains # once installed in prod
@@ -1,3 +1,3 @@
1
1
  """controlm-chain-admin — manage BMC Control-M job chains as a git-friendly JSON repository."""
2
2
 
3
- __version__ = "0.1.0"
3
+ __version__ = "0.1.2"
@@ -253,6 +253,12 @@ def build_parser() -> argparse.ArgumentParser:
253
253
  dest="full",
254
254
  help="some folders only, e.g. a delivery",
255
255
  )
256
+ rec.add_argument(
257
+ "--values-only",
258
+ action="store_true",
259
+ help="take the values of the environment (machines, users, mails, its name in texts) "
260
+ "and leave the rest out by default: for an environment with releases production has not",
261
+ )
256
262
  rec.add_argument(
257
263
  "--choose",
258
264
  action="append",
@@ -564,8 +570,8 @@ def cmd_env_help(args: argparse.Namespace) -> int:
564
570
 
565
571
  _SHOWN_PER_KIND = 20
566
572
  _DECISION_ORDER = (
567
- "learn", "set-value", "group", "constant", "unmatched", "value", "children", "removed",
568
- "added", "absent",
573
+ "learn", "set-value", "adopt", "group", "constant", "unmatched", "value", "children",
574
+ "removed", "added", "absent",
569
575
  ) # fmt: skip
570
576
 
571
577
 
@@ -581,6 +587,9 @@ def _decision_line(decision: intake_mod.Decision) -> str:
581
587
  return f"{head} {decision.constant}: {decision.expected} -> {decision.incoming}{uses}"
582
588
  if decision.kind in ("value", "constant"):
583
589
  return f"{head} {decision.current} -> {decision.incoming}"
590
+ if decision.kind == "adopt":
591
+ uses = _plural(decision.count, "use")
592
+ return f"{head} {decision.incoming} where the repository has {decision.current} ({uses})"
584
593
  if decision.kind == "unmatched":
585
594
  uses = _plural(decision.count, "use")
586
595
  return (
@@ -607,6 +616,7 @@ def _print_intake(intake: intake_mod.Intake) -> None:
607
616
  f"Folders: {len(intake.folders)} ({statuses['new']} new, {statuses['changed']} changed, "
608
617
  f"{statuses['same']} unchanged)"
609
618
  + (", full export" if intake.full else ", some folders only" if intake.partial else "")
619
+ + (", values only" if intake.values_only else "")
610
620
  )
611
621
  if intake.decisions:
612
622
  print(
@@ -630,12 +640,12 @@ def cmd_receive(args: argparse.Namespace) -> int:
630
640
  repo = load_repo(args.repo)
631
641
  data = args.xml.read_bytes()
632
642
  choices = _pairs(args.choose, "ID=OPTION", last=True) # ids may hold "=", options do not
633
- intake = intake_mod.analyze(repo, data, args.env, args.full)
643
+ intake = intake_mod.analyze(repo, data, args.env, args.full, args.values_only)
634
644
  if args.format == "text":
635
645
  _print_intake(intake)
636
646
  if args.apply:
637
647
  workspace_mod.ensure_editable(repo)
638
- change = intake_mod.receive(repo, data, intake.env, choices, intake.full)
648
+ change = intake_mod.receive(repo, data, intake.env, choices, intake.full, args.values_only)
639
649
  if args.format == "json":
640
650
  result = refactor_mod.apply(repo, change) if args.apply and change.edits else None
641
651
  body = {"intake": intake.to_json(), "change": change.to_json(), "applied": bool(result)}
@@ -26,10 +26,12 @@ from controlm_chain_admin.core.repo import ChainRepo, read_json
26
26
  from controlm_chain_admin.core.validate import (
27
27
  Finding,
28
28
  NameRules,
29
+ copy_stem,
29
30
  embeds_endpoints,
30
31
  parse_condition_name,
31
32
  replaced_words,
32
33
  resources,
34
+ similar_names,
33
35
  validate,
34
36
  )
35
37
 
@@ -101,6 +103,7 @@ class _Index:
101
103
  jobs: dict[str, list[str]] # folder -> job ids
102
104
  repo: ChainRepo | None = None
103
105
  names: NameRules | None = None
106
+ similar: dict[str, list[Link]] = field(default_factory=dict) # stem -> similar conditions
104
107
 
105
108
 
106
109
  def _index(repo: ChainRepo) -> _Index:
@@ -114,7 +117,16 @@ def _index(repo: ChainRepo) -> _Index:
114
117
  for job in ids:
115
118
  by_name.setdefault(job.split(DUPLICATE_SEPARATOR)[0], []).append(NodeRef(folder, job))
116
119
  used = {link.name: link.id for link in links.values()}
117
- return _Index(links, standard_names(links), used, by_name, jobs, repo, NameRules.of(repo))
120
+ return _Index(
121
+ links,
122
+ standard_names(links),
123
+ used,
124
+ by_name,
125
+ jobs,
126
+ repo,
127
+ NameRules.of(repo),
128
+ similar_names(links),
129
+ )
118
130
 
119
131
 
120
132
  def _waiting(finding: Finding, link: Link, index: _Index) -> list[Fix]:
@@ -277,6 +289,50 @@ def _numbering(finding: Finding, index: _Index) -> list[Fix]:
277
289
  ]
278
290
 
279
291
 
292
+ def _numbering_similar(finding: Finding, index: _Index) -> list[Fix]:
293
+ stem, family = finding.name, index.similar.get(finding.name, [])
294
+ kept = [link for link in family if link.external]
295
+ taken = {name.upper() for name in index.used}
296
+ renames: list[tuple[Link, str]] = []
297
+ number = 1
298
+ for link in (link for link in family if not link.external):
299
+ while True:
300
+ name = f"{stem}{number}"
301
+ if copy_stem(name) != name: # the stem ends with -<letter>: keep the number apart
302
+ name = f"{stem}_{number}"
303
+ number += 1
304
+ if name.upper() not in taken:
305
+ break
306
+ renames.append((link, name))
307
+ if not renames:
308
+ return []
309
+ op, params = _operation(
310
+ [("rename-link", {"link": link.id, "name": name}) for link, name in renames]
311
+ )
312
+ explanation = (
313
+ f"Gives each condition the common name with a number at the end ({stem}1, {stem}2…), "
314
+ "in every job that uses it, so that no two names differ only by a suffix or by case."
315
+ )
316
+ explanation += "".join(
317
+ f" {link.name} keeps its name: a job outside this repository may use it." for link in kept
318
+ )
319
+ return [
320
+ Fix(
321
+ "number-similar",
322
+ "Number the similar names",
323
+ "; ".join(f"rename {link.name} -> {name}" for link, name in renames),
324
+ explanation,
325
+ op,
326
+ params,
327
+ tuple(
328
+ Effect(str(node), "change", "condition name", link.name, name)
329
+ for link, name in renames
330
+ for node in _users(link)
331
+ ),
332
+ )
333
+ ]
334
+
335
+
280
336
  def _rename_to(link: Link, name: str, index: _Index, kind: str, title: str, why: str) -> list[Fix]:
281
337
  if name == link.name or index.used.get(name, link.id) != link.id:
282
338
  return [] # nothing to do, or the name belongs to another link
@@ -367,6 +423,8 @@ def _name_fixes(finding: Finding, index: _Index) -> list[Fix]:
367
423
  def _fixes(finding: Finding, index: _Index) -> list[Fix]:
368
424
  if finding.code == "duplicate-jobname":
369
425
  return _numbering(finding, index)
426
+ if finding.code == "similar-names":
427
+ return _numbering_similar(finding, index)
370
428
  if finding.code in (
371
429
  "foreign-letter",
372
430
  "letter-for-p",
@@ -23,7 +23,9 @@ Environments: {environments}. Production is {production}: `{trunk}` mirrors it.
23
23
  it) and closed into `{trunk}` once {production} has it (`ctmchain release close`).
24
24
  3. Exports come in through `ctmchain receive <xml>` or the *Import* page: preview, decide,
25
25
  `--apply`, commit. A full export of an environment other than {production} leaves
26
- {production} alone by default; a delivery (some folders) changes the release.
26
+ {production} alone by default; a delivery (some folders) changes the release. When that
27
+ environment has releases {production} has not, take only its values (`--values-only`, box
28
+ *Only the values* of the *Import* page): the releases come from their deliveries.
27
29
  4. Never hand-edit `index.json` (derived) or `meta/` (volatile attributes, export header);
28
30
  edit through `ctmchain` or the web UI, which preview every change.
29
31
  5. Exports are big: read them with scripts and counts, never whole.
@@ -45,6 +47,7 @@ Environments: {environments}. Production is {production}: `{trunk}` mirrors it.
45
47
  ctmchain release status # branch, releases, what may be edited
46
48
  ctmchain receive exports/uat/delivery.xml # preview the decisions of an export
47
49
  ctmchain receive exports/uat/delivery.xml --apply
50
+ ctmchain receive exports/uat/full.xml --values-only # machines, users, mails of uat only
48
51
  ctmchain take release/<other> --list # what another release changed, item by item
49
52
  ctmchain validate # broken chains, dirty names, suggested fixes
50
53
  ctmchain export --env uat -o uat.xml