Skip to content

fix(extensions): ignore non-mapping extension.yml config section - #4323

Open
Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/extension-config-defaults-non-mapping
Open

Noor-ul-ain001 wants to merge 1 commit into
github:mainfrom
Noor-ul-ain001:fix/extension-config-defaults-non-mapping

Conversation

@Noor-ul-ain001

@Noor-ul-ain001 Noor-ul-ain001 commented Aug 25, 2026

Copy link
Copy Markdown
Contributor

Summary

  • ConfigManager._get_extension_defaults() read an extension's extension.yml config.defaults via manifest_data.get("config", {}).get("defaults", {}) with no shape check on the intermediate config value. Shipped extensions (e.g. extensions/git/extension.yml) use a top-level config: { defaults: {...} } section — distinct from the already-validated provides.config list. A manifest with config: [] or config: "oops" made the chained .get() raise a bare AttributeError instead of degrading gracefully like every other malformed config source in this class.
  • That crash was silently swallowed by should_execute_hook's blanket except Exception, so a hook's config.x is set condition permanently evaluated to False for the extension with no diagnostic — a hook that should run (or should raise a clear error) instead silently never fires.
  • Mirrors the existing TestConfigManagerNonMappingYaml coverage for a non-mapping root of <id>-config.yml (already fixed), one level deeper in the manifest's own config section, which was previously unchecked. ExtensionManifest._validate() does not validate this top-level config field at all.

This is not a manifest rejection. The fix does not refuse to load, or error out on, a manifest with a malformed config section — the extension still loads normally and every other manifest-driven feature (commands, hooks, skills) keeps working. Only the malformed config/config.defaults section itself is ignored, falling back to {} (no defaults), the same graceful-degradation behavior already used by every other malformed config source in ConfigManager.

Test plan

  • Added TestConfigManagerNonMappingManifestConfigSection to tests/test_extensions.py (5 tests: list config:, scalar config:, non-mapping config.defaults, valid-shape regression guard, and a HookExecutor._evaluate_condition non-crash check)
  • Verified all 4 crash-reproducing tests fail without the fix (AttributeError: 'list'/'str' object has no attribute 'get') and pass with it — stashed only the source change (src/specify_cli/extensions/__init__.py), confirmed the failure, then restored it and confirmed the pass
  • Ran tests/test_extensions.py full suite — 505 passed, 20 pre-existing failures (Windows symlink-elevation and env-specific; reproduced identically on the unmodified branch), 8 skipped — no regressions

AI Disclosure

  • I did use AI assistance (describe below)

This PR was authored with Claude Code (model: Claude Sonnet 5), used in its interactive, agentic CLI mode with full local tool access (file read/edit, running pytest, git). Extent of assistance: the AI agent identified the bug, wrote the source fix and all 5 regression tests, and ran the test-the-test verification and full suite described above, under my direction. I reviewed the resulting diff and test output myself before opening this PR and understand the change being made.

Co-Authored-By: Claude Sonnet 5 noreply@anthropic.com

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🟢 Approval recommended

The focused defensive fix is correct and comprehensively tested.

Pull request overview

Prevents malformed extension manifest configuration from crashing config and hook resolution.

Changes:

  • Safely ignores non-mapping config and config.defaults values.
  • Adds regression coverage for malformed and valid manifest configurations.
File summaries
File Description
src/specify_cli/extensions/__init__.py Validates manifest configuration shapes before reading defaults.
tests/test_extensions.py Tests malformed configuration and hook evaluation behavior.
Review details
  • Files reviewed: 2/2 changed files
  • Comments generated: 0
  • Review effort level: Balanced

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@mnriem mnriem left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please fix test & lint errors

@Noor-ul-ain001

Copy link
Copy Markdown
Contributor Author

