From 3fc43809652b564c8f32a881552d6c23293f182e Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 16:16:46 -0300 Subject: [PATCH 1/7] chore(codegen): regenerate CI Makefile gitignore projections Sync managed projections from flext-infra tip (member Beads identity, submodule verify when pin present, mise-action v4.2.3, non-root .agents/ ignore). Generated by make gen WHAT=all APPLY=Y; do not hand-edit. Beads: mro-z75t Co-authored-by: Cursor --- .github/workflows/ci-matrix.yml | 4 ++-- .github/workflows/ci.yml | 3 ++- .gitignore | 5 +++-- Makefile | 19 ++++++++++++++----- 4 files changed, 21 insertions(+), 10 deletions(-) diff --git a/.github/workflows/ci-matrix.yml b/.github/workflows/ci-matrix.yml index 8e975ba13..ec91f53f9 100644 --- a/.github/workflows/ci-matrix.yml +++ b/.github/workflows/ci-matrix.yml @@ -77,7 +77,7 @@ jobs: with: python-version-file: .python-version - name: Install mise toolchain - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: @@ -102,7 +102,7 @@ jobs: with: python-version-file: .python-version - name: Install mise toolchain - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8ac283eff..de0e5d508 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -45,8 +45,9 @@ jobs: submodules: false fetch-depth: 0 + - name: Install mise toolchain - uses: jdx/mise-action@dba19683ed58901619b14f395a24841710cb4925 # v4.1.0 + uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 - name: Install uv uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 diff --git a/.gitignore b/.gitignore index fa926f1d5..8aceff5a3 100644 --- a/.gitignore +++ b/.gitignore @@ -1,7 +1,8 @@ # BLOCK: AI/Agent Local Data (NEVER track these) .claude/* -!.agents/skills/ -!.agents/skills/** + +# BLOCK: Agent surface outside the workspace root +.agents/ # Skill scan reports (transient, regenerated by skill_validate.py) .agents/skills/*/report.json diff --git a/Makefile b/Makefile index f2888bae4..ff318b053 100644 --- a/Makefile +++ b/Makefile @@ -274,9 +274,12 @@ SETUP_ENVIRONMENT_RECIPE = set -eu; \ WORKSPACE_ORCHESTRATE = $(UV_RUN) python -m flext_infra workspace orchestrate REQUESTED_PROJECTS := $(strip $(if $(PROJECT),$(PROJECT),$(PROJECTS))) # A workspace root owns no local gate implementation: its verbs fan out to the -# declared members. Selecting the root here would make it orchestrate itself. +# declared members. Selecting the root (PROJECT=.) would make it orchestrate +# itself forever; map `.` to WORKSPACE_MEMBERS instead of failing closed mid-CI. DEFAULT_PROJECTS := $(WORKSPACE_MEMBERS) . + SELECTED_PROJECTS := $(if $(strip $(REQUESTED_PROJECTS)),$(REQUESTED_PROJECTS),$(DEFAULT_PROJECTS)) + WORKSPACE_PROJECT_ARGS := $(foreach project,$(SELECTED_PROJECTS),--projects $(project)) WORKSPACE_CHECK_ARGS := $(if $(strip $(CHECK_GATES)),--make-arg "CHECK_GATES=$(strip $(CHECK_GATES))") WORKSPACE_TEST_ARGS := $(if $(strip $(FLEXT_PYTEST_FILE_RAW)),--file "$${FLEXT_PYTEST_FILE_RAW}") $(if $(strip $(FLEXT_PYTEST_MATCH_RAW)),--match "$${FLEXT_PYTEST_MATCH_RAW}") $(if $(strip $(FLEXT_PYTEST_WHAT_RAW)),--what "$${FLEXT_PYTEST_WHAT_RAW}") @@ -636,10 +639,16 @@ _builtin_setup_submodules: exit 1; \ fi; \ need_fetch=1; \ - if git -C "$$child_root" rev-parse --verify "$$remote_ref" >/dev/null 2>&1 && \ - git -C "$$child_root" merge-base --is-ancestor "$$gitlink" HEAD && \ - git -C "$$child_root" merge-base --is-ancestor "$$remote_ref" HEAD; then \ - need_fetch=0; \ + if git -C "$$child_root" merge-base --is-ancestor "$$gitlink" HEAD; then \ + if git -C "$$child_root" rev-parse --verify "$$remote_ref" >/dev/null 2>&1; then \ + if git -C "$$child_root" merge-base --is-ancestor "$$remote_ref" HEAD; then \ + need_fetch=0; \ + fi; \ + else \ + # Pin is already present; origin tip may be absent on a shallow CI + # clone. Origin lag must not fail verify (setup never destroys). + need_fetch=0; \ + fi; \ fi; \ if [ "$$need_fetch" -eq 1 ]; then \ git -C "$$child_root" fetch --quiet origin "$$branch" || { \ From 84d20117b27e73546001ed2e6353d1fe436c8e4d Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 16:47:52 -0300 Subject: [PATCH 2/7] chore(codegen): land WHAT=apply and docs workflow projections Regenerate managed Makefile / pre-commit / CI docs surfaces from flext-infra codegen tip (WHAT=apply allowlist, pre-commit/pre-push split, docs.yml). Beads: mro-z75t --- .github/workflows/ci.yml | 11 ++- .github/workflows/docs.yml | 148 +++++++++++++++++++++++++++++++++++++ .pre-commit-config.yaml | 17 ++++- Makefile | 24 +++--- 4 files changed, 185 insertions(+), 15 deletions(-) create mode 100644 .github/workflows/docs.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index de0e5d508..d715abd92 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -28,6 +28,11 @@ permissions: contents: read # End SECTION: permissions +# Why: one active run per workflow and branch prevents obsolete CI from consuming runners. +concurrency: + group: ${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + jobs: # === SECTION: ci job (managed) === # Source: template + config:github_actions.* @@ -57,13 +62,13 @@ jobs: run: make setup - name: gen (blocking) - run: make gen APPLY=Y + run: make gen WHAT=apply APPLY=Y - name: fmt (blocking) - run: make fmt APPLY=Y + run: make fmt WHAT=apply APPLY=Y - name: fix (blocking) - run: make fix APPLY=Y + run: make fix WHAT=apply APPLY=Y - name: check (blocking) run: make check diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml new file mode 100644 index 000000000..e2a316e5d --- /dev/null +++ b/.github/workflows/docs.yml @@ -0,0 +1,148 @@ +# Generated by `flext-infra codegen conform` for flext-cli — DO NOT EDIT. +name: Docs + +on: + push: + branches: + - main + - 0.12.0-dev + paths: + - "docs/**" + - "mkdocs.yml" + - "pyproject.toml" + + - ".github/workflows/docs.yml" + workflow_dispatch: + +permissions: + contents: read + pages: write + id-token: write + +concurrency: + group: pages + cancel-in-progress: false + +jobs: + docs-quality: + name: docs-quality + runs-on: ubuntu-latest + timeout-minutes: 30 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: false + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Install uv + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + with: + enable-cache: true + + - name: Sync workspace toolchain + run: make setup + + # mro-o6h5 (agent: kimi) — failure diagnostics: per-project logs live under + # .reports/workspace/docs/*.log and were invisible in CI ("0 errors" FAILs); + # dump them inline + per-project audit-report.md for non-zero issue counts + + # upload as artifact so the real error is always reachable. + - name: Docs audit (blocking) + run: | + make docs DOCS_PHASE=audit || { + for f in .reports/workspace/docs/*.log; do + echo "=== $f ===" + tail -60 "$f" + done + for r in */.reports/docs/audit-summary.json; do + [ -f "$r" ] || continue + n=$(grep -o '"issues": [0-9]*' "$r" | head -1 | grep -o '[0-9]*') + if [ "${n:-0}" -gt 0 ]; then + echo "=== $(dirname "$r")/audit-report.md ===" + cat "$(dirname "$r")/audit-report.md" + fi + done + exit 1 + } + + - name: Docs generate (materialize contract artifacts) + run: make docs WHAT=generate APPLY=Y + + - name: Docs validate (blocking) + run: | + make docs DOCS_PHASE=validate || { + for f in .reports/workspace/docs/*.log; do + echo "=== $f ===" + tail -60 "$f" + done + exit 1 + } + + # mro-o6h5 (agent: kimi) — include-hidden-files: .reports/ is a dot-dir and + # upload-artifact v4 skips hidden paths by default (artifact silently empty). + - name: Upload docs reports on failure + if: failure() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: docs-reports + path: .reports/ + retention-days: 7 + include-hidden-files: true + + build: + name: build + needs: docs-quality + runs-on: ubuntu-latest + timeout-minutes: 60 + steps: + - name: Checkout + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + submodules: false + fetch-depth: 0 + + - name: Setup Python + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + with: + python-version: "3.13" + + - name: Install uv + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + with: + enable-cache: true + + - name: Sync workspace toolchain + run: make setup + + - name: Generate docs + run: make docs WHAT=generate APPLY=Y + + - name: Build site + run: make docs WHAT=build + + - name: Configure Pages + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 + + - name: Upload site artifact + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + with: + path: .reports/docs/site + + deploy: + name: deploy + needs: build + if: github.ref == 'refs/heads/main' + runs-on: ubuntu-latest + timeout-minutes: 10 + environment: + name: github-pages + url: ${{ steps.deployment.outputs.page_url }} + steps: + - name: Deploy to GitHub Pages + id: deployment + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 9c08a65e9..b746da7c8 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -2,11 +2,22 @@ repos: - repo: local hooks: - - id: flext-canonical-workflow - name: FLEXT canonical workflow + # Why: fast fixes run before each commit; expensive gates run before push. + - id: flext-pre-commit + name: FLEXT pre-commit workflow language: system entry: >- bash -eu -o pipefail -c - 'make gen APPLY=Y && make fmt APPLY=Y && make fix APPLY=Y && make check && make test' + 'make fmt APPLY=Y && make fix APPLY=Y' pass_filenames: false always_run: true + stages: [pre-commit] + - id: flext-pre-push + name: FLEXT pre-push workflow + language: system + entry: >- + bash -eu -o pipefail -c + 'make gen APPLY=Y && make check && make test' + pass_filenames: false + always_run: true + stages: [pre-push] diff --git a/Makefile b/Makefile index ff318b053..dcc2c3519 100644 --- a/Makefile +++ b/Makefile @@ -117,14 +117,14 @@ _ALLOWED_WHATS_deps := check lock upgrade $(shell sed -n 's/^_custom_deps_\([a-z _ALLOWED_WHATS_build := artifacts $(shell sed -n 's/^_custom_build_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_check := all $(shell sed -n 's/^_custom_check_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_test := all $(shell sed -n 's/^_custom_test_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') -_ALLOWED_WHATS_fmt := check all $(shell sed -n 's/^_custom_fmt_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') -_ALLOWED_WHATS_fix := check all $(shell sed -n 's/^_custom_fix_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') +_ALLOWED_WHATS_fmt := check all apply $(shell sed -n 's/^_custom_fmt_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') +_ALLOWED_WHATS_fix := check all apply $(shell sed -n 's/^_custom_fix_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_run := default $(shell sed -n 's/^_custom_run_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_status := diagnostics $(shell sed -n 's/^_custom_status_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_docs := all generate fix audit build validate $(shell sed -n 's/^_custom_docs_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_clean := generated $(shell sed -n 's/^_custom_clean_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_release := status $(shell sed -n 's/^_custom_release_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') -_ALLOWED_WHATS_gen := check all $(shell sed -n 's/^_custom_gen_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') +_ALLOWED_WHATS_gen := check all apply $(shell sed -n 's/^_custom_gen_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') _ALLOWED_WHATS_worktree := list add update remove $(shell sed -n 's/^_custom_worktree_\([a-z0-9_-]*\):.*/\1/p' "$(MAKEFILE_ROOT)/custom.mk" 2>/dev/null | sort -u | tr '\n' ' ') CHECK_GATES_ALLOWED := lint format pyrefly mypy pyright security markdown smells @@ -177,11 +177,11 @@ _DEFAULT_gen := check _DEFAULT_worktree := list _APPLY_WHAT_deps := upgrade -_APPLY_WHAT_fmt := all -_APPLY_WHAT_fix := all +_APPLY_WHAT_fmt := apply +_APPLY_WHAT_fix := apply _APPLY_WHAT_run := default _APPLY_WHAT_clean := generated -_APPLY_WHAT_gen := all +_APPLY_WHAT_gen := apply _APPLY_WHAT_worktree := update @@ -373,7 +373,7 @@ define _run_for_selected_projects done endef -.PHONY: $(PUBLIC_VERBS) $(SERIALIZED_TARGETS) _builtin_help_usage _builtin_setup_environment _builtin_deps_check _builtin_deps_lock _builtin_deps_upgrade _builtin_build_artifacts _builtin_check_all _builtin_test_all _builtin_fmt_check _builtin_fmt_all _builtin_fix_check _builtin_fix_all _builtin_run_default _builtin_status_diagnostics _builtin_docs_all _builtin_docs_generate _builtin_docs_fix _builtin_docs_audit _builtin_docs_build _builtin_docs_validate _builtin_clean_generated _builtin_release_status _builtin_gen_check _builtin_gen_all _builtin_worktree_list _builtin_worktree_add _builtin_worktree_update _builtin_worktree_remove +.PHONY: $(PUBLIC_VERBS) $(SERIALIZED_TARGETS) _builtin_help_usage _builtin_setup_environment _builtin_deps_check _builtin_deps_lock _builtin_deps_upgrade _builtin_build_artifacts _builtin_check_all _builtin_test_all _builtin_fmt_check _builtin_fmt_all _builtin_fmt_apply _builtin_fix_check _builtin_fix_all _builtin_fix_apply _builtin_run_default _builtin_status_diagnostics _builtin_docs_all _builtin_docs_generate _builtin_docs_fix _builtin_docs_audit _builtin_docs_build _builtin_docs_validate _builtin_clean_generated _builtin_release_status _builtin_gen_check _builtin_gen_all _builtin_gen_apply _builtin_worktree_list _builtin_worktree_add _builtin_worktree_update _builtin_worktree_remove $(filter-out setup $(SERIALIZED_VERBS),$(PUBLIC_VERBS)): $(call _dispatch,$@) @@ -645,8 +645,8 @@ _builtin_setup_submodules: need_fetch=0; \ fi; \ else \ - # Pin is already present; origin tip may be absent on a shallow CI - # clone. Origin lag must not fail verify (setup never destroys). + # Pin is already present; origin tip may be absent on a shallow CI \ + # clone. Origin lag must not fail verify (setup never destroys). \ need_fetch=0; \ fi; \ fi; \ @@ -795,6 +795,8 @@ _builtin_fmt_all: _builtin_require_environment $(call _require_apply) @$(UV_RUN) ruff format $(RUFF_PATHS) +_builtin_fmt_apply: _builtin_fmt_all + _builtin_fix_check: @printf 'ERROR: make fix requires APPLY=Y\n' >&2; exit 2 @@ -802,6 +804,8 @@ _builtin_fix_all: _builtin_require_environment $(call _require_apply) @$(UV_RUN) ruff check --fix $(RUFF_PATHS) +_builtin_fix_apply: _builtin_fix_all + _builtin_run_default: _builtin_require_environment @$(UV_RUN) $(PROJECT_NAME) $(ARGS) @@ -878,6 +882,8 @@ _builtin_gen_all: _builtin_require_environment @$(PROJECT_FLEXT_INFRA) deps modernize --workspace "$(PROJECT_ROOT)" --apply @$(PROJECT_FLEXT_INFRA) deps extra-paths --workspace "$(PROJECT_ROOT)" --apply +_builtin_gen_apply: _builtin_gen_all + _builtin_worktree_list: @$(PROJECT_FLEXT_INFRA) workspace worktree --workspace "$(WORKSPACE)" --operation list From c6a1f0a6b0dad123797ce07860ea35e62ba5b078 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 16:58:02 -0300 Subject: [PATCH 3/7] fix(ci): install managed Git hooks --- .github/scripts/install-git-hooks.sh | 109 +++++++++++++++++++++++++++ 1 file changed, 109 insertions(+) create mode 100755 .github/scripts/install-git-hooks.sh diff --git a/.github/scripts/install-git-hooks.sh b/.github/scripts/install-git-hooks.sh new file mode 100755 index 000000000..3619a0605 --- /dev/null +++ b/.github/scripts/install-git-hooks.sh @@ -0,0 +1,109 @@ +#!/usr/bin/env bash +# Owner-Skill: .agents/skills/scripts-validation/SKILL.md +# install-git-hooks.sh — Install the Beads git hooks at the workspace root and +# apply the FLEXT agent-trailer guard. +# +# Canonical owner of git-hook provisioning for this workspace. Reproducible and +# idempotent: safe to run repeatedly and after every `bd hooks install`. +# +# Why the guard: +# FLEXT law (R5 / ai-hub agent-law §12) forbids agent attribution trailers by +# default. The Beads `prepare-commit-msg` shim's sole job is adding those +# trailers, so it must be gated behind an explicit opt-in: +# BD_ALLOW_AGENT_COMMIT_TRAILERS=1 +# `.github/scripts/check-beads-policy.sh` enforces the guard text is present +# in the installed hook; `make check WHAT=coordination` fails without it. +# +# Mechanism: +# `bd hooks install --chain` writes bd-managed sections between markers and +# preserves any content OUTSIDE those markers across installs/upgrades. This +# script re-applies bd's install, then injects the guard block above the bd +# `--- BEGIN BEADS INTEGRATION ---` marker so it survives future bd installs. +# +# Usage: +# make hooks +# .github/scripts/install-git-hooks.sh [--verbose] + +set -euo pipefail + +VERBOSE="${1:-}" +WORKSPACE_ROOT="$(cd "$(dirname "$0")/../.." && pwd)" +cd "${WORKSPACE_ROOT}" + +_log() { + if [[ "${VERBOSE}" == "--verbose" ]]; then + echo "[INFO] $*" + fi +} + +fail() { + printf 'install-git-hooks: %s\n' "$*" >&2 + exit 1 +} + +command -v pre-commit >/dev/null 2>&1 || fail "pre-commit is not installed; install it before provisioning hooks" +command -v bd >/dev/null 2>&1 || fail "bd is not installed; install Beads before provisioning hooks" + +# Why: install both staged workflow entry points before Beads chains its guard. +_log "Installing pre-commit and pre-push hooks at ${WORKSPACE_ROOT}" +pre-commit install -t pre-commit -t pre-push >/dev/null \ + || fail "pre-commit hook installation failed" +_log "Installing Beads git hooks (chained) at ${WORKSPACE_ROOT}" +bd hooks install --chain >/dev/null || fail "bd hooks install --chain failed" + +hook_path="$(git rev-parse --git-path hooks/prepare-commit-msg)" +[ -f "${hook_path}" ] || fail "prepare-commit-msg hook missing after bd hooks install" + +_log "Applying FLEXT agent-trailer guard to ${hook_path}" +GUARD_TOKEN="BD_ALLOW_AGENT_COMMIT_TRAILERS" python3 - "${hook_path}" <<'PY' +import os +import pathlib +import sys + +token = os.environ["GUARD_TOKEN"] +path = pathlib.Path(sys.argv[1]) +text = path.read_text() + +if token in text: + # Guard already present (idempotent): nothing to do. + sys.exit(0) + +guard = ( + "# --- BEGIN FLEXT AGENT-TRAILER GUARD ---\n" + "# Managed by .github/scripts/install-git-hooks.sh — do not hand-edit.\n" + "# FLEXT law (R5): prepare-commit-msg must NOT add agent attribution\n" + "# trailers unless the user opts in with BD_ALLOW_AGENT_COMMIT_TRAILERS=1.\n" + "# The Beads shim below only adds trailers, so gate it here.\n" + 'if [ "${BD_ALLOW_AGENT_COMMIT_TRAILERS:-0}" != "1" ]; then\n' + " exit 0\n" + "fi\n" + "# --- END FLEXT AGENT-TRAILER GUARD ---\n" +) + +lines = text.splitlines(keepends=True) +marker = "# --- BEGIN BEADS INTEGRATION" +insert_at = next( + (i for i, line in enumerate(lines) if line.startswith(marker)), + None, +) +if insert_at is None: + raise SystemExit( + "beads integration marker not found; cannot place guard deterministically" + ) + +# Insert the guard immediately before the bd-managed section (outside markers, +# so `bd hooks install` preserves it on future upgrades). +lines[insert_at:insert_at] = [guard] +path.write_text("".join(lines)) +PY + +grep -q 'BD_ALLOW_AGENT_COMMIT_TRAILERS' "${hook_path}" \ + || fail "guard token missing after injection" +grep -q 'bd hooks run prepare-commit-msg' "${hook_path}" \ + || fail "bd delegation missing; refusing to leave hook without beads integration" +[ -f "$(git rev-parse --git-path hooks/pre-commit)" ] \ + || fail "pre-commit hook missing after provisioning" +[ -f "$(git rev-parse --git-path hooks/pre-push)" ] \ + || fail "pre-push hook missing after provisioning" + +echo "install-git-hooks: prepare-commit-msg guarded (BD_ALLOW_AGENT_COMMIT_TRAILERS opt-in)" From 19181b77804155954434a9648d4fe39f1a26e621 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 17:10:23 -0300 Subject: [PATCH 4/7] chore(gen): land CI/docs workflow fixed-point projections Regenerate managed GitHub workflows from flext-infra tip (quoted on, action comment spacing, private_submodules include slot). Beads: mro-z75t --- .github/workflows/ci.yml | 1 + .github/workflows/docs.yml | 26 +++++++++++++++----------- 2 files changed, 16 insertions(+), 11 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d715abd92..856de137d 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -51,6 +51,7 @@ jobs: fetch-depth: 0 + - name: Install mise toolchain uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index e2a316e5d..bfdb1858b 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -1,7 +1,7 @@ # Generated by `flext-infra codegen conform` for flext-cli — DO NOT EDIT. name: Docs -on: +"on": push: branches: - main @@ -30,18 +30,20 @@ jobs: timeout-minutes: 30 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: false fetch-depth: 0 + + - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" - name: Install uv - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true @@ -87,7 +89,7 @@ jobs: # upload-artifact v4 skips hidden paths by default (artifact silently empty). - name: Upload docs reports on failure if: failure() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 with: name: docs-reports path: .reports/ @@ -101,18 +103,20 @@ jobs: timeout-minutes: 60 steps: - name: Checkout - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 with: submodules: false fetch-depth: 0 + + - name: Setup Python - uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 + uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: python-version: "3.13" - name: Install uv - uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 + uses: astral-sh/setup-uv@c771a70e6277c0a99b617c7a806ffedaca235ff9 # v9.0.0 with: enable-cache: true @@ -126,10 +130,10 @@ jobs: run: make docs WHAT=build - name: Configure Pages - uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 + uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0 - name: Upload site artifact - uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 + uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0 with: path: .reports/docs/site @@ -145,4 +149,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deployment - uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 + uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0 From 01a0bd678b5121883992dbff5a10e6ab3c309395 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 17:20:38 -0300 Subject: [PATCH 5/7] chore(gen): land blank-line fixed-point and pytest timeout projections Regenerate managed CI/docs workflows and Makefile after flext-infra private_submodules endif trim and tooling pytest timeout SSOT. Beads: mro-z75t --- .github/workflows/ci.yml | 2 -- .github/workflows/docs.yml | 4 ---- Makefile | 2 +- 3 files changed, 1 insertion(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 856de137d..0e32ef65f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -50,8 +50,6 @@ jobs: submodules: false fetch-depth: 0 - - - name: Install mise toolchain uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3 diff --git a/.github/workflows/docs.yml b/.github/workflows/docs.yml index bfdb1858b..383975763 100644 --- a/.github/workflows/docs.yml +++ b/.github/workflows/docs.yml @@ -35,8 +35,6 @@ jobs: submodules: false fetch-depth: 0 - - - name: Setup Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: @@ -108,8 +106,6 @@ jobs: submodules: false fetch-depth: 0 - - - name: Setup Python uses: actions/setup-python@5fda3b95a4ea91299a34e894583c3862153e4b97 # v7.0.0 with: diff --git a/Makefile b/Makefile index dcc2c3519..36bc3cbca 100644 --- a/Makefile +++ b/Makefile @@ -48,7 +48,7 @@ BRANCH ?= PYTEST_ARGS ?= PYTEST_DIAG_ARGS ?= -rA --durations=0 --tb=long --showlocals PYTEST_REPORT_ARGS ?= -ra --durations=25 --durations-min=0.001 --tb=short -PYTEST_PROCESS_TIMEOUT_SECONDS ?= 60 +PYTEST_PROCESS_TIMEOUT_SECONDS ?= 360 # mro-99ae: the pytest process inherits a hard wall-clock boundary, mirroring # MYPY_BOUNDED, so a hung run is terminated even if the typed runner stalls. PYTEST_BOUNDED = timeout --signal=TERM --kill-after=5s "$(PYTEST_PROCESS_TIMEOUT_SECONDS)s" From 29a2280a12de7207e738e0ab85d9f75973268bea Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 17:56:27 -0300 Subject: [PATCH 6/7] chore(gen): land docs apply-guarded Makefile projections Regenerate Makefile after flext-infra declared docs apply-guarded and serialized so make docs WHAT=generate APPLY=Y is accepted. Beads: mro-z75t --- Makefile | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/Makefile b/Makefile index 36bc3cbca..d3adeb384 100644 --- a/Makefile +++ b/Makefile @@ -130,8 +130,8 @@ _ALLOWED_WHATS_worktree := list add update remove $(shell sed -n 's/^_custom_wor CHECK_GATES_ALLOWED := lint format pyrefly mypy pyright security markdown smells CHECK_GATES_DEFAULT := lint pyrefly mypy pyright security markdown smells DOCS_ACTIONS := generate fix audit build validate -SERIALIZED_VERBS := check test gen fmt fix deps clean worktree -SERIALIZED_TARGETS := _serialized_check _serialized_test _serialized_gen _serialized_fmt _serialized_fix _serialized_deps _serialized_clean _serialized_worktree +SERIALIZED_VERBS := check test gen fmt fix deps clean worktree docs +SERIALIZED_TARGETS := _serialized_check _serialized_test _serialized_gen _serialized_fmt _serialized_fix _serialized_deps _serialized_clean _serialized_worktree _serialized_docs # End SECTION: verb dispatch # === SECTION: lint/type paths (managed) === @@ -180,6 +180,7 @@ _APPLY_WHAT_deps := upgrade _APPLY_WHAT_fmt := apply _APPLY_WHAT_fix := apply _APPLY_WHAT_run := default +_APPLY_WHAT_docs := generate _APPLY_WHAT_clean := generated _APPLY_WHAT_gen := apply _APPLY_WHAT_worktree := update @@ -435,6 +436,13 @@ _serialized_worktree: $(call _dispatch,worktree) +docs: _builtin_require_environment + @$(PROJECT_FLEXT_INFRA) workspace serialize-make --workspace "$(PROJECT_ROOT)" --makefile "$(SELF_MAKEFILE)" --verb "docs" --selector-value "$(WHAT)" --apply-token "$(APPLY)" + +_serialized_docs: + $(call _dispatch,docs) + + # `setup` keeps its own recipe (it must not require the environment it is about # to build), but it still runs the pre-/post-setup lifecycle hooks so a project @@ -494,7 +502,7 @@ _builtin_help_usage: - @printf ' %-10s WHAT=%s\n' 'docs' "$$(printf '%s' '$(_ALLOWED_WHATS_docs)' | awk '{$$1=$$1; gsub(/ /, "|"); print}')"; + @printf ' %-10s WHAT=%s APPLY=Y\n' 'docs' "$$(printf '%s' '$(_ALLOWED_WHATS_docs)' | awk '{$$1=$$1; gsub(/ /, "|"); print}')"; From 208d3900196cb9758afb77b50b42a32314c3bd96 Mon Sep 17 00:00:00 2001 From: Marlon Costa Date: Mon, 3 Aug 2026 17:57:22 -0300 Subject: [PATCH 7/7] chore(deps): adopt Dependabot upper-bound bumps (cachetools, rich) --- pyproject.toml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pyproject.toml b/pyproject.toml index 41698af1c..6b4bf0a46 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -18,7 +18,7 @@ classifiers = [ "Typing :: Typed", ] dependencies = [ - "cachetools>=6.2,<7.0", + "cachetools>=6.2,<8.0", "click>=8.3.3", "defusedxml>=0.7.1", "flext-core @ git+https://github.com/flext-sh/flext-core.git@0.12.0-dev", @@ -33,7 +33,7 @@ dependencies = [ "python-docx>=1.1.2", "python-pptx>=1.0.2", "pyyaml>=6.0.3", - "rich>=14,<15", + "rich>=14,<16", "ruamel.yaml>=0.18.16", "tabulate>=0.10.0", "tomlkit>=0.14.0",