diff --git a/.github/scripts/validate-git-hook.sh b/.github/scripts/validate-git-hook.sh new file mode 100755 index 00000000..098921e9 --- /dev/null +++ b/.github/scripts/validate-git-hook.sh @@ -0,0 +1,20 @@ +#!/usr/bin/env bash +# Generated by flext-infra. AI Hub owns Git hook installation. +set -euo pipefail +stage="${1:?Git hook stage is required}" +cd "$(git rev-parse --show-toplevel)" +case "$stage" in + pre-commit|pre-merge-commit) + if ! git diff --quiet --ignore-submodules=none || [ -n "$(git ls-files --others --exclude-standard)" ]; then + printf '%s\n' 'Commit blocked: stage the complete candidate before validation; partial staging is preserved.' >&2 + exit 1 + fi + ;; + pre-push) + test -z "$(git status --porcelain --untracked-files=all --ignore-submodules=none)" + ;; + *) printf 'Unsupported Git hook stage: %s\n' "$stage" >&2; exit 2 ;; +esac +# --all-files avoids pre-commit hook-impl's stash of unstaged changes. +unset SKIP PRE_COMMIT_ALLOW_NO_CONFIG +pre-commit run --all-files --hook-stage "$stage" diff --git a/.gitignore b/.gitignore index c805412f..9686c913 100644 --- a/.gitignore +++ b/.gitignore @@ -36,9 +36,7 @@ */.hooks.json.agents-governance.json .claude/*.agents-governance.json .gemini/*.agents-governance.json -.claude/settings.json .claude/settings.local.json -.gemini/settings.json # Operator-private local config overrides /config/codegen-overrides.local.yaml @@ -359,6 +357,7 @@ CLAUDE.local.md !.github/scripts/ !.github/scripts/check-beads-policy.sh !.github/scripts/check-cqrs-compliance.sh +!.github/scripts/validate-git-hook.sh !.github/scripts/flext-law-loop.sh !tests/fixtures/ !tests/fixtures/ci/ diff --git a/Makefile b/Makefile index 46ac080c..5f2ddc66 100644 --- a/Makefile +++ b/Makefile @@ -149,7 +149,8 @@ endif endif # End SECTION: REPOSITORY_ROOT isolation # === SECTION: verb dispatch (managed) === -# Source: config:make.verbs and the canonical gate vocabulary. +# Source: config:make.verbs and the canonical gate vocabulary. A verb exists +# only in the profiles it declares (make.verbs[].profiles). PUBLIC_VERBS := help setup upg build check test test-full fmt fix fix-enforcement audit status docs clean release-plan release-version release-tag release-build publication gen initialize mod waza duplication sonarcloud-sync BUILTIN_VERBS := help setup upg build check test test-full fmt fix fix-enforcement audit status docs clean release-plan release-version release-tag release-build publication gen initialize mod waza duplication sonarcloud-sync SCRIPT_VERBS := diff --git a/docs/api-reference/generated/overview.md b/docs/api-reference/generated/overview.md index 910fb46e..f654b5f4 100644 --- a/docs/api-reference/generated/overview.md +++ b/docs/api-reference/generated/overview.md @@ -8,27 +8,14 @@ -- Package: `flext_cli` - Version: `0.12.0` - Description: FLEXT CLI - Developer Command Line Interface -- Doc summary: Flext Cli package. -- Classifiers: Development Status :: 3 - Alpha, Intended Audience :: Developers, - Operating System :: OS Independent, Programming Language :: Python :: 3 :: Only, - Programming Language :: Python :: 3.13, Topic :: Software Development :: Libraries :: - Python Modules, Typing :: Typed -- Project class: `domain` -- Keywords: `cli`, `command-line`, `enterprise`, `flext`, `tools`, `typed` -- Main facades: `FlextCli`, `FlextCliAuth`, `FlextCliCli`, `FlextCliCmd`, - `FlextCliCommonParams`, `FlextCliConfig`, `FlextCliConstants`, `FlextCliDocx` (+17 - more) -- Alias exports: `c`, `d`, `e`, `h`, `m`, `p`, `r`, `s`, `t`, `u`, `x` -- Public symbol exports: `FlextCli`, `FlextCliAuth`, `FlextCliCli`, `FlextCliCmd`, - `FlextCliCommonParams`, `FlextCliConfig`, `FlextCliConstants`, `FlextCliDocx`, - `FlextCliFileTools`, `FlextCliFormatters` (+18 more) -- Exported module shortcuts: `config`, `services` -- Generated module pages: `26` +- Governed projects: `0` +- Project classes: _none_ + +Generated from workspace discovery, `pyproject.toml`, public exports, and docstrings. ## Next Pages -- [Public API](public-api.md) -- [Module Index](modules/index.md) +- [Workspace Module Pages](projects/index.md) +- [Project Catalog](../../projects/generated/catalog.md) diff --git a/docs/api-reference/generated/projects/index.md b/docs/api-reference/generated/projects/index.md new file mode 100644 index 00000000..f4555490 --- /dev/null +++ b/docs/api-reference/generated/projects/index.md @@ -0,0 +1,13 @@ +# Workspace Module Pages + + + +- No sections found + + + + + +Each project renders one page per public module, driven by docstrings. + +_No projects discovered._ diff --git a/docs/projects/generated/catalog.md b/docs/projects/generated/catalog.md new file mode 100644 index 00000000..4fe3eba1 --- /dev/null +++ b/docs/projects/generated/catalog.md @@ -0,0 +1,15 @@ +# FLEXT Project Catalog + + + +- No sections found + + + + + +Project links resolve to the generated root API reference for each governed FLEXT +package. + +| project | class | package | description | +|---|---|---|---| diff --git a/mkdocs.yml b/mkdocs.yml index 55b7525b..3d80a16e 100644 --- a/mkdocs.yml +++ b/mkdocs.yml @@ -1,10 +1,9 @@ # AUTO-GENERATED — DO NOT EDIT MANUALLY -site_name: flext-cli Documentation -site_description: Generated documentation for flext-cli +site_name: flext-cli +site_description: Workspace documentation generated from code and curated root docs site_url: https://github.com/flext-sh/flext-cli/blob/main/README.md repo_name: flext-sh/flext-cli repo_url: https://github.com/flext-sh/flext-cli -edit_uri: edit/main/flext-cli/ docs_dir: docs site_dir: .reports/docs/site @@ -88,7 +87,9 @@ markdown_extensions: custom_checkbox: true # MkDocs discovers the complete documentation tree, including maintained pages. -# A fixed three-page nav silently omitted every domain guide and decision record. +# A fixed three-page nav silently omitted guides, standards, and decisions. +not_in_nav: | + api-reference/generated/** validation: omitted_files: warn absolute_links: warn diff --git a/pyproject.toml b/pyproject.toml index 731e91bc..1e3d14e4 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,3 +1,8 @@ +# @flext-generated: continuous +# @flext-owner: flext-infra/config/codegen.yaml + flext-infra/src/flext_infra/templates/project/base/pyproject.toml.j2 +# @flext-adjust: MANAGED=conflict_sections + overwrite_project_keys. CUSTOM=preserve_project_keys and [tool.*] outside conflict_sections. Never edit this projection. +# @flext-regenerate: make gen + [build-system] build-backend = "hatchling.build" requires = ["hatchling"] @@ -131,7 +136,6 @@ exclude_also = [ "if TYPE_CHECKING:", "if typing.TYPE_CHECKING:", ] -fail_under = 45 precision = 2 show_missing = true skip_covered = false diff --git a/tests/unit/test_runtime_process_descendants.py b/tests/unit/test_runtime_process_descendants.py index 942e9291..ee7342a2 100644 --- a/tests/unit/test_runtime_process_descendants.py +++ b/tests/unit/test_runtime_process_descendants.py @@ -64,8 +64,7 @@ def test_normal_root_exit_leaves_no_descendant(self, tmp_path: Path) -> None: ) result = u.Cli().run_to_file( - [sys.executable, "-I", "-S", "-c", parent, str(process_info)], - output_file, + [sys.executable, "-I", "-S", "-c", parent, str(process_info)], output_file ) tm.ok(result) diff --git a/tests/unit/test_utilities_cov.py b/tests/unit/test_utilities_cov.py index 9a1b503c..0ef5863d 100644 --- a/tests/unit/test_utilities_cov.py +++ b/tests/unit/test_utilities_cov.py @@ -1,7 +1,8 @@ """Behavioral tests for the CLI utilities public contract (``u`` / ``u.Cli``). Exercises only the observable contract of the utility helpers: -- ``u.process`` — item processing with predicate filtering and error policy. +- ``u.process`` — item processing with predicate filtering and fail-loud + first-failure propagation. - ``u.Cli.validate_not_empty`` — emptiness validation returning ``r[bool]``. - ``u.Cli.project_names_from_values`` / ``project_numbers_from_values`` — CLI selector normalization. @@ -34,15 +35,25 @@ def test_process_returns_mapped_values_on_success(self) -> None: def test_process_fails_when_processor_raises(self) -> None: """Verify that process fails when processor raises.""" - result = u.process([1, 0], _raise_on_zero, on_error="fail") + result = u.process([1, 0], _raise_on_zero) tm.fail(result) tm.that(result.error or "", has="0") - def test_process_skip_policy_drops_failing_items(self) -> None: - """Verify that process skip policy drops failing items.""" - result = u.process([1, 0, 5], _raise_on_zero, on_error="skip") - tm.ok(result) - tm.that(list(result.unwrap()), eq=[10, 2]) + def test_process_fails_loud_with_original_exception_and_stops_at_first_failure( + self, + ) -> None: + """Verify process stops at the first failure, never visiting later items.""" + visited: list[int] = [] + + def _tracking_raise_on_zero(value: int) -> int: + visited.append(value) + return _raise_on_zero(value) + + result = u.process([1, 0, 5], _tracking_raise_on_zero) + tm.fail(result) + tm.that(visited, eq=[1, 0]) + assert isinstance(result.exception, ValueError) + tm.that(str(result.exception), eq="div zero") def test_process_predicate_excludes_items_before_processing(self) -> None: """Verify that process predicate excludes items before processing.""" diff --git a/uv.lock b/uv.lock index 1c6b249a..281530dd 100644 --- a/uv.lock +++ b/uv.lock @@ -575,7 +575,7 @@ dev = [ [[package]] name = "flext-core" version = "0.12.0" -source = { git = "https://github.com/flext-sh/flext-core.git?rev=0.12.0-dev#d65ba487fd56312f1a5fc809ea478f0413634978" } +source = { git = "https://github.com/flext-sh/flext-core.git?rev=0.12.0-dev#8de52fa6b117ef54a840ac655c4518aff26ba027" } dependencies = [ { name = "annotated-types" }, { name = "beartype" }, @@ -591,7 +591,7 @@ dependencies = [ [[package]] name = "flext-infra" version = "0.12.0" -source = { git = "https://github.com/flext-sh/flext-infra.git?rev=0.12.0-dev#4563c04ac340487ffab47cc55e741ef6c09d7198" } +source = { git = "https://github.com/flext-sh/flext-infra.git?rev=0.12.0-dev#a4898b561147835857017a356006a1f723a39aa3" } dependencies = [ { name = "annotated-types" }, { name = "beartype" }, @@ -617,7 +617,7 @@ dependencies = [ [[package]] name = "flext-tests" version = "0.12.0" -source = { git = "https://github.com/flext-sh/flext-tests.git?rev=0.12.0-dev#ab054bd924f2cfa94459b52c668a0ea6f18beae7" } +source = { git = "https://github.com/flext-sh/flext-tests.git?rev=0.12.0-dev#58ba7e1d88887cbf3c8dc165a437c964e4e23677" } dependencies = [ { name = "annotated-types" }, { name = "beartype" }, @@ -1184,11 +1184,11 @@ wheels = [ [[package]] name = "nodeenv" -version = "1.10.0" +version = "1.11.0" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/24/bf/d1bda4f6168e0b2e9e5958945e01910052158313224ada5ce1fb2e1113b8/nodeenv-1.10.0.tar.gz", hash = "sha256:996c191ad80897d076bdfba80a41994c2b47c68e224c542b48feba42ba00f8bb", size = 55611, upload-time = "2025-12-20T14:08:54.006Z" } +sdist = { url = "https://files.pythonhosted.org/packages/9a/8e/105de02c1322cfada6d9710d9146ef8026419d433c9d08359a2d35805811/nodeenv-1.11.0.tar.gz", hash = "sha256:3ce8fe5b71d16e8af7039ca65257354100bc772965d6bc549070649e53b1b146", size = 99301, upload-time = "2026-09-26T11:29:21.367Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/88/b2/d0896bdcdc8d28a7fc5717c305f1a861c26e18c05047949fb371034d98bd/nodeenv-1.10.0-py2.py3-none-any.whl", hash = "sha256:5bb13e3eed2923615535339b3c620e76779af4cb4c6a90deccc9e36b274d3827", size = 23438, upload-time = "2025-12-20T14:08:52.782Z" }, + { url = "https://files.pythonhosted.org/packages/54/c8/12811c9b48fde162bb72b6f2e78fada9a247a09d7bb5be2050a5d099c77b/nodeenv-1.11.0-py2.py3-none-any.whl", hash = "sha256:edaa16e6c14d7cf395d75d4bbd5a26390f4dc06501a33b4e76282b02cc688a25", size = 34277, upload-time = "2026-09-26T11:29:19.933Z" }, ] [[package]] @@ -1249,11 +1249,11 @@ wheels = [ [[package]] name = "platformdirs" -version = "4.11.14" +version = "4.11.15" source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/72/2075f80b8de9992872715444e7088ec3b6eea2bc0e99521e1d6e562e382d/platformdirs-4.11.14.tar.gz", hash = "sha256:0e706ec0a73a4ec023d11496153a4b6607a672f4b5027c23c3aefa9c9288c523", size = 45301, upload-time = "2026-09-25T17:24:00.877Z" } +sdist = { url = "https://files.pythonhosted.org/packages/5f/e0/7c20b5d0e0f147c40a934e8f9e9f717bd6e3e372c4d58ca5c2fcce2b1652/platformdirs-4.11.15.tar.gz", hash = "sha256:d7419e973b2b740d428200130f80c0e79304d1c71081001db911e15b26a3a6d4", size = 46059, upload-time = "2026-09-26T02:04:58.142Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/dc/40/03f2c094178624d155b8d27a9b0c5ab1e39efa6cc9074b599e8c671b9db1/platformdirs-4.11.14-py3-none-any.whl", hash = "sha256:c1b1a1eab4f4d1b0eb81754593f1c38de88e84ebe6f93bf6714706a6759074b1", size = 26700, upload-time = "2026-09-25T17:23:59.443Z" }, + { url = "https://files.pythonhosted.org/packages/84/83/246d3052d043683cc5f400ab5748bd07a99fbdf311dc771cfad7e3f9e7e0/platformdirs-4.11.15-py3-none-any.whl", hash = "sha256:84508421c77d3c6462e71dd8c0d2776c2ace0e94eb006dc4fd383bc1dcf5a176", size = 26689, upload-time = "2026-09-26T02:04:56.71Z" }, ] [[package]] @@ -1720,8 +1720,8 @@ wheels = [ [[package]] name = "rope" -version = "1.14.0+dc.2" -source = { git = "https://github.com/marlon-costa-dc/rope.git?rev=1.14.0%2Bdc.2#d2571e8cfabe2e00de7783e153403ac0cf4828d4" } +version = "1.14.0+dc.3" +source = { git = "https://github.com/marlon-costa-dc/rope.git?rev=1.14.0%2Bdc.3#64134bf54db1c02ca19602f320d26e1a570e7786" } dependencies = [ { name = "pytoolconfig", extra = ["global"] }, ] @@ -1781,6 +1781,7 @@ wheels = [ name = "rumdl" version = "0.2.77" source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/73/9e/dae5c1b451cddc6d82a9c0c5a84cc7d0a1c5087ae15dd916db89ed4f9bb2/rumdl-0.2.77.tar.gz", hash = "sha256:81a35938965a2153149c1351db13007f146149d49504e3ef81f5f1eb5656d412", size = 4878672, upload-time = "2026-09-26T14:54:23.375Z" } wheels = [ { url = "https://files.pythonhosted.org/packages/c8/c0/15494f04e84fb67a76b5889c9fefe46b96e79aac9757a9f55d2af0c31bc2/rumdl-0.2.77-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:46a86c67dbfc8d4ec7643c0696e69bd0672d4105584b55e436e43982f6e74ab5", size = 6911721, upload-time = "2026-09-23T13:25:47.365Z" }, { url = "https://files.pythonhosted.org/packages/9e/d2/23708cdeadd1b352e988a277d050478eba9504a96b55149e6531990004ea/rumdl-0.2.77-py3-none-macosx_11_0_arm64.whl", hash = "sha256:cef5890e4995e968bf87f1d6b3ec129092204987242fe68c55781c9c90829552", size = 6449218, upload-time = "2026-09-23T13:25:41.335Z" },