How to Configure Workflow Publish Import Validation¶
Home > How-To > Configure Workflow Publish Import Validation
This guide describes the workflow publish import validation contract now used by Step 15. The command shapes shown below are the current implementation surface.
1. Start from the Current Behavior¶
The current repo state is:
amplifier-bundle/recipes/default-workflow.yamlstill uses Step 15 for Commit and Push- that Step-15 command now writes a staged publish manifest, builds a scoped validation file, prints
seed_count,expanded_local_dep_count, andvalidated_count, then runscheck_imports.py --files-from ...before creating the commit - that same commit call now sets
SKIP=check-importsso pre-commit does not rerun the repo-wide import hook after scoped validation already succeeded scripts/pre-commit/build_publish_validation_scope.pyowns scope buildingscripts/pre-commit/check_imports.pysupports both positional files and--files-fromscoped mode
The validation remains a publish-only substep inserted immediately before the current commit/push step. The workflow numbering stays the same.
2. Start from the Staged Publish Manifest¶
The publish manifest is the source of truth for the validator. It is a newline-delimited UTF-8 file with one repo-relative path per line:
amplifier-bundle/tools/orch_helper.py
src/amplihack/recipes/__init__.py
src/amplihack/recipes/recipe_command.py
Path rules:
- manifest paths are repo-relative and point at files inside the repo
- no absolute paths
- no
..traversal - no control characters
- no directories
- no missing files
3. Derive the Allowed Roots¶
The scope builder should derive an allowlist of roots from the manifest's Python seed files, then expand dependencies only inside those already-allowed roots.
| Seed path shape | Derived root | Boundary rule |
|---|---|---|
src/<package>/... | src/<package>/ | Expansion may stay inside that same top-level package root. |
amplifier-bundle/modules/<name>/... | amplifier-bundle/modules/<name>/ | Expansion stays inside that bundle module unless another root was separately seeded. |
amplifier-bundle/<subtree>/... | amplifier-bundle/<subtree>/ | tools, recipes, and other sibling bundle subtrees stay isolated unless the manifest seeded them too. |
.claude/scenarios/<name>/... | .claude/scenarios/<name>/ only when explicitly seeded | Scenario files stay out of unrelated publishes by default. |
| Other Python files | Closest owning package directory, or the containing script directory | The builder must not escalate to a repo-wide root. |
Important cross-root rule: if a bundle wrapper depends on src/amplihack/..., the manifest must seed that src/amplihack/ root too. The scope builder must not invent hidden aliases between bundle wrappers and runtime packages.
4. Build the Validation Scope¶
The helper script turns the publish manifest into the exact Python file list that the publish-validation stage checks.
python scripts/pre-commit/build_publish_validation_scope.py \
--manifest /tmp/workflow-publish-manifest.txt \
--output /tmp/workflow-publish-scope.txt
Expected output:
The output file should contain repo-relative .py paths only, one per line. Non-Python assets remain part of the publish, but they do not enter import smoke validation.
5. Validate Exactly That Scope¶
The scoped validator looks like this:
--files-from rules:
- mutually exclusive with positional
FILES... - input file is UTF-8 text with one repo-relative
.pypath per line - blank lines are ignored
- comments are not supported
- paths are trimmed, normalized, deduplicated, and rejected if they escape the repo, refer to missing files, name directories, or are not
.py - validation runs against that exact list only; there is no repo-wide fallback
- an empty scope file is a valid "no Python files to check" success
- the later Step-15
git commitmust skip only the pre-commitcheck-importshook so the exact scoped run remains authoritative
Without --files-from, check_imports.py should keep its existing positional behavior for legacy callers.
6. Read the Counts¶
The publish-validation stage reports three counts:
| Field | Meaning |
|---|---|
seed_count | Published .py files taken directly from the manifest. |
expanded_local_dep_count | Additional repo-local Python files pulled in through dependency expansion. |
validated_count | Total files sent to check_imports.py --files-from. |
Use the counts to see whether the scope is too narrow or too broad. A sudden jump in expanded_local_dep_count usually means the staged publish surface now depends on more local modules than before.
7. Know the Fixed Rules¶
These are contract rules, not tuning knobs:
- the publish-validation stage runs before the current commit/push step
.claude/scenarios/**does not block unrelated workflow publishes.textualis only required when a scoped file imports it.amplifier_coreis only required when a scoped file imports it.- relevant missing imports inside the scoped validation set still fail the publish.
- an empty Python validation surface is an explicit success.
- the later Step-15 commit must not rerun the repo-wide
check-importshook.
If you need different behavior, change the workflow contract and its tests together. Do not reintroduce repo-wide scanning or blanket allowlists.
8. Troubleshoot the Common Failures¶
| Symptom | Likely cause | What to do |
|---|---|---|
No module named 'textual' | A scoped file really imports textual. | Install textual or remove the in-scope import. |
No module named 'amplifier_core' | A scoped file really imports amplifier_core. | Install amplifier_core or remove the in-scope import. |
| A scenario script blocks the publish | The scenario script was explicitly selected, or a custom wrapper bypassed scoped mode. | Check the manifest and verify the new stage uses --files-from. |
A bundle helper is missing src/amplihack dependencies | The manifest seeded the bundle root but not the src/amplihack/ root it depends on. | Seed both roots explicitly; do not rely on hidden aliasing. |
| Helper rejects a path | The manifest contains an unsafe, missing, or non-repo path. | Rewrite the manifest with valid repo-relative file paths only. |
validated_count=0 when you expected Python files | The selected publish contains no .py files, or the Python files were not part of the manifest. | Check the manifest contents first. |