Fix aipm lint silent for a plugin-shaped dir missing aipm.config.ts - #109
Merged
Merged
Conversation
#91 made validate (hard envelope-invalid) and build (thrown ConfigLoadError) catch a plugin-shaped directory missing aipm.config.ts, but lint stayed silent for the identical tree. schema/envelope-shape now also fires on a missing config, emitting the equivalent envelope-invalid diagnostic lint and validate agree on. Refs #101
There was a problem hiding this comment.
Pull request overview
Aligns aipm lint with aipm validate/aipm build by ensuring a plugin-shaped directory missing aipm.config.ts is surfaced as an envelope-invalid-backed diagnostic instead of being silently ignored, and documents + tests the agreement.
Changes:
- Update
schema/envelope-shapeto emit anenvelope-invaliddiagnostic whenaipm.config.tsis missing. - Add regression tests asserting
lint()andrunValidate()agree on the missing-config plugin-shaped directory case. - Update the lint engine spec and add a patch changeset documenting the behavior change.
Reviewed changes
Copilot reviewed 4 out of 4 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| packages/core/src/lint/rules/legacy-envelope-shape.ts | Emit envelope-invalid diagnostic for missing aipm.config.ts in plugin-shaped dirs. |
| packages/core/src/lint/engine.test.ts | Add agreement regression tests for lint() vs runValidate() on missing-config plugins. |
| docs/specs/lint-engine.md | Document that schema/envelope-shape also fires on missing aipm.config.ts. |
| .changeset/lint-missing-config-envelope-invalid.md | Patch changeset describing the lint behavior fix. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
The rule pre-checked for a missing aipm.config.ts and returned a hard-coded copy of the message loadPluginConfig throws for that case, then returned no diagnostic at all for any non-Zod load failure. A config with a syntax error or no usable default export therefore still passed aipm lint silently while validate and build both reported envelope-invalid — the same disagreement the missing-config fix set out to close. Drop the existence pre-check and derive every diagnostic from the thrown error, mirroring runValidate()'s envelope-load catch: a ZodError cause expands into per-issue diagnostics, anything else becomes a single envelope-invalid carrying the loader's own message. Agreement with validate is now structural rather than a duplicated string, and covers missing, unimportable and schema-violating configs alike. Also corrects the lint engine's comment on its envelope short-circuit, which claimed the config loader explains an unusable envelope on its own.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
#91madeaipm validate(hardenvelope-invalidfinding) andaipm build(thrownConfigLoadError) catch a plugin-shaped directory missingaipm.config.ts.aipm lintstayedsilent for the identical tree, so the three surfaces disagreed on a real defect: a user relying on
lintgot a clean bill of health for a directoryvalidate/buildcorrectly flagged.schema/envelope-shape(packages/core/src/lint/rules/legacy-envelope-shape.ts) no longershort-circuits with
[]on a failed envelope load. It has nofs.existsSyncpre-check at all;it always calls
loadPluginConfig()and derives every diagnostic from the thrown error,mirroring
runValidate()'s envelope-load catch inpipeline/validate.ts— aConfigLoadErrorwith a
ZodErrorcause expands into per-issue diagnostics, and anything else becomes a singlehard
envelope-invalidcarryingerr.messageverbatim. Agreement withvalidateis thereforestructural rather than a duplicated message string, and it covers both a missing
aipm.config.tsand one that exists but cannot be imported (syntax error, no usable defaultexport) — the latter previously left
lintsilent whilevalidate/buildflagged it.packages/core/src/lint/engine.ts's comment on its envelope short-circuit is corrected; itclaimed the config loader explained an unusable envelope on its own, which was not true for the
import-failure path.
validate/buildbehavior (unchanged since plugin dir without aipm.config.ts silently drops out of discovery: validate AND build exit 0 green ('Built 0 plugin(s)') #91) or to theenvelope-invalid→schema/envelope-shapemapping in the reconcileddocs/specs/lint-engine.md§3.6 migrationtable (Reconcile docs/specs/lint-engine.md with the shipped engine/CLI (spec drifted behind implementation) #88) — the rule id/category/severity were already correct; only the missing-config case
needed to actually fire it.
docs/specs/lint-engine.md§3.1 updated to describeschema/envelope-shapeas firing on anyfailure to resolve the envelope — missing, unimportable, or schema-violating — not only a
malformed one.
Acceptance criteria mapping
"For a plugin-shaped directory missing
aipm.config.ts,aipm lintemits a diagnosticequivalent in severity and meaning to
validate's hardenvelope-invalidfinding for the sametree." — covered by
packages/core/src/lint/engine.test.ts'slint() — plugin-shaped repo-root subdirectory missing aipm.config.ts (#91, #101) > agrees with runValidate(): both report the equivalent envelope-invalid finding for the same tree, which asserts the lint diagnostic'sruleId(
schema/envelope-shape),category(schema),severity(error),legacyCode(
envelope-invalid), andmessage(byte-identical tovalidate's)."A test asserts that lint and validate AGREE on this case ... not one silent while the other
flags it." — the same test runs
lint()andrunValidate()against the identical temp tree(the plugin dir without aipm.config.ts silently drops out of discovery: validate AND build exit 0 green ('Built 0 plugin(s)') #91 repro shape: a
plugins/*dir with a target manifest but noaipm.config.ts) andasserts both surface the equivalent finding. It fails against pre-fix code (lint returns
[])and passes with the fix.
Follow-on from the same defect class:
lint() — plugin whose aipm.config.ts exists but fails to import (#101)asserts the same agreement for a config with a deliberate syntax error, wherelintpreviously emitted zero diagnostics; a sibling test pins the schema-violation path toits per-issue expansion so the two error shapes stay distinct. Confirmed end-to-end: against a
plugin with an unparseable
aipm.config.ts,aipm lintandaipm validateboth exit 1 withFailed to import aipm.config.ts: ParseError: Unexpected token; pre-fix,lintexited 0silently.
A second test in the first
describeblock (emits nothing further beyond the envelope-invalid diagnostic for the config-less plugin) confirms the engine's existing short-circuit (continueafter the failed
loadPluginConfigcall) still holds — no other rule spuriously runs against anunresolved envelope.
Test plan
pnpm --filter @ai-plugin-marketplace/core exec vitest run src/lint/engine.test.ts(newagreement tests + existing suite)
pnpm run check(typecheck, lint, api-report, full test suite, format, deps, knip) — greenpnpm run fix:formatrun before pushRefs #101