controlm-chain-admin 0.1.0__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.
- controlm_chain_admin-0.1.0/.claude/ATTRIBUTION.md +41 -0
- controlm_chain_admin-0.1.0/.claude/skills/bootstrap/SKILL.md +116 -0
- controlm_chain_admin-0.1.0/.claude/skills/fastapi-api/SKILL.md +70 -0
- controlm_chain_admin-0.1.0/.claude/skills/oss-repo-standards/SKILL.md +68 -0
- controlm_chain_admin-0.1.0/.claude/skills/oss-repo-standards/check_repo.sh +45 -0
- controlm_chain_admin-0.1.0/.claude/skills/python-core-tdd/SKILL.md +82 -0
- controlm_chain_admin-0.1.0/.claude/skills/react-frontend/SKILL.md +71 -0
- controlm_chain_admin-0.1.0/.claude/skills/verify/SKILL.md +64 -0
- controlm_chain_admin-0.1.0/.claude/skills/verify/verify.sh +73 -0
- controlm_chain_admin-0.1.0/.editorconfig +18 -0
- controlm_chain_admin-0.1.0/.github/FUNDING.yml +1 -0
- controlm_chain_admin-0.1.0/.github/ISSUE_TEMPLATE/bug_report.md +25 -0
- controlm_chain_admin-0.1.0/.github/ISSUE_TEMPLATE/feature_request.md +17 -0
- controlm_chain_admin-0.1.0/.github/PULL_REQUEST_TEMPLATE.md +23 -0
- controlm_chain_admin-0.1.0/.github/workflows/ci.yml +91 -0
- controlm_chain_admin-0.1.0/.github/workflows/release.yml +47 -0
- controlm_chain_admin-0.1.0/.gitignore +37 -0
- controlm_chain_admin-0.1.0/.pre-commit-config.yaml +18 -0
- controlm_chain_admin-0.1.0/BACKLOG.md +221 -0
- controlm_chain_admin-0.1.0/CHANGELOG.md +186 -0
- controlm_chain_admin-0.1.0/CLAUDE.md +44 -0
- controlm_chain_admin-0.1.0/CODE_OF_CONDUCT.md +31 -0
- controlm_chain_admin-0.1.0/CONTRIBUTING.md +63 -0
- controlm_chain_admin-0.1.0/DESIGN.md +731 -0
- controlm_chain_admin-0.1.0/LICENSE +21 -0
- controlm_chain_admin-0.1.0/PKG-INFO +186 -0
- controlm_chain_admin-0.1.0/README.md +140 -0
- controlm_chain_admin-0.1.0/SECURITY.md +25 -0
- controlm_chain_admin-0.1.0/pyproject.toml +111 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/__init__.py +3 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/__main__.py +10 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/cli.py +1239 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/__init__.py +5 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/attribute_groups.py +61 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/commit.py +243 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/compare.py +806 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/constants.py +371 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/errors.py +131 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/fixes.py +433 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/graph.py +157 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/guide.py +76 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/history.py +335 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/inspect.py +105 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/intake.py +1148 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/jsonmodel.py +253 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/letter.py +169 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/links.py +299 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/membership.py +36 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/model.py +67 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/ops.py +656 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/overrides.py +113 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/package.py +318 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/refactor.py +1301 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/relations.py +194 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/repo.py +339 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/selection.py +189 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/take.py +567 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/validate.py +454 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/workspace.py +592 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/core/xmlio.py +151 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/py.typed +0 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/__init__.py +1 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/app.py +136 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/deps.py +27 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/errors.py +82 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/__init__.py +1 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/changes.py +55 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/environments.py +26 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/folders.py +100 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/history.py +136 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/intake.py +31 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/jobs.py +63 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/package.py +77 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/repo.py +27 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/table.py +17 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/validate.py +36 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/routers/workspace.py +127 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/schemas.py +525 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/serve.py +23 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/settings.py +30 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/graph-CA0VXlpl.js +31 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/index-BA0sPihz.css +1 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/index-DYc-B93N.js +28 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/assets/react-DB7G9u3x.js +28 -0
- controlm_chain_admin-0.1.0/src/controlm_chain_admin/web/static/index.html +16 -0
- controlm_chain_admin-0.1.0/tests/__init__.py +0 -0
- controlm_chain_admin-0.1.0/tests/conftest.py +45 -0
- controlm_chain_admin-0.1.0/tests/realdata/__init__.py +0 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_compare_real.py +48 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_constants_real.py +51 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_dedupe_real.py +45 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_environments.py +65 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_export_file.py +37 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_fixes_real.py +49 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_import_export.py +56 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_package_real.py +51 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_refactor_real.py +49 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_roundtrip.py +33 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_table_real.py +40 -0
- controlm_chain_admin-0.1.0/tests/realdata/test_validate_real.py +50 -0
- controlm_chain_admin-0.1.0/tests/support/__init__.py +0 -0
- controlm_chain_admin-0.1.0/tests/support/anomalies.py +64 -0
- controlm_chain_admin-0.1.0/tests/support/envexports.py +72 -0
- controlm_chain_admin-0.1.0/tests/support/gitrepo.py +70 -0
- controlm_chain_admin-0.1.0/tests/support/rawxml.py +71 -0
- controlm_chain_admin-0.1.0/tests/support/samples.py +39 -0
- controlm_chain_admin-0.1.0/tests/unit/__init__.py +0 -0
- controlm_chain_admin-0.1.0/tests/unit/test_attribute_groups.py +25 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli.py +46 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_commands.py +137 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_commit.py +53 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_compare.py +94 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_distinct_filter.py +56 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_env.py +97 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_inspect.py +99 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_package.py +107 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_receive.py +154 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_refactor.py +201 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_release.py +88 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_resolve.py +60 -0
- controlm_chain_admin-0.1.0/tests/unit/test_cli_serve.py +55 -0
- controlm_chain_admin-0.1.0/tests/unit/test_commit.py +151 -0
- controlm_chain_admin-0.1.0/tests/unit/test_compare.py +262 -0
- controlm_chain_admin-0.1.0/tests/unit/test_constants.py +198 -0
- controlm_chain_admin-0.1.0/tests/unit/test_constants_ops.py +316 -0
- controlm_chain_admin-0.1.0/tests/unit/test_constants_overview.py +28 -0
- controlm_chain_admin-0.1.0/tests/unit/test_dedupe.py +197 -0
- controlm_chain_admin-0.1.0/tests/unit/test_edit_ops.py +262 -0
- controlm_chain_admin-0.1.0/tests/unit/test_env_rule.py +133 -0
- controlm_chain_admin-0.1.0/tests/unit/test_fixes.py +238 -0
- controlm_chain_admin-0.1.0/tests/unit/test_graph.py +87 -0
- controlm_chain_admin-0.1.0/tests/unit/test_history.py +112 -0
- controlm_chain_admin-0.1.0/tests/unit/test_inspect.py +82 -0
- controlm_chain_admin-0.1.0/tests/unit/test_intake.py +127 -0
- controlm_chain_admin-0.1.0/tests/unit/test_intake_envs.py +153 -0
- controlm_chain_admin-0.1.0/tests/unit/test_intake_groups.py +180 -0
- controlm_chain_admin-0.1.0/tests/unit/test_jsonmodel.py +150 -0
- controlm_chain_admin-0.1.0/tests/unit/test_letter.py +171 -0
- controlm_chain_admin-0.1.0/tests/unit/test_links.py +140 -0
- controlm_chain_admin-0.1.0/tests/unit/test_links_truth.py +230 -0
- controlm_chain_admin-0.1.0/tests/unit/test_main_module.py +20 -0
- controlm_chain_admin-0.1.0/tests/unit/test_membership.py +62 -0
- controlm_chain_admin-0.1.0/tests/unit/test_overrides.py +184 -0
- controlm_chain_admin-0.1.0/tests/unit/test_package.py +277 -0
- controlm_chain_admin-0.1.0/tests/unit/test_packaging.py +49 -0
- controlm_chain_admin-0.1.0/tests/unit/test_rawxml.py +50 -0
- controlm_chain_admin-0.1.0/tests/unit/test_receive.py +131 -0
- controlm_chain_admin-0.1.0/tests/unit/test_refactor.py +349 -0
- controlm_chain_admin-0.1.0/tests/unit/test_relations.py +80 -0
- controlm_chain_admin-0.1.0/tests/unit/test_repo_ops.py +218 -0
- controlm_chain_admin-0.1.0/tests/unit/test_selection.py +138 -0
- controlm_chain_admin-0.1.0/tests/unit/test_take.py +179 -0
- controlm_chain_admin-0.1.0/tests/unit/test_validate.py +142 -0
- controlm_chain_admin-0.1.0/tests/unit/test_validate_names.py +145 -0
- controlm_chain_admin-0.1.0/tests/unit/test_workspace.py +470 -0
- controlm_chain_admin-0.1.0/tests/unit/test_xmlio.py +144 -0
- controlm_chain_admin-0.1.0/tests/web/__init__.py +0 -0
- controlm_chain_admin-0.1.0/tests/web/conftest.py +38 -0
- controlm_chain_admin-0.1.0/tests/web/openapi.snapshot.json +4613 -0
- controlm_chain_admin-0.1.0/tests/web/test_api.py +167 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_commit.py +73 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_compare.py +64 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_edit.py +107 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_environments.py +30 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_intake.py +98 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_package.py +116 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_take.py +83 -0
- controlm_chain_admin-0.1.0/tests/web/test_api_workspace.py +163 -0
- controlm_chain_admin-0.1.0/tests/web/test_openapi.py +31 -0
- controlm_chain_admin-0.1.0/tests/web/test_static_ui.py +60 -0
- controlm_chain_admin-0.1.0/web/.prettierrc +6 -0
- controlm_chain_admin-0.1.0/web/eslint.config.js +22 -0
- controlm_chain_admin-0.1.0/web/index.html +13 -0
- controlm_chain_admin-0.1.0/web/package-lock.json +6864 -0
- controlm_chain_admin-0.1.0/web/package.json +56 -0
- controlm_chain_admin-0.1.0/web/scripts/dump_fixtures.py +261 -0
- controlm_chain_admin-0.1.0/web/src/app/App.tsx +89 -0
- controlm_chain_admin-0.1.0/web/src/components/DiffView.tsx +28 -0
- controlm_chain_admin-0.1.0/web/src/components/EffectsTable.tsx +56 -0
- controlm_chain_admin-0.1.0/web/src/components/ui.tsx +226 -0
- controlm_chain_admin-0.1.0/web/src/features/chain-table/ChainTablePage.test.tsx +67 -0
- controlm_chain_admin-0.1.0/web/src/features/chain-table/ChainTablePage.tsx +211 -0
- controlm_chain_admin-0.1.0/web/src/features/changes/ChangeProvider.test.tsx +105 -0
- controlm_chain_admin-0.1.0/web/src/features/changes/ChangeProvider.tsx +170 -0
- controlm_chain_admin-0.1.0/web/src/features/changes/TokenForm.tsx +37 -0
- controlm_chain_admin-0.1.0/web/src/features/changes/changes-context.ts +20 -0
- controlm_chain_admin-0.1.0/web/src/features/commit/CommitButton.test.tsx +68 -0
- controlm_chain_admin-0.1.0/web/src/features/commit/CommitButton.tsx +26 -0
- controlm_chain_admin-0.1.0/web/src/features/commit/CommitDialog.tsx +137 -0
- controlm_chain_admin-0.1.0/web/src/features/commit/branch-name.ts +12 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/CommitPicker.tsx +85 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/ComparePage.test.tsx +423 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/ComparePage.tsx +844 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/CopyMarkdownButton.tsx +56 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/PackagePanel.tsx +213 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/TakeBar.tsx +170 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/refs.ts +29 -0
- controlm_chain_admin-0.1.0/web/src/features/compare/take-context.ts +19 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/AttributeForm.tsx +111 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/InlineAction.tsx +49 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/RawJsonEditor.tsx +44 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/RelationsEditor.test.tsx +97 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/RelationsEditor.tsx +367 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/VariablesEditor.tsx +68 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/editors.test.tsx +140 -0
- controlm_chain_admin-0.1.0/web/src/features/editing/labels.ts +8 -0
- controlm_chain_admin-0.1.0/web/src/features/environments/EnvironmentsPage.test.tsx +69 -0
- controlm_chain_admin-0.1.0/web/src/features/environments/EnvironmentsPage.tsx +195 -0
- controlm_chain_admin-0.1.0/web/src/features/exceptions/ExceptionsPanel.test.tsx +81 -0
- controlm_chain_admin-0.1.0/web/src/features/exceptions/ExceptionsPanel.tsx +147 -0
- controlm_chain_admin-0.1.0/web/src/features/exceptions/exception-count.ts +9 -0
- controlm_chain_admin-0.1.0/web/src/features/folder-graph/FolderGraph.tsx +62 -0
- controlm_chain_admin-0.1.0/web/src/features/folder-graph/FolderPage.test.tsx +41 -0
- controlm_chain_admin-0.1.0/web/src/features/folder-graph/FolderPage.tsx +270 -0
- controlm_chain_admin-0.1.0/web/src/features/folder-graph/flow.test.ts +38 -0
- controlm_chain_admin-0.1.0/web/src/features/folder-graph/flow.ts +54 -0
- controlm_chain_admin-0.1.0/web/src/features/intake/ImportPage.test.tsx +195 -0
- controlm_chain_admin-0.1.0/web/src/features/intake/ImportPage.tsx +413 -0
- controlm_chain_admin-0.1.0/web/src/features/job-detail/JobPage.test.tsx +73 -0
- controlm_chain_admin-0.1.0/web/src/features/job-detail/JobPage.tsx +176 -0
- controlm_chain_admin-0.1.0/web/src/features/repo/RepoBadge.test.tsx +24 -0
- controlm_chain_admin-0.1.0/web/src/features/repo/RepoBadge.tsx +22 -0
- controlm_chain_admin-0.1.0/web/src/features/validate/FixOptions.tsx +64 -0
- controlm_chain_admin-0.1.0/web/src/features/validate/ResolvePanel.tsx +143 -0
- controlm_chain_admin-0.1.0/web/src/features/validate/ValidatePage.test.tsx +132 -0
- controlm_chain_admin-0.1.0/web/src/features/validate/ValidatePage.tsx +250 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/BranchButton.test.tsx +160 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/BranchButton.tsx +28 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/ProtectedBanner.tsx +21 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/ReadOnlyNotice.tsx +32 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/WorkspaceDialog.tsx +384 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/plural.ts +3 -0
- controlm_chain_admin-0.1.0/web/src/features/workspace/use-workspace-action.ts +14 -0
- controlm_chain_admin-0.1.0/web/src/index.css +99 -0
- controlm_chain_admin-0.1.0/web/src/lib/api/client.ts +112 -0
- controlm_chain_admin-0.1.0/web/src/lib/api/queries.ts +135 -0
- controlm_chain_admin-0.1.0/web/src/lib/api/session.test.ts +14 -0
- controlm_chain_admin-0.1.0/web/src/lib/api/session.ts +34 -0
- controlm_chain_admin-0.1.0/web/src/lib/api/types.gen.ts +2989 -0
- controlm_chain_admin-0.1.0/web/src/lib/layout.ts +30 -0
- controlm_chain_admin-0.1.0/web/src/lib/utils.test.ts +32 -0
- controlm_chain_admin-0.1.0/web/src/lib/utils.ts +42 -0
- controlm_chain_admin-0.1.0/web/src/main.tsx +17 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/compare-selected.json +448 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/compare.json +993 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/environments.json +71 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/folder-P77_CHAIN.json +318 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/folder-P77_INGEST_DAILY.json +133 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/folders.json +19 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/git.json +29 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/intake-full.json +101 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/intake-groups.json +170 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/intake.json +100 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/job-P77_CHAIN-P77_B.json +158 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/job-P77_INGEST_DAILY-P77_INGEST_LOAD.json +132 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/package.json +98 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/pending.json +35 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/preview.json +12 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/repo.json +11 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/table.json +233 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/validate.json +440 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/workspace-main.json +18 -0
- controlm_chain_admin-0.1.0/web/src/test/fixtures/workspace.json +54 -0
- controlm_chain_admin-0.1.0/web/src/test/render.tsx +32 -0
- controlm_chain_admin-0.1.0/web/src/test/server.ts +139 -0
- controlm_chain_admin-0.1.0/web/src/test/setup.ts +40 -0
- controlm_chain_admin-0.1.0/web/tsconfig.json +25 -0
- controlm_chain_admin-0.1.0/web/vite.config.ts +43 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# Attribution
|
|
2
|
+
|
|
3
|
+
The skills under `.claude/skills/` were written for this project and adapt material from
|
|
4
|
+
**Everything Claude Code (ECC)** by Affaan Mustafa — <https://github.com/affaan-m/everything-claude-code>,
|
|
5
|
+
released under the MIT License. Adapted components:
|
|
6
|
+
|
|
7
|
+
| Skill here | Adapted from ECC |
|
|
8
|
+
|---|---|
|
|
9
|
+
| `verify` | `skills/verification-loop` |
|
|
10
|
+
| `oss-repo-standards` | `agents/opensource-packager`, `agents/opensource-sanitizer`, `rules/common/*` |
|
|
11
|
+
| `python-core-tdd` | `skills/tdd-workflow`, `skills/python-testing`, `agents/python-reviewer`, `rules/python/*` |
|
|
12
|
+
| `fastapi-api` | `skills/fastapi-patterns`, `agents/fastapi-reviewer`, `rules/python/fastapi.md` |
|
|
13
|
+
| `react-frontend` | `skills/react-patterns`, `skills/react-testing`, `skills/vite-patterns`, `rules/react/*`, `rules/typescript/*`, `rules/web/*` |
|
|
14
|
+
|
|
15
|
+
The adaptations are specific to this repository (stdlib-only core, one-core-two-adapters,
|
|
16
|
+
no-real-data rule, synthetic fixtures); they are not a redistribution of ECC. ECC's licence
|
|
17
|
+
text follows.
|
|
18
|
+
|
|
19
|
+
## Everything Claude Code — MIT License
|
|
20
|
+
|
|
21
|
+
MIT License
|
|
22
|
+
|
|
23
|
+
Copyright (c) 2026 Affaan Mustafa
|
|
24
|
+
|
|
25
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
26
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
27
|
+
in the Software without restriction, including without limitation the rights
|
|
28
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
29
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
30
|
+
furnished to do so, subject to the following conditions:
|
|
31
|
+
|
|
32
|
+
The above copyright notice and this permission notice shall be included in all
|
|
33
|
+
copies or substantial portions of the Software.
|
|
34
|
+
|
|
35
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
36
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
37
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
38
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
39
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
40
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
41
|
+
SOFTWARE.
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: bootstrap
|
|
3
|
+
description: Set up controlm-chain-admin where only PyPI is reachable (no GitHub), then build a new chain repository from the full exports of every environment, step by step, with the conventions agreed for environments, constants and exceptions. Use when someone says «instalar na empresa», «set up at work», «criar o repositório de dados», «começar do zero», «bootstrap», or opens this source for the first time on a new machine.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# bootstrap — the code from PyPI, the data repository from the exports
|
|
7
|
+
|
|
8
|
+
Two separate directories, never one inside the other:
|
|
9
|
+
|
|
10
|
+
- **the code**: this project, unpacked from its source distribution, to run and adjust;
|
|
11
|
+
- **the data**: a new git repository with the chains of one application, built from the
|
|
12
|
+
exports. It holds real data and never mixes with the code.
|
|
13
|
+
|
|
14
|
+
Ask the person before anything that is hard to undo (pushing, deleting, `--apply` on many
|
|
15
|
+
files) and number every question so they can answer «1 sim, 2 não».
|
|
16
|
+
|
|
17
|
+
## Part A — the code
|
|
18
|
+
|
|
19
|
+
1. Check what the machine has: `python --version` (3.11 or newer), `git --version`, and
|
|
20
|
+
whether `pip download` reaches PyPI (or the company mirror). npm is only needed to change
|
|
21
|
+
the web UI; the built UI already ships in the package.
|
|
22
|
+
2. Unpack the source and make it a git repository of its own (adjustments made here stay
|
|
23
|
+
here; the next version brings them back from upstream):
|
|
24
|
+
|
|
25
|
+
```bash
|
|
26
|
+
pip download controlm-chain-admin==<version> --no-deps --no-binary :all:
|
|
27
|
+
tar -xzf controlm_chain_admin-<version>.tar.gz
|
|
28
|
+
cd controlm_chain_admin-<version>
|
|
29
|
+
git init -b main && git add -A && git commit -m "chore: controlm-chain-admin <version>"
|
|
30
|
+
python -m venv .venv
|
|
31
|
+
.venv/bin/pip install -e ".[dev,web]" # Windows: .venv\Scripts\pip ...
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
3. Create `.claude/no-real-data.patterns` (git-ignored, one regular expression per line) with
|
|
35
|
+
the names that must never enter this code: the company, its domains, the application
|
|
36
|
+
code, machine and user prefixes. Ask the person for them. The gate scans for them.
|
|
37
|
+
4. Run the gate once: `PATH="$PWD/.venv/bin:$PATH" .claude/skills/verify/verify.sh` (on
|
|
38
|
+
Windows from Git Bash, with `.venv/Scripts`). Without npm the frontend steps are skipped.
|
|
39
|
+
5. From here on the project rules apply (`CLAUDE.md`): TDD, English in the code, no real data
|
|
40
|
+
in this directory, not even in tests.
|
|
41
|
+
|
|
42
|
+
## Part B — the data repository
|
|
43
|
+
|
|
44
|
+
### 1. What the exports say
|
|
45
|
+
|
|
46
|
+
Put the full exports in `exports/<env>/` of the new data directory (they are big: look at
|
|
47
|
+
them with scripts and counts, never whole). Then ask, numbered, what the exports do not say:
|
|
48
|
+
|
|
49
|
+
1. the application (`APPLICATION` of its folders) and the environments with their letters,
|
|
50
|
+
e.g. `test=T,uat=U,bench=B,prod=P`;
|
|
51
|
+
2. the bare prefixes of the letter rule (the application code, `RQ_`, `RC_`) and which of
|
|
52
|
+
them carry the letter on their first token only (`--leading-only RQ_,RC_`: resources such
|
|
53
|
+
as `URQ_P77_AGGREGATION`);
|
|
54
|
+
3. which environment is production (default `prod`).
|
|
55
|
+
|
|
56
|
+
### 2. Production first: it is the trunk
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
mkdir chains && cd chains && git init -b main
|
|
60
|
+
ctmchain init --application <APP> --envs test=T,uat=U,bench=B,prod=P \
|
|
61
|
+
--prefixes P77,RQ_,RC_ --leading-only RQ_,RC_
|
|
62
|
+
ctmchain import ../exports/prod/<prod>.xml --env prod --dedupe-jobnames
|
|
63
|
+
ctmchain export --env prod --jobnames original -o /tmp/prod-check.xml
|
|
64
|
+
cmp /tmp/prod-check.xml ../exports/prod/<prod>.xml && echo "prod: byte for byte"
|
|
65
|
+
git add -A && git commit -m "chore: production baseline"
|
|
66
|
+
```
|
|
67
|
+
|
|
68
|
+
This first commit is the only one made on `main` by hand: from now on `main` mirrors
|
|
69
|
+
production and every change goes through a release.
|
|
70
|
+
|
|
71
|
+
### 3. The other environments, one release
|
|
72
|
+
|
|
73
|
+
```bash
|
|
74
|
+
ctmchain release start baseline
|
|
75
|
+
ctmchain receive ../exports/uat/<uat>.xml --full # preview: the decisions
|
|
76
|
+
```
|
|
77
|
+
|
|
78
|
+
A full export of an environment other than production leaves production alone by default.
|
|
79
|
+
Read the decisions with the person, by section, before `--apply`:
|
|
80
|
+
|
|
81
|
+
- **groups** (the same difference in many jobs, or a machine, user, mail or variable): a new
|
|
82
|
+
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
|
+
|
|
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.
|
|
90
|
+
|
|
91
|
+
### 4. The conventions agreed for these chains
|
|
92
|
+
|
|
93
|
+
- **Machines**: neutral names by order, `node.bat1` (first) and `node.bat2` (second); each
|
|
94
|
+
environment has its own machines behind them.
|
|
95
|
+
- **Users**: `user.<role>_user_proc`, e.g. `user.batch_user_proc`, `user.per_user_proc`. A
|
|
96
|
+
value that fits none: ask whether it is an existing one (`use:<name>`) or a new one (e.g.
|
|
97
|
+
`user.lala_user_proc`).
|
|
98
|
+
- **Mails, transfer (CFT) settings, variables such as the environment code**: one value per
|
|
99
|
+
environment (a constant), which a single job may still change (an exception).
|
|
100
|
+
- **Time windows, MAXWAIT, calendars, file age limits**: the same everywhere; a difference is
|
|
101
|
+
an exception for that job, after asking.
|
|
102
|
+
- **TASKTYPE Dummy, another CMDLINE in one environment**: exceptions of that environment.
|
|
103
|
+
- **Dirty names** (another environment's letter in a condition, a resource of another
|
|
104
|
+
environment, a letter in place of the application's first letter): not fixed on the way
|
|
105
|
+
in. `ctmchain validate` lists them with suggested fixes, to correct in later releases.
|
|
106
|
+
|
|
107
|
+
### 5. Close the baseline
|
|
108
|
+
|
|
109
|
+
```bash
|
|
110
|
+
ctmchain release installed prod # nothing to install: the baseline only describes them
|
|
111
|
+
ctmchain release close
|
|
112
|
+
```
|
|
113
|
+
|
|
114
|
+
Then the everyday flow of the data repository's own `CLAUDE.md` (written by `init`):
|
|
115
|
+
deliveries through `ctmchain receive`, a full prod export now and then to catch up with
|
|
116
|
+
production, releases installed environment by environment.
|
|
@@ -0,0 +1,70 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: fastapi-api
|
|
3
|
+
description: Standards for the local FastAPI server of controlm-chain-admin (src/controlm_chain_admin/web, the [web] extra, `ctmchain serve`). Use for any change to API routers, schemas, dependencies, error handling, OpenAPI metadata or API tests, and when someone asks to «criar endpoint», «expor na API», «servir», or review the API. Thin handlers over the core, typed errors, TestClient tests, OpenAPI snapshot.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# fastapi-api
|
|
7
|
+
|
|
8
|
+
Adapted from Everything Claude Code `fastapi-patterns`, `fastapi-reviewer` and
|
|
9
|
+
`rules/python/fastapi.md` (MIT, see `.claude/ATTRIBUTION.md`). Architecture: `DESIGN.md` §11.
|
|
10
|
+
|
|
11
|
+
## Structure
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
src/controlm_chain_admin/web/
|
|
15
|
+
app.py # create_app(settings) -> FastAPI; lifespan loads ChainRepo; mounts static SPA
|
|
16
|
+
settings.py # repo path, host (127.0.0.1), port, token, open_browser
|
|
17
|
+
deps.py # get_repo() (reloads when files changed), require_token()
|
|
18
|
+
errors.py # ONE exception handler: core typed errors -> HTTP status + {"error","message","detail"}
|
|
19
|
+
schemas/ # Pydantic v2 response/request models, one module per resource
|
|
20
|
+
routers/ # repo.py folders.py jobs.py links.py validate.py changes.py
|
|
21
|
+
static/ # built SPA (web/dist), served at "/" (not committed)
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
## Rules
|
|
25
|
+
|
|
26
|
+
- **Handlers are thin**: parse → call one `core` function → return a schema. No loops over
|
|
27
|
+
folders, no name logic, no file access in a router. If a handler needs a rule, the rule goes
|
|
28
|
+
to `core` and the CLI gets it too.
|
|
29
|
+
- The core is synchronous and in-memory, so handlers are plain `def` (FastAPI runs them in a
|
|
30
|
+
threadpool). Use `async def` only for handlers that await something. Never block an `async`
|
|
31
|
+
handler with file or subprocess calls.
|
|
32
|
+
- `response_model` on every endpoint; separate request, response and list schemas; schemas
|
|
33
|
+
mirror `core` dataclasses (build them with `model_validate(dataclasses.asdict(...))` or
|
|
34
|
+
`from_attributes`), never expose internals the UI does not need.
|
|
35
|
+
- Pagination (`limit`, `offset`) on every list endpoint even if the UI does not use it yet.
|
|
36
|
+
- Errors: `core` typed errors map in `errors.py` only — `NotFound` 404, `NameCollision`/
|
|
37
|
+
`LiteralCollision` 409, `UnresolvedConstant`/`DanglingLink` 422, unexpected 500 with a log
|
|
38
|
+
line and no stack trace in the body. Every error response has the same shape.
|
|
39
|
+
- Mutations (`POST/PUT/DELETE`) are `/api/changes/*`: preview returns a `ChangeSet`, apply takes
|
|
40
|
+
the same `ChangeSet` back. Both require the `X-Ctmchain-Token` header checked by
|
|
41
|
+
`require_token`. `GET` endpoints are read-only and need no token.
|
|
42
|
+
- Bind `127.0.0.1` by default; `--host` must be explicit; CORS off (same origin: the SPA is
|
|
43
|
+
served by the same app). Log the request line, never request bodies.
|
|
44
|
+
- OpenAPI: `title`, `version` from the package, a tag per router with a description, every
|
|
45
|
+
endpoint has `summary` and documented error responses. The schema is the frontend contract
|
|
46
|
+
(`web/` generates TypeScript types from it), so changing it is a visible diff.
|
|
47
|
+
- Configuration via `settings.py` (pydantic-settings) with env prefix `CTMCHAIN_`; no reading
|
|
48
|
+
`os.environ` in routers.
|
|
49
|
+
|
|
50
|
+
## Tests (TDD, like everything else)
|
|
51
|
+
|
|
52
|
+
- `tests/web/conftest.py` builds a fixture chain repo in `tmp_path`, overrides `get_repo` with
|
|
53
|
+
`app.dependency_overrides`, clears it after the test, and yields a `TestClient`.
|
|
54
|
+
- One test module per router; assert status, body shape and the exact values from the
|
|
55
|
+
fixture; one negative test per error mapping; one test proving a mutation without the token
|
|
56
|
+
is 401 and with the token returns the preview.
|
|
57
|
+
- `tests/web/test_openapi.py` compares `app.openapi()` with
|
|
58
|
+
`tests/web/openapi.snapshot.json`; updating the snapshot is a deliberate commit.
|
|
59
|
+
- No network, no real data, no sleeping; the server is never started in tests.
|
|
60
|
+
|
|
61
|
+
## Verify
|
|
62
|
+
|
|
63
|
+
1. `pytest tests/web -q` green, including the OpenAPI snapshot.
|
|
64
|
+
2. Checklist (from `fastapi-reviewer`): no logic in handlers · `response_model` everywhere ·
|
|
65
|
+
one error handler, consistent error shape · token on every mutation · no `async def`
|
|
66
|
+
doing blocking work · list endpoints paginated · settings only via `settings.py` ·
|
|
67
|
+
no secrets, hosts or real names in code or snapshot · docs tags and summaries present.
|
|
68
|
+
3. Start it once by hand: `ctmchain serve --repo <fixture repo> --no-browser`, open
|
|
69
|
+
`/docs`, call two endpoints, stop it.
|
|
70
|
+
4. Finish with the `verify` skill and paste its report in the PR.
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: oss-repo-standards
|
|
3
|
+
description: Open-source repository standards for controlm-chain-admin — README structure, community health files, Conventional Commits, Keep a Changelog, SemVer, issue/PR templates, Trusted Publishing release, naming conventions, bilingual mkdocs. Use when creating or editing README, CONTRIBUTING, CHANGELOG, docs, pyproject metadata, GitHub workflows or templates, when naming a module, command or JSON key, or when someone asks whether the repo «segue os padrões open source».
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# oss-repo-standards
|
|
7
|
+
|
|
8
|
+
Adapted from Everything Claude Code `opensource-packager`, `opensource-sanitizer` and
|
|
9
|
+
`rules/common` (MIT, see `.claude/ATTRIBUTION.md`), and from the author's `xmldiffreport`.
|
|
10
|
+
|
|
11
|
+
## Files every release must have
|
|
12
|
+
|
|
13
|
+
| File | Standard |
|
|
14
|
+
|---|---|
|
|
15
|
+
| `README.md` | See structure below |
|
|
16
|
+
| `LICENSE` | MIT, copyright line with year and author |
|
|
17
|
+
| `CONTRIBUTING.md` | how to set up, run tests, TDD rule, commit format, PR checklist, where to ask |
|
|
18
|
+
| `CODE_OF_CONDUCT.md` | Contributor Covenant 2.1 with a contact address |
|
|
19
|
+
| `SECURITY.md` | supported versions, private reporting channel, response time |
|
|
20
|
+
| `CHANGELOG.md` | Keep a Changelog 1.1: `## [Unreleased]` on top, sections Added/Changed/Fixed/Removed, compare links |
|
|
21
|
+
| `.github/ISSUE_TEMPLATE/bug_report.md`, `feature_request.md`, `.github/PULL_REQUEST_TEMPLATE.md` | PR template includes the `verify` report and the TDD checkbox |
|
|
22
|
+
| `.github/workflows/ci.yml`, `release.yml`, `docs.yml` | CI on push/PR (lint + test matrix 3.11–3.13, macOS/Windows), release on `v*` tags with PyPI Trusted Publishing (OIDC, environment `pypi`), docs to GitHub Pages |
|
|
23
|
+
| `.editorconfig`, `.pre-commit-config.yaml` | ruff, ruff-format, end-of-file, trailing whitespace |
|
|
24
|
+
| `pyproject.toml` | name, version (single source), description, readme, `requires-python`, license, authors, keywords, classifiers (Development Status, Intended Audience, License, OS, Python versions, Topic, Typing :: Typed), `[project.urls]` Homepage/Documentation/Repository/Issues/Changelog, `[project.scripts] ctmchain` |
|
|
25
|
+
| `mkdocs.yml` | mkdocs-material, `mkdocs-static-i18n` with `en` default and `pt`, every page has a `.pt.md` twin |
|
|
26
|
+
|
|
27
|
+
## README structure (in this order)
|
|
28
|
+
|
|
29
|
+
1. Title, one-line tagline, badges: Docs, CI, PyPI version, Python versions, License.
|
|
30
|
+
2. Documentation links (English and Português).
|
|
31
|
+
3. One paragraph: what it is, for whom, the problem it solves (Control-M XML exports are hard
|
|
32
|
+
to manage; JSON in git, links as first-class, environment-neutral base).
|
|
33
|
+
4. Why not X: short comparison with the Control-M UI, Automation API JSON, raw XML + git.
|
|
34
|
+
5. Install (`pip install controlm-chain-admin`, `[web]` extra) and requirements.
|
|
35
|
+
6. Quickstart: `init`, `distinct`, `import`, `validate`, `export`, `serve` in 10 lines, with
|
|
36
|
+
the synthetic fixture so the reader can run it without real data.
|
|
37
|
+
7. Concepts in a small table (chain repository, base, environment, link, constant, override).
|
|
38
|
+
8. Status / roadmap pointer to `BACKLOG.md`, Contributing pointer, Licence.
|
|
39
|
+
|
|
40
|
+
No real data in any example: application `P77`, hosts `srvu-p77-*`, addresses `@example.com`.
|
|
41
|
+
|
|
42
|
+
## Conventions
|
|
43
|
+
|
|
44
|
+
- **Commits**: Conventional Commits (`feat:`, `fix:`, `docs:`, `test:`, `refactor:`, `chore:`,
|
|
45
|
+
`ci:`), imperative, ≤ 72 chars subject, body explains why. Breaking change → `!` and footer.
|
|
46
|
+
- **Versions**: SemVer; `0.x` while the JSON layout may change; tag `vX.Y.Z` = release.
|
|
47
|
+
- **Branches**: `main` protected by CI; short-lived `feat/<topic>`, `fix/<topic>`.
|
|
48
|
+
- **Python names**: package `controlm_chain_admin`, modules and functions `snake_case`,
|
|
49
|
+
classes `PascalCase`, constants `UPPER_SNAKE`, private `_leading_underscore`.
|
|
50
|
+
- **CLI**: command `ctmchain`, subcommands kebab-case (`rename-job`), flags `--kebab-case`.
|
|
51
|
+
- **JSON keys**: attributes mirrored from Control-M XML keep their exact XML name
|
|
52
|
+
(`JOBNAME`, `SUB_APPLICATION`) for fidelity; keys that belong to this tool are
|
|
53
|
+
`snake_case` (`envs`, `links`, `cleanup`, `external`).
|
|
54
|
+
- **Docs**: English first, Portuguese twin, same headings; code blocks copy-pasteable.
|
|
55
|
+
- **Issues/PRs**: labels `bug`, `enhancement`, `docs`, `good first issue`; PR title in
|
|
56
|
+
Conventional Commits form.
|
|
57
|
+
|
|
58
|
+
## Verify
|
|
59
|
+
|
|
60
|
+
1. Run `.claude/skills/oss-repo-standards/check_repo.sh`; it lists missing files/sections and
|
|
61
|
+
checks the last 20 commit subjects against Conventional Commits. All items `OK` or an
|
|
62
|
+
accepted `TODO` with the backlog story that will add it.
|
|
63
|
+
2. `gh api repos/{owner}/{repo}/community/profile --jq .health_percentage` must be `100`
|
|
64
|
+
once the files exist.
|
|
65
|
+
3. Read the README top to bottom as a stranger: can they install and run the quickstart in
|
|
66
|
+
five minutes without asking anything? Every command copy-pasteable and tested.
|
|
67
|
+
4. Grep the docs for anything that looks like real data (see `verify`'s no-real-data phase).
|
|
68
|
+
5. Finish with the `verify` skill and paste its report in the PR.
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# check_repo.sh — open-source hygiene checklist for controlm-chain-admin. Prints OK/MISSING per item.
|
|
3
|
+
set -uo pipefail
|
|
4
|
+
cd "$(git rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
5
|
+
MISSING=0
|
|
6
|
+
ok() { printf 'OK %s\n' "$1"; }
|
|
7
|
+
miss() { printf 'MISSING %s\n' "$1"; MISSING=$((MISSING+1)); }
|
|
8
|
+
has_file() { [ -e "$1" ] && ok "$1" || miss "$1"; }
|
|
9
|
+
has_section() { [ -f "$1" ] && grep -qiE "$2" "$1" && ok "$1: $3" || miss "$1: $3"; }
|
|
10
|
+
|
|
11
|
+
echo "== community files"
|
|
12
|
+
for f in README.md LICENSE CONTRIBUTING.md CODE_OF_CONDUCT.md SECURITY.md CHANGELOG.md \
|
|
13
|
+
.github/ISSUE_TEMPLATE/bug_report.md .github/ISSUE_TEMPLATE/feature_request.md \
|
|
14
|
+
.github/PULL_REQUEST_TEMPLATE.md .github/workflows/ci.yml .github/workflows/release.yml \
|
|
15
|
+
.editorconfig .pre-commit-config.yaml pyproject.toml mkdocs.yml; do has_file "$f"; done
|
|
16
|
+
|
|
17
|
+
echo "== README sections"
|
|
18
|
+
has_section README.md 'img.shields.io|badge' "badges"
|
|
19
|
+
has_section README.md '^## .*(install|instala)' "Install section"
|
|
20
|
+
has_section README.md '^## .*(quick ?start|usage|getting started)' "Quickstart section"
|
|
21
|
+
has_section README.md 'contribut' "Contributing pointer"
|
|
22
|
+
has_section README.md '^## .*licen[cs]e|MIT' "Licence"
|
|
23
|
+
|
|
24
|
+
echo "== changelog / metadata"
|
|
25
|
+
has_section CHANGELOG.md '^## \[Unreleased\]' "Unreleased section"
|
|
26
|
+
has_section pyproject.toml '^\[project.urls\]' "project.urls"
|
|
27
|
+
has_section pyproject.toml 'classifiers' "classifiers"
|
|
28
|
+
has_section pyproject.toml 'ctmchain *=' "ctmchain script"
|
|
29
|
+
has_section mkdocs.yml 'i18n' "bilingual docs (mkdocs-static-i18n)"
|
|
30
|
+
has_section .github/workflows/release.yml 'id-token: *write' "Trusted Publishing (OIDC)"
|
|
31
|
+
|
|
32
|
+
echo "== commits (last 20 subjects, Conventional Commits)"
|
|
33
|
+
BAD="$(git log -20 --format=%s 2>/dev/null | grep -vE '^(feat|fix|docs|test|refactor|chore|ci|build|perf|style|revert)(\([a-z0-9/_-]+\))?!?: .+' || true)"
|
|
34
|
+
if [ -z "$BAD" ]; then ok "all recent subjects follow Conventional Commits"; else printf 'MISSING non-conventional subjects:\n%s\n' "$BAD" | sed 's/^/ /'; MISSING=$((MISSING+1)); fi
|
|
35
|
+
|
|
36
|
+
echo "== GitHub community profile"
|
|
37
|
+
if command -v gh >/dev/null && gh repo view --json nameWithOwner --jq .nameWithOwner >/dev/null 2>&1; then
|
|
38
|
+
REPO="$(gh repo view --json nameWithOwner --jq .nameWithOwner)"
|
|
39
|
+
PCT="$(gh api "repos/$REPO/community/profile" --jq .health_percentage 2>/dev/null || echo '?')"
|
|
40
|
+
[ "$PCT" = "100" ] && ok "community profile 100%" || printf 'INFO community profile: %s%%\n' "$PCT"
|
|
41
|
+
else
|
|
42
|
+
echo "INFO gh not available or not a GitHub repo; skipped"
|
|
43
|
+
fi
|
|
44
|
+
|
|
45
|
+
echo; [ $MISSING -eq 0 ] && echo "RESULT: OK" || { echo "RESULT: $MISSING item(s) missing"; exit 1; }
|
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: python-core-tdd
|
|
3
|
+
description: TDD and coding standards for the Python core and CLI of controlm-chain-admin (src/controlm_chain_admin, tests/). Use for any change to Python code — new feature, bug fix, refactor — and when someone asks to «implementar», «corrigir», «testar» or «refatorar» the core, the parser, the serializer, links, validate or the CLI. Enforces test-first, stdlib-only core, one-core-two-adapters, coverage gate.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# python-core-tdd
|
|
7
|
+
|
|
8
|
+
Adapted from Everything Claude Code `tdd-workflow`, `python-testing`, `python-reviewer` and
|
|
9
|
+
`rules/python` (MIT, see `.claude/ATTRIBUTION.md`). Architecture rules come from `DESIGN.md` §11.
|
|
10
|
+
|
|
11
|
+
## The loop (every change, no exceptions)
|
|
12
|
+
|
|
13
|
+
1. **Red** — write the test in `tests/` that describes the behaviour; run it; it must fail for
|
|
14
|
+
the right reason (not an import error). Name it after the behaviour:
|
|
15
|
+
`test_export_reproduces_bytes_for_untouched_folder`.
|
|
16
|
+
2. **Green** — write the smallest code that passes. No extra features.
|
|
17
|
+
3. **Refactor** — remove duplication, name things, keep functions < 50 lines; tests stay green.
|
|
18
|
+
4. **Commit** test and code together (`feat:`/`fix:`/`test:`), the test file visible in the diff.
|
|
19
|
+
|
|
20
|
+
A bug fix starts with a failing regression test that reproduces it. If a test must change,
|
|
21
|
+
the behaviour changed on purpose and the commit message says so.
|
|
22
|
+
|
|
23
|
+
## Test layout
|
|
24
|
+
|
|
25
|
+
```text
|
|
26
|
+
tests/
|
|
27
|
+
conftest.py # fixture_repo, fixture_xml, tmp chain repos
|
|
28
|
+
fixtures/
|
|
29
|
+
build_fixtures.py # deterministic synthetic export of application P77
|
|
30
|
+
p77_export.xml # committed output of the generator
|
|
31
|
+
unit/test_<module>.py # pure functions, one module per file
|
|
32
|
+
integration/test_cli_*.py # CLI through main(argv), tmp repo, exit codes
|
|
33
|
+
realdata/test_roundtrip.py # @pytest.mark.realdata, skipped unless CTMCHAIN_DATA_REPO is set
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
- Arrange / Act / Assert, one behaviour per test, `pytest.mark.parametrize` for tables.
|
|
37
|
+
- Fixtures over setup code; `tmp_path` for every file write; never write inside the repo.
|
|
38
|
+
- Byte-level assertions for the serializer (`assert out == original_bytes`), not string
|
|
39
|
+
"looks like" checks. Compare with a filtered original when applications are skipped.
|
|
40
|
+
- `realdata` tests read the private clone path from `CTMCHAIN_DATA_REPO`, never a hardcoded
|
|
41
|
+
path, and never print real values on failure beyond counts and positions.
|
|
42
|
+
- Coverage gate: `fail_under = 90` in `pyproject.toml` for `controlm_chain_admin`; adapters
|
|
43
|
+
(`cli.py`, `web/`) count too, so keep them thin.
|
|
44
|
+
|
|
45
|
+
## Architecture rules (checked in review)
|
|
46
|
+
|
|
47
|
+
- `core/` imports only the standard library. Third-party code lives in `web/` (`[web]` extra)
|
|
48
|
+
or dev tooling.
|
|
49
|
+
- `core` functions take a loaded `ChainRepo` and parameters, return frozen dataclasses
|
|
50
|
+
(`ChangeSet`, `Report`, resolved nodes) or raise typed errors (`UnresolvedConstant`,
|
|
51
|
+
`DanglingLink`, `NameCollision`, `LiteralCollision`). Nothing in `core` prints or exits.
|
|
52
|
+
- `cli.py` only parses arguments, calls one `core` function, renders (`text|md|json`), maps
|
|
53
|
+
errors to exit codes (0 ok, 1 findings, 2 usage, 3 error).
|
|
54
|
+
- Writes go through `ops.apply(repo, change)`: all files or none; the preview is the same
|
|
55
|
+
object the user saw.
|
|
56
|
+
- Immutability by default: `@dataclass(frozen=True)`, tuples, `Mapping`; build new objects
|
|
57
|
+
instead of mutating. Document order is kept with lists, never with dict insertion assumptions.
|
|
58
|
+
- Determinism: no `datetime.now()` or randomness in model code; the export comment timestamp
|
|
59
|
+
is a parameter.
|
|
60
|
+
|
|
61
|
+
## Style
|
|
62
|
+
|
|
63
|
+
- Python ≥ 3.11, `from __future__ import annotations` not needed; use `X | None`.
|
|
64
|
+
- Type annotations on every public signature; `mypy src` clean; no `Any` without a comment.
|
|
65
|
+
- `ruff` (line 99; rules E, F, I, UP, B, SIM, C4, RUF) and `ruff format`; no `print` in
|
|
66
|
+
`core`; `logging` for diagnostics; `pathlib` for paths; `with` for every file.
|
|
67
|
+
- Docstrings in English on public modules, classes and functions: one line what, then why if
|
|
68
|
+
non-obvious. Comments explain intent, not mechanics.
|
|
69
|
+
- Errors: catch specific exceptions; never `except: pass`; messages name the folder/job
|
|
70
|
+
identity, never dump a whole element.
|
|
71
|
+
- Encoding: read and write bytes explicitly as UTF-8; never let the platform default decide.
|
|
72
|
+
|
|
73
|
+
## Verify
|
|
74
|
+
|
|
75
|
+
1. `pytest -q` green; `pytest -m realdata` green locally when the change touches parsing,
|
|
76
|
+
serialization or links.
|
|
77
|
+
2. `ruff check . && ruff format --check . && mypy src` clean.
|
|
78
|
+
3. Checklist: test written first (test file in the diff) · no third-party import under
|
|
79
|
+
`core/` · no `print`/`sys.exit` outside `cli.py` · new behaviour has a byte- or
|
|
80
|
+
value-level assertion · errors typed · no real data in tests or fixtures · functions
|
|
81
|
+
< 50 lines, files < 800 lines · CHANGELOG `Unreleased` updated for user-visible changes.
|
|
82
|
+
4. Finish with the `verify` skill and paste its report in the PR.
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: react-frontend
|
|
3
|
+
description: Standards for the read-only web UI of controlm-chain-admin (web/, Vite + React + TypeScript + shadcn/ui, TanStack Query, React Flow). Use for any change to .ts/.tsx files, components, pages, API client, tests or the Vite build, and when someone asks to «criar página», «mostrar na UI», «componente», «grafo» or review the frontend. Test-first with Vitest + Testing Library + MSW, OpenAPI-generated types, accessibility, no template look.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# react-frontend
|
|
7
|
+
|
|
8
|
+
Adapted from Everything Claude Code `react-patterns`, `react-testing`, `vite-patterns`,
|
|
9
|
+
`rules/react`, `rules/typescript` and `rules/web` (MIT, see `.claude/ATTRIBUTION.md`).
|
|
10
|
+
|
|
11
|
+
## Stack and structure
|
|
12
|
+
|
|
13
|
+
```text
|
|
14
|
+
web/
|
|
15
|
+
package.json # scripts: dev, build, lint, typecheck, test, test:e2e, gen:api
|
|
16
|
+
vite.config.ts # /api proxy to http://127.0.0.1:<port> in dev; outDir dist (git-ignored)
|
|
17
|
+
src/
|
|
18
|
+
app/ # router, providers (QueryClient, theme), layout shell
|
|
19
|
+
features/<area>/ # folders/, folder-graph/, job-detail/, validate/ — components + hooks + tests side by side
|
|
20
|
+
components/ui/ # shadcn/ui primitives (generated, do not hand-edit)
|
|
21
|
+
lib/api/ # client.ts (fetch wrapper), types.gen.ts (openapi-typescript output), queries.ts
|
|
22
|
+
lib/ # utils, formatters
|
|
23
|
+
tests/e2e/ # Playwright smoke against the built app served by ctmchain serve
|
|
24
|
+
```
|
|
25
|
+
|
|
26
|
+
- TypeScript `strict`; no `any` (use `unknown` and narrow); types for the API come only from
|
|
27
|
+
`npm run gen:api` (openapi-typescript over the FastAPI schema). Never hand-write an API type.
|
|
28
|
+
- Server state only in TanStack Query (`useQuery`/`useSuspenseQuery` with query keys per
|
|
29
|
+
resource); UI state local with `useState`; derived state computed in render, never duplicated.
|
|
30
|
+
- Components: `PascalCase.tsx`, props as `type Props = {...}` destructured in the signature,
|
|
31
|
+
hooks `useX` in `use-x.ts`, boolean props `isX/hasX/canX`, handlers `handleX`, prop `onX`.
|
|
32
|
+
- Imports: react, third-party, `@/` absolute, relative; `import type` for types.
|
|
33
|
+
- Graph: React Flow (`@xyflow/react`) with `dagre` layout computed in a hook; nodes are
|
|
34
|
+
jobs, folder entry/exit pseudo-nodes for folder-level conditions, edge data carries the link
|
|
35
|
+
(name, ODATE, AND_OR, cleanup, docond, external). Selection drives the inspector panel.
|
|
36
|
+
- Read-only phase: no forms that mutate; every mutation UI comes later through
|
|
37
|
+
`/api/changes/*` with preview → apply.
|
|
38
|
+
|
|
39
|
+
## Design quality (not a default template)
|
|
40
|
+
|
|
41
|
+
- Hierarchy on purpose: the chain graph and the job's execution attributes are the focus;
|
|
42
|
+
metadata recedes. Attributes grouped (identity, scheduling, execution, notifications,
|
|
43
|
+
resources, variables, ON/DO), not a 75-row flat table.
|
|
44
|
+
- Intentional hover/focus/selected states on nodes, edges and rows; keyboard navigation in
|
|
45
|
+
lists and graph (Tab to nodes, Enter opens, Esc closes the inspector).
|
|
46
|
+
- Both light and dark themes read as finished; colour is never the only carrier of meaning
|
|
47
|
+
(external links and findings also get an icon or label).
|
|
48
|
+
- Empty, loading and error states designed for every query; no spinner-only screens.
|
|
49
|
+
|
|
50
|
+
## Tests (TDD)
|
|
51
|
+
|
|
52
|
+
- Vitest + React Testing Library + `@testing-library/user-event` (`userEvent.setup()` once per
|
|
53
|
+
test, always `await`); MSW for `/api/*` with handlers built from fixture JSON, never mocking
|
|
54
|
+
TanStack Query or React internals.
|
|
55
|
+
- Query by role, then label, then text; `data-testid` only for the graph canvas.
|
|
56
|
+
- `findBy*`/`waitFor` for async, never timers; no snapshot tests of components.
|
|
57
|
+
- Accessibility: `vitest-axe` on every page component; no violations.
|
|
58
|
+
- Playwright smoke (`test:e2e`): build, start `ctmchain serve` on a fixture repo, open the
|
|
59
|
+
folder list, open one folder graph, open one job, see the validate page. Runs in CI on
|
|
60
|
+
Linux only.
|
|
61
|
+
- Coverage ≥ 80% for `features/` and `lib/`; `components/ui` excluded.
|
|
62
|
+
|
|
63
|
+
## Verify
|
|
64
|
+
|
|
65
|
+
1. `npm run lint && npm run typecheck && npm test -- --run --coverage && npm run build`.
|
|
66
|
+
2. `npm run gen:api` produces no diff (types in sync with the API snapshot).
|
|
67
|
+
3. Checklist: tests written first · no `any`, no `console.log` · no hand-written API types ·
|
|
68
|
+
roles/labels queryable · axe clean · empty/loading/error states present · keyboard works ·
|
|
69
|
+
both themes checked · bundle report has no unexpected large chunk · no real data in
|
|
70
|
+
fixtures or MSW handlers.
|
|
71
|
+
4. Finish with the `verify` skill and paste its report in the PR.
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: verify
|
|
3
|
+
description: End-of-part quality gate for controlm-chain-admin. Runs lint, types, tests with coverage, the no-real-data scan and a diff review, then writes a PASS/FAIL report. Use at the end of every backlog story, before opening a PR, after a refactor, or whenever someone asks to «verificar», «validar», «está ok?», «run the gate» or «check before PR».
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# verify — the common gate
|
|
7
|
+
|
|
8
|
+
Adapted from Everything Claude Code `verification-loop` (MIT, see `.claude/ATTRIBUTION.md`).
|
|
9
|
+
Every area skill (`python-core-tdd`, `fastapi-api`, `react-frontend`, `oss-repo-standards`)
|
|
10
|
+
ends by invoking this one. The report goes into the PR description.
|
|
11
|
+
|
|
12
|
+
## 1. Run the script
|
|
13
|
+
|
|
14
|
+
```bash
|
|
15
|
+
.claude/skills/verify/verify.sh # whole repo
|
|
16
|
+
.claude/skills/verify/verify.sh --python # only the Python gate
|
|
17
|
+
.claude/skills/verify/verify.sh --web # only the frontend gate
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
The script detects what exists (`pyproject.toml`, `web/package.json`) and runs, in order:
|
|
21
|
+
|
|
22
|
+
| Phase | Python | Frontend |
|
|
23
|
+
|---|---|---|
|
|
24
|
+
| Lint | `ruff check .` and `ruff format --check .` | `npm run lint` |
|
|
25
|
+
| Types | `mypy src` | `npm run typecheck` |
|
|
26
|
+
| Tests | `pytest --cov=controlm_chain_admin --cov-fail-under=<gate>` | `npm test -- --run --coverage` |
|
|
27
|
+
| Build | `python -m build` (when `build` is installed) | `npm run build` |
|
|
28
|
+
| No real data | `git ls-files` grepped with the patterns in `.claude/no-real-data.patterns` (local, git-ignored) | same |
|
|
29
|
+
| Debug leftovers | `breakpoint()`, `pdb` in `src/` | `console.log` in `web/src/` |
|
|
30
|
+
| Diff | `git diff --stat` against the base branch | same |
|
|
31
|
+
|
|
32
|
+
A phase that fails stops the gate with `FAIL`; fix it before continuing. The no-real-data
|
|
33
|
+
patterns file is deliberately not in the repository; when it is missing the phase reports
|
|
34
|
+
`SKIPPED (no patterns file)` and the maintainer must run the gate locally before merging.
|
|
35
|
+
|
|
36
|
+
## 2. Review the diff by hand
|
|
37
|
+
|
|
38
|
+
After the script passes, read `git diff <base>...HEAD` and answer, per file:
|
|
39
|
+
|
|
40
|
+
- Is every change intended and within the story's scope?
|
|
41
|
+
- Does each new behaviour have a test that was written first (test file in the same commit)?
|
|
42
|
+
- Are errors handled explicitly, with typed errors from `core`, never swallowed?
|
|
43
|
+
- Is anything duplicated that `core` already provides?
|
|
44
|
+
- Does any string look like real data (job names with a real application prefix, hostnames,
|
|
45
|
+
user names, e-mail addresses, internal URLs)?
|
|
46
|
+
|
|
47
|
+
## 3. Write the report
|
|
48
|
+
|
|
49
|
+
```text
|
|
50
|
+
VERIFICATION REPORT — <story id> — <date>
|
|
51
|
+
Lint: PASS/FAIL
|
|
52
|
+
Types: PASS/FAIL (n errors)
|
|
53
|
+
Tests: PASS/FAIL (x/y passed, z% coverage, gate g%)
|
|
54
|
+
Build: PASS/FAIL/SKIPPED
|
|
55
|
+
No real data: PASS/FAIL/SKIPPED
|
|
56
|
+
Debug left: PASS/FAIL
|
|
57
|
+
Diff: n files, +a/−b, reviewed by hand: yes/no
|
|
58
|
+
Overall: READY / NOT READY for PR
|
|
59
|
+
Issues:
|
|
60
|
+
1. ...
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
`READY` requires every phase `PASS` (or `SKIPPED` only for Build and, locally run later, No
|
|
64
|
+
real data) and the hand review done. Never report `READY` from memory: paste the script output.
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
#!/usr/bin/env bash
|
|
2
|
+
# verify.sh — the controlm-chain-admin quality gate. Prints a PASS/FAIL report, exits 1 on FAIL.
|
|
3
|
+
# Usage: .claude/skills/verify/verify.sh [--python] [--web] [--base <git-ref>]
|
|
4
|
+
set -uo pipefail
|
|
5
|
+
|
|
6
|
+
ROOT="$(git -C "$(dirname "$0")" rev-parse --show-toplevel 2>/dev/null || pwd)"
|
|
7
|
+
cd "$ROOT"
|
|
8
|
+
RUN_PY=1; RUN_WEB=1; BASE="origin/main"
|
|
9
|
+
while [ $# -gt 0 ]; do
|
|
10
|
+
case "$1" in
|
|
11
|
+
--python) RUN_WEB=0 ;;
|
|
12
|
+
--web) RUN_PY=0 ;;
|
|
13
|
+
--base) BASE="$2"; shift ;;
|
|
14
|
+
*) echo "unknown option: $1" >&2; exit 2 ;;
|
|
15
|
+
esac
|
|
16
|
+
shift
|
|
17
|
+
done
|
|
18
|
+
|
|
19
|
+
declare -a REPORT=(); FAIL=0
|
|
20
|
+
record() { REPORT+=("$(printf '%-14s %s' "$1:" "$2")"); case "$2" in FAIL*) FAIL=1 ;; esac; }
|
|
21
|
+
run() { # run <label> <command...>; keeps the last 25 lines of output on failure
|
|
22
|
+
local label="$1"; shift
|
|
23
|
+
local out; out="$("$@" 2>&1)"; local rc=$?
|
|
24
|
+
if [ $rc -eq 0 ]; then record "$label" "PASS"; else record "$label" "FAIL (exit $rc)"; printf -- '--- %s\n%s\n' "$label" "$(printf '%s\n' "$out" | tail -25)"; fi
|
|
25
|
+
return $rc
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
# ---------------------------------------------------------------- Python
|
|
29
|
+
if [ $RUN_PY -eq 1 ] && [ -f pyproject.toml ]; then
|
|
30
|
+
run "Lint (ruff)" ruff check . && run "Format (ruff)" ruff format --check . || true
|
|
31
|
+
[ -d src ] && run "Types (mypy)" mypy src || true
|
|
32
|
+
GATE="$(sed -n 's/^fail_under *= *\([0-9]*\).*/\1/p' pyproject.toml | head -1)"; GATE="${GATE:-90}"
|
|
33
|
+
run "Tests (pytest)" pytest -q --cov=controlm_chain_admin --cov-report=term-missing --cov-fail-under="$GATE" || true
|
|
34
|
+
if python3 -c "import build" 2>/dev/null; then run "Build (wheel)" python3 -m build --outdir /tmp/ctmchain-build-check || true; else record "Build (wheel)" "SKIPPED (pip install build)"; fi
|
|
35
|
+
if [ -d src ] && grep -rnE "breakpoint\(\)|import pdb|pdb\.set_trace" src >/dev/null 2>&1; then record "Debug left (py)" "FAIL"; grep -rnE "breakpoint\(\)|import pdb|pdb\.set_trace" src; else record "Debug left (py)" "PASS"; fi
|
|
36
|
+
elif [ $RUN_PY -eq 1 ]; then
|
|
37
|
+
record "Python" "SKIPPED (no pyproject.toml)"
|
|
38
|
+
fi
|
|
39
|
+
|
|
40
|
+
# ---------------------------------------------------------------- Frontend
|
|
41
|
+
if [ $RUN_WEB -eq 1 ] && [ -f web/package.json ]; then
|
|
42
|
+
if [ ! -d web/node_modules ]; then
|
|
43
|
+
record "Frontend" "SKIPPED (web/node_modules missing: run npm ci in web/)"
|
|
44
|
+
else
|
|
45
|
+
run "Lint (web)" npm --prefix web run --silent lint || true
|
|
46
|
+
run "Types (web)" npm --prefix web run --silent typecheck || true
|
|
47
|
+
run "Tests (web)" npm --prefix web run --silent test:run -- --coverage || true
|
|
48
|
+
run "Build (web)" npm --prefix web run --silent build || true
|
|
49
|
+
fi
|
|
50
|
+
if grep -rn "console.log" web/src >/dev/null 2>&1; then record "Debug left (web)" "FAIL"; grep -rn "console.log" web/src | head; else record "Debug left (web)" "PASS"; fi
|
|
51
|
+
elif [ $RUN_WEB -eq 1 ]; then
|
|
52
|
+
record "Frontend" "SKIPPED (no web/package.json)"
|
|
53
|
+
fi
|
|
54
|
+
|
|
55
|
+
# ---------------------------------------------------------------- No real data
|
|
56
|
+
PATTERNS=".claude/no-real-data.patterns"
|
|
57
|
+
if [ -f "$PATTERNS" ]; then
|
|
58
|
+
HITS="$(git ls-files -z | xargs -0 grep -nIiE -f "$PATTERNS" -- 2>/dev/null | grep -v "^${PATTERNS}:" || true)"
|
|
59
|
+
if [ -n "$HITS" ]; then record "No real data" "FAIL ($(printf '%s\n' "$HITS" | wc -l | tr -d ' ') hits)"; printf '%s\n' "$HITS" | head -20; else record "No real data" "PASS"; fi
|
|
60
|
+
else
|
|
61
|
+
record "No real data" "SKIPPED (no $PATTERNS; run locally before merging)"
|
|
62
|
+
fi
|
|
63
|
+
|
|
64
|
+
# ---------------------------------------------------------------- Diff
|
|
65
|
+
if git rev-parse --verify -q "$BASE" >/dev/null; then
|
|
66
|
+
record "Diff vs $BASE" "$(git diff --shortstat "$BASE"...HEAD 2>/dev/null | sed 's/^ *//' || echo 'n/a')"
|
|
67
|
+
else
|
|
68
|
+
record "Diff" "base $BASE not found (use --base)"
|
|
69
|
+
fi
|
|
70
|
+
|
|
71
|
+
echo; echo "VERIFICATION REPORT — $(date +%Y-%m-%d) — $(git rev-parse --short HEAD 2>/dev/null || echo 'no commits')"
|
|
72
|
+
printf '%s\n' "${REPORT[@]}"
|
|
73
|
+
if [ $FAIL -eq 1 ]; then echo "Overall: NOT READY"; exit 1; else echo "Overall: READY (hand review of the diff still required)"; fi
|