Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci-matrix.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand All @@ -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:
Expand Down
2 changes: 0 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,6 @@ jobs:
submodules: false
fetch-depth: 0



- name: Install mise toolchain
uses: jdx/mise-action@9e7f7633ff6f6d6048a9418a68d48f288f50eb14 # v4.2.3

Expand Down
148 changes: 148 additions & 0 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
@@ -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
5 changes: 3 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -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
Expand Down
55 changes: 39 additions & 16 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down Expand Up @@ -117,21 +117,21 @@ _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
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) ===
Expand Down Expand Up @@ -177,11 +177,12 @@ _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_docs := generate
_APPLY_WHAT_clean := generated
_APPLY_WHAT_gen := all
_APPLY_WHAT_gen := apply
_APPLY_WHAT_worktree := update


Expand Down Expand Up @@ -274,9 +275,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}")
Expand Down Expand Up @@ -370,7 +374,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,$@)
Expand Down Expand Up @@ -432,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
Expand Down Expand Up @@ -491,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}')";



Expand Down Expand Up @@ -636,10 +647,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" || { \
Expand Down Expand Up @@ -786,13 +803,17 @@ _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

_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)
Expand Down Expand Up @@ -869,6 +890,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

Expand Down
4 changes: 2 additions & 2 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand All @@ -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",
Expand Down
Loading