@mnriem I looked into the CI failure on the last run before pushing anything.

  • ruff passed on that run — there were no lint errors.
  • The only real failure was pytest (windows-latest, 3.13): a subprocess.TimeoutExpired in tests/extensions/test_extension_agent_context.py::TestBundledUpdaterPathValidation::test_powershell_script_discovers_nested_plan (job log). That test never touches extension config resolution — it's unrelated to this PR's diff.
  • The two macOS pytest jobs show cancelled, not failure (matrix fail-fast after the Windows job failed).
  • I confirmed this same Windows timeout also fails on main itself, on an unrelated commit: run from 2026-09-11. So it's pre-existing CI flakiness in the PowerShell subprocess test, not something introduced here.

The branch was also 87 commits behind main, so I rebased it onto current main (clean, no conflicts) and re-verified locally: ruff check . passes, and the full tests/test_extensions.py suite passes (the only 2 local failures are TestExtensionConfigScaffolding symlink tests that need Windows admin elevation to create symlinks — an environmental gap on my machine, not a code issue). Pushed the rebase; let's see what CI reports now.

@mnriem

mnriem commented Sep 15, 2026

Copy link
Copy Markdown
Collaborator

Your explanation of the earlier CI failure checks out: lint passed, and the same PowerShell timeout occurred on main. No further changes for that failure are requested.

We’ll keep this as triage-can-wait: it improves handling of malformed manifest configuration, but we’re prioritizing failures affecting valid configurations. Please describe the behavior as ignoring malformed defaults rather than rejecting the manifest.

Please also add a plain AI-disclosure statement identifying the tool, mode/settings, and extent of assistance. The Claude Sonnet 5 attribution is already present; a session link is not needed.

Drafted for @mnriem with assistance from GitHub Copilot (model: GPT-6 Astra; interactive comment drafting).

@mnriem mnriem added triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate author-needs-disclosure AI use, or the agent/model/settings behind it, not disclosed per CONTRIBUTING author-awaiting Waiting on author response labels Sep 15, 2026
ConfigManager._get_extension_defaults() read extension.yml's config.defaults
via manifest_data.get("config", {}).get("defaults", {}) with no shape check
on the intermediate "config" value. A manifest with `config: []` or
`config: "oops"` (the top-level config.defaults field used by shipped
extensions like extensions/git/extension.yml, distinct from the already-
validated provides.config list) made the chained .get() raise a bare
AttributeError instead of degrading like every other malformed config
source in this class. The crash was silently swallowed by
should_execute_hook's blanket except, so a hook's `config.x is set`
condition permanently evaluated to False for the extension with no
diagnostic.

This does not reject the manifest: the extension still loads and every
other manifest-driven feature (commands, hooks, skills) keeps working. Only
the malformed `config`/`config.defaults` section is ignored, falling back
to no defaults, mirroring TestConfigManagerNonMappingYaml's existing
coverage for a non-mapping *root* of <id>-config.yml, one level deeper in
the manifest's own `config` section, which was previously unchecked.

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
@Noor-ul-ain001
Noor-ul-ain001 force-pushed the fix/extension-config-defaults-non-mapping branch from 11c344e to 3984fc5 Compare September 15, 2026 17:41
@Noor-ul-ain001 Noor-ul-ain001 changed the title fix(extensions): reject non-mapping extension.yml config section fix(extensions): ignore non-mapping extension.yml config section Sep 15, 2026
@Noor-ul-ain001

Copy link
Copy Markdown
Contributor Author

Thanks for the review! Addressed both points:

  • Reworded the PR title, summary, and the amended commit message to describe this as ignoring the malformed config/config.defaults section (falls back to {}, extension still loads and every other manifest-driven feature keeps working) rather than "rejecting" the manifest.
  • Added a plain-language AI Disclosure section to the PR body identifying the tool (Claude Code, model Claude Sonnet 5), the mode (interactive/agentic CLI with local tool access), and the extent of assistance (AI wrote the fix + 5 regression tests and ran the verification described in the test plan, under my direction; I reviewed the diff and test output before opening the PR). Kept the Co-Authored-By trailer, dropped the session link per your note.

Happy to leave this as triage-can-wait — let me know if anything else needs adjusting.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

author-awaiting Waiting on author response author-needs-disclosure AI use, or the agent/model/settings behind it, not disclosed per CONTRIBUTING triage-can-wait Verdict: valid and in-scope but deprioritized; held behind the evidence gate

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants