T-B1Y3-test-scaffold-sys-modules-registration
Status: closed/superseded · Impact: low · Complexity: small
Auto-generated from a /sdlc:task-work post-mortem. Review and
promote to open/ready before picking up.
Per-skill test files that load their target script via
importlib.util.spec_from_file_location fail with a confusing
AttributeError: 'NoneType' object has no attribute '__dict__'
when the target script uses @dataclass, because Python 3.14’s
dataclass machinery now looks the module up in sys.modules
during decoration. The fix is a one-line
sys.modules[name] = mod before exec_module, but the error
surface is opaque enough that the next contributor will spend the
same debugging time. Standardize the loader pattern so the
registration is built in. Cited by
T-A2C6-spawn-from-post-mortem-stronger-dedup.
plugin/skills/task-work/test_start_task.py uses the bare
importlib loader without sys.modules registration, and gets
away with it only because start_task.py has no dataclasses.
plugin/skills/task-work/test_dedup_search.py (added in the same
PR) registers manually after hitting the failure. No central
helper exists — every new test file copy-pastes the loader.
Proposed
Section titled “Proposed”A small helper — either a function in a shared test-support
module under plugin/skills/ or an inlined snippet documented as
canonical — that wraps the spec_from_file_location →
module_from_spec → sys.modules[name] = mod → exec_module
sequence so the registration cannot be omitted. Update existing
test files to use it.
Approach
Section titled “Approach”- Decide the shape: a shared helper module
(e.g.
plugin/skills/_test_support/load_module.py) vs an inline pattern documented in a CLAUDE.md underplugin/skills/. The shared module is cleaner but adds an import path; the documented pattern is friction-free but relies on copy-paste discipline. - Implement whichever shape wins; cover the dataclass-bearing case in a self-test.
- Migrate
test_start_task.pyandtest_dedup_search.pyto use the helper. Spot-check by deleting their dataclass workaround and confirming the helper still passes the suite.
Files to touch
Section titled “Files to touch”plugin/skills/_test_support/load_module.py(new) — orplugin/skills/CLAUDE.mdwith a documented snippet.plugin/skills/task-work/test_dedup_search.py— switch to the helper.plugin/skills/task-work/test_start_task.py— switch to the helper for consistency.
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: A test file that loads a target script containing
@dataclass(frozen=True)via the helper does not raiseAttributeErroron Python 3.14. - AC-2: Existing tests (
test_start_task.py,test_dedup_search.py) keep passing after migration.
Out of scope
Section titled “Out of scope”- Migrating every importlib-loaded test in the plugin in one PR. Two callers is enough to validate the helper shape; the rest can convert as they’re touched.
- Switching to
pytestor another test framework that hides the loader detail entirely — that’s a bigger architectural conversation.
Dependencies
Section titled “Dependencies”- none
Discovery context
Section titled “Discovery context”Spawned by /sdlc:task-work post-mortem of T-A2C6-spawn-from-post-mortem-stronger-dedup on 2026-05-21.
Dedup search (spawn-from-post-mortem)
Section titled “Dedup search (spawn-from-post-mortem)”Bullet: importlib.util.spec_from_file_location + dataclass raised AttributeError on Python 3.14 because dynamically-loaded module wasnt registered in sys.modules before exec — per-skill test scaffolding should standardize the sys.modules registration step Keywords searched: spec_from_file_location, dynamically-loaded, attributeerror, registration, scaffolding, standardize, registered, importlib Top candidates (score / status / headline):
- 11 / in-progress / 2026-05-21-spawn-from-post-mortem-stronger-dedup — Strengthen spawn-from-post-mortem dedup and record the search trail (self-match; excluded from decision)
- 3 / closed/partially-superseded / 2026-05-19-task-new-flags-cascading-schema-edits — Flag cascading schema-aware script/skill edits in task scaffolding guidance
- 3 / in-progress / 2026-05-20-task-work-preflight-permissions-probe — /sdlc:task-work pre-flight should probe for tool-family permission allowlists
- 2 / closed/done / 2026-05-19-new-scripts-derive-patterns-from-schema — Derive wikilink/id patterns from schema in new_*.py scripts
- 1 / closed/done / 2026-05-19-add-orchestrate-skill — Add /sdlc:orchestrate skill — one-tick reconcile + dispatch loop Decision: SPAWNED Rationale: Top candidate is the originating task itself (self-match). Excluding it, the next-best score is 3, well below the min-score floor of 6, and unrelated to importlib/sys.modules. The self-match exposure is itself tracked in T-HC03-dedup-search-excludes-originating-task.