Skip to content

Project contexts and framework selection

Context detection

Context detection: detect_context() resolves five ProjectContext shapes — experiment, paper, workspace, member, standalone.

The experiment branch is evaluated FIRST, keyed on a root manifest.yaml whose parsed document is a mapping declaring BOTH contract_version and id (invalid YAML, a non-mapping document, a mapping missing either key, or an unreadable file all mean not an experiment — the predicate never raises), so an experiment nested inside a paper itself nested in a uv workspace stays an experiment.

The marker logic is split in two, mirroring the paper shape: a pure predicate over the YAML text and a thin filesystem wrapper reading the root manifest.

The paper branch is evaluated next, keyed on an explicit [tool.axm-lab] pyproject section OR (for a satellite paper carrying no pyproject) the full structural triple PLAN*.md + paper/ + experiments/, all three required, so a paper nested in a uv workspace stays a paper instead of inheriting the Python-packaging rulebook.

Plus find_workspace_root() / get_workspace_members() which delegate uv-workspace resolution to axm_ingot.uv (find_workspace_root / resolve_workspace) and only project the result

Skip and redirect policy

SKIP_BY_CONTEXT / REDIRECT_BY_CONTEXT map every ProjectContext to a frozenset of check ids — a new context is a new row, not a new branch.

validate_context_tables() runs at CheckEngine construction, so an id no discovered check declares raises ValueError up front instead of being a silently inert skip.

The two experiment.* ids are unioned into every OTHER context's skip row, derived from the registry via _category_check_ids("experiment") so startup validation stays green.

The experiment row of REDIRECT_BY_CONTEXT stays empty on purpose: every redirectable id is a packaging check, and the experiment context skips the packaging rulebook outright — _filter_checks evaluates skip before redirect, so a redirect entry there would be dead code

SKIP_BY_CONTEXT[PAPER] is derived, not hand-listed: _known_check_ids() - _PAPER_CHECKS - _EXPERIMENT_CHECKS.

Every Python-packaging id (Trusted Publishing, CI matrix, Diataxis nav, mkdocs, dependabot, py.typed, lock file, classifiers, coverage, ruff/mypy config) is therefore skipped on a paper, and a packaging check added later is skipped the day it lands.

Symmetrically the paper.* ids sit in the standalone, workspace and member rows, and the two experiment.* ids sit in all four non-experiment rows (the paper row included)

Same derivation, one context lower: SKIP_BY_CONTEXT[EXPERIMENT] is the union of _PACKAGING_CHECKS and _PAPER_CHECKS, both registry-derived, so no id is hand-listed and a renamed check is either routed automatically or rejected up front by validate_context_tables().

A folder holding a manifest.yaml is not a Python distribution: pyproject.pyproject_exists, structure.src_layout, structure.py_typed, structure.tests_dir, docs.mkdocs_exists and the rest of the packaging rulebook never run on it, and the paper invariants belong to the paper root ABOVE it.

Only experiment.experiment_structure and experiment.experiment_files are graded — so an experiment freshly scaffolded with --kind experiment is reported in the experiment context with an EMPTY failure list, while its manifest substance (still TODO placeholders) stays axm-lab's business

What a member inherits

CI and shared tooling checks can execute against the workspace root. Others, such as the member's docs dependency group and global reference generator, are skipped. Redirecting a check is different from excluding it.

Context and framework are separate decisions. Framework detection recognizes Python, Node, React and Svelte from project files; context still follows research and uv-workspace markers. A package.json does not by itself define a uv workspace member.

Use grade interpretation and the check catalogue to interpret the selected checks. The canonical check name is also the key used by [tool.axm-init].exclude.