Skip to content

feat(tools-marketplace-inventory): wire resolver into arch graph - #1074

Merged
cuioss-oliver merged 12 commits into
mainfrom
feature/marketplace-dependency-resolver
Aug 1, 2026
Merged

feat(tools-marketplace-inventory): wire resolver into arch graph#1074
cuioss-oliver merged 12 commits into
mainfrom
feature/marketplace-dependency-resolver

Conversation

@cuioss-oliver

@cuioss-oliver cuioss-oliver commented Aug 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

Wires the marketplace's existing cross-file dependency-derivation engine into the
manage-architecture graph family through the shipped Axis-C derivation-resolver seam, so
architecture impact / architecture graph answer from real derived edges for the
plan-marshall marketplace instead of returning an empty graph.

Two resolvers are registered against the seam, each with its own stable provenance id so every
derived edge names which one produced it:

  • a markdown resolver on pm-plugin-development — script notation, skill references,
    relative-path xrefs, and implements: entries;
  • a python resolver on pm-dev-python — AST-parsed imports.

The two-resolver split is required rather than an optimization: collapsing them into one would
forfeit the per-edge markdown-vs-import provenance that is the point of the change.

Changes

Derived-edge production

  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/scripts/plugin_discover.py
    — materialize component_refs into each module's derived data, the substrate both resolvers
    read from.
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py — register
    the markdown derivation resolver.
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py — register the
    python-import derivation resolver.
  • marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py
    single-source the Axis-C suppression-note renderer shared by the resolver base.

Documentation and contract

  • marketplace/bundles/plan-marshall/skills/extension-api/standards/ext-point-derivation-resolver.md
    — record the shipped resolvers and correct DerivationResolverBase contract drift.
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/module-discovery.md,
    doc/concepts/extension-architecture.adoc — Axis-C now has a live implementor on each hierarchy.
  • SKILL.md updates for
    marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/SKILL.md,
    marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/SKILL.md, and
    marketplace/bundles/pm-plugin-development/skills/tools-marketplace-inventory/SKILL.md.

Build truthfulness (surfaced while verifying the new modules)

  • build.py — make a scoped compile truthful for fully mypy-excluded modules, and attribute
    mypy-exclude diagnostics to the calling command rather than to the excluded module.

Tests

  • test/pm-plugin-development/plan-marshall-plugin/test_markdown_derivation_resolver.py
  • test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py
  • test/pm-plugin-development/plan-marshall-plugin/test_component_ref_materialization.py
  • test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py — proves
    the graph family answers from derived edges rather than returning empty.
  • test/default/test_build_verify.py — covers the two build-truthfulness fixes.

Test Plan

  • Verification command passed (python3 .plan/execute-script.py plan-marshall:build-pyproject:pyproject_build run --command-args "verify")
  • Manual testing completed (if applicable)

Related Issues

None.

Intent

The problem. A working cross-file dependency-derivation engine already existed in
pm-plugin-development, but the manage-architecture graph family could not see it:
architecture impact / architecture graph returned an empty graph for the plan-marshall
marketplace. The capability and the consumer were both present; the wire between them was not.

The chosen approach. Consume the already-shipped Axis-C derivation-resolver seam rather than
touch manage-architecture itself, and register two resolvers against it — a markdown resolver
on pm-plugin-development (script notation, skill references, relative-path xrefs, implements:)
and a python-import resolver on pm-dev-python (AST-parsed imports). The split is load-bearing, not
an optimization: one merged resolver would still produce the edges but would forfeit the per-edge
markdown-vs-import provenance the seam's contract requires. component_refs is materialized inside
discover_plugin_modules() so the derived data both resolvers read from is produced exactly where
the discovery gate already runs.

Explicit non-goals.

  • Not "any bundle project". The scope is the plan-marshall marketplace specifically.
    discover_plugin_modules() early-returns [] unless the tree carries
    marketplace/.claude-plugin/marketplace.json with name == "plan-marshall", and
    component_refs is materialized

[Intent truncated — 1392 of 2294 characters shown; full outline in the plan workspace]

Summary by CodeRabbit

  • New Features

    • Dependency relationships are now derived for Python imports and Markdown references.
    • Dependency references include resolved and unresolved status, with duplicates removed and ordering standardized.
    • Invalid, unknown, or self-referential links are excluded and summarized in grouped diagnostic notes.
  • Bug Fixes

    • Mypy now skips scopes containing only excluded files while preserving failures for collectable code.
  • Documentation

    • Updated extension, resolver, discovery, and dependency-engine guidance to reflect the expanded architecture and behavior.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Sorry @cuioss-oliver, you have reached your weekly rate limit of 500000 diff characters.

Please try again later or upgrade to continue using Sourcery

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 5e62fdcf-6cc1-46fd-ab10-02de4db7142b

📥 Commits

Reviewing files that changed from the base of the PR and between 1decffb and 188fcbe.

📒 Files selected for processing (16)
  • build.py
  • doc/concepts/extension-architecture.adoc
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/ext-point-derivation-resolver.md
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/module-discovery.md
  • marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/SKILL.md
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/SKILL.md
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/scripts/plugin_discover.py
  • marketplace/bundles/pm-plugin-development/skills/tools-marketplace-inventory/SKILL.md
  • test/default/test_build_verify.py
  • test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py
  • test/pm-plugin-development/plan-marshall-plugin/test_component_ref_materialization.py
  • test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py
  • test/pm-plugin-development/plan-marshall-plugin/test_markdown_derivation_resolver.py
🚧 Files skipped from review as they are similar to previous changes (13)
  • doc/concepts/extension-architecture.adoc
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/module-discovery.md
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/SKILL.md
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/ext-point-derivation-resolver.md
  • test/pm-plugin-development/plan-marshall-plugin/test_markdown_derivation_resolver.py
  • test/default/test_build_verify.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/SKILL.md
  • test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py
  • marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py
  • test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/scripts/plugin_discover.py

📝 Walkthrough

Walkthrough

The change adds exclusion-aware mypy verification. It also materializes component references during discovery and adds pure Python and Markdown derivation resolvers with shared suppression-note aggregation, provenance, deterministic edges, and end-to-end graph validation.

Changes

Resolver derivation and verification

Layer / File(s) Summary
Exclude-aware mypy verification
build.py, test/default/test_build_verify.py
Reads mypy exclusions from pyproject.toml, skips empty scopes, preserves failures for collectable files, and conditionally includes .claude.
Resolver contracts and suppression notes
doc/concepts/extension-architecture.adoc, marketplace/bundles/plan-marshall/skills/extension-api/standards/*, marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py, marketplace/bundles/pm-*/skills/plan-marshall-plugin/SKILL.md
Documents Axis-C resolver registration, component_refs, resolver ownership, purity requirements, and shared suppression-note aggregation.
Component-reference materialization
marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/scripts/plugin_discover.py, marketplace/bundles/pm-plugin-development/skills/tools-marketplace-inventory/SKILL.md, test/pm-plugin-development/plan-marshall-plugin/test_component_ref_materialization.py
Projects dependency-index results to deterministic bundle references and attaches them to every discovered module.
Python and Markdown edge resolvers
marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py, marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py, test/pm-*/plan-marshall-plugin/test_*derivation_resolver.py
Adds pure python and markdown resolvers with reference filtering, invalid-edge suppression, aggregated notes, and sorted deduplicated output.
Graph pipeline validation
test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py
Validates resolver discovery, hierarchy coverage, provenance, edge merging, endpoint validity, deterministic output, and materialization exposure.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

🚥 Pre-merge checks | ✅ 2
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the primary change: integrating marketplace derivation resolvers with the architecture graph.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@cuioss-review-bot

cuioss-review-bot Bot commented Aug 1, 2026

Copy link
Copy Markdown

PR Reviewer Guide 🔍

(Review updated until commit 7f2bcd8)

🧪 PR contains tests
🔒 No security concerns identified
⚡ No major issues detected

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Triage dispositions

In reply to comment_id: IC_kwDOQ3xasM8AAAABMyJI9Q

Acknowledged - PR-Agent Reviewer Guide reports no actionable finding (no security concerns, no major issues, tests present). It names no standards violation, so no FIX-Eligible category applies per ext-triage-plugin pr-comment-disposition.md, and it carries no file or thread anchor to suppress against. Noted for the record: this is the only participating automated review on this PR (CodeRabbit rate-limited, Sourcery quota-exhausted), so the clean result is uncorroborated rather than independently confirmed.

@cuioss-oliver
cuioss-oliver force-pushed the feature/marketplace-dependency-resolver branch from d3aea0f to 1decffb Compare August 1, 2026 19:31
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

@coderabbitai review

The branch was rebased onto the current main tip; the diff now spans 8db7b42d43599db133ca70d2bb7afeb81ff679c8..1decffb9be2683bf27dae29b7a0ccda36a986900. Requesting a review of the rebased head now that the rate-limit window has reopened.

Reviewer note on what is most worth scrutiny in this change set:

  • marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py — a previously-duplicated _aggregate_notes helper was hoisted onto DerivationResolverBase, which is inherited by three resolvers (markdown, python, maven). The hoist also generalized the iteration from a fixed category tuple to the suppressed mapping's own key order. maven neither defines nor calls the helper, so it should inherit it inert — worth confirming.
  • build.py — an exclude-aware mypy scope-emptiness guard. It must not weaken type coverage: a genuine mypy failure in a scoped bundle has to stay red.
  • test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py — the non-empty-impact assertion is only reachable for the plan-marshall marketplace itself, because plugin_discover early-returns [] unless marketplace.json names it. That precondition is asserted separately so a fixture regression cannot masquerade as a resolver regression.

@coderabbitai

coderabbitai Bot commented Aug 1, 2026

Copy link
Copy Markdown

@cuioss-oliver I will review the rebased pull request. I will inspect the specified resolver inheritance, mypy exclusion guard, and marketplace fixture precondition.

✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🧹 Nitpick comments (6)
test/pm-plugin-development/plan-marshall-plugin/test_component_ref_materialization.py (1)

149-155: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Move the fixture marketplace into a pytest fixture.

Nine tests repeat the same three-line tempfile.TemporaryDirectory() plus _build_fixture_marketplace(root) scaffolding. A module-scoped tmp_path_factory fixture removes the duplication and builds the tree once instead of nine times. The tests only read the tree, so sharing it is safe.

♻️ Sketch
import pytest


`@pytest.fixture`(scope='module')
def marketplace_root(tmp_path_factory) -> Path:
    """Build the fixture marketplace once for the whole module."""
    root = tmp_path_factory.mktemp('marketplace-fixture')
    _build_fixture_marketplace(root)
    return root


def test_refs_are_produced_for_the_fixture_bundle_tree(marketplace_root):
    refs = _modules_by_name(marketplace_root)['alpha']['component_refs']
    ...

test_missing_bundles_directory_yields_no_refs needs its own temporary root and stays as it is.

Also applies to: 163-167, 181-185

marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py (1)

212-232: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚖️ Poor tradeoff

Consider lifting the reference-join loop onto DerivationResolverBase.

This loop is identical to the markdown resolver's loop in marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py. Only the accepted dep_type set differs. The three suppression predicates, the candidate-string format, and the seeded category order are the same in both.

DerivationResolverBase already owns _aggregate_notes for exactly this reason. A protected helper such as _join_component_refs(derived_by_name, accepted_dep_types) would single-source the suppression semantics without creating coupling between the two bundles, because both already depend on the shared base rather than on each other.

This is optional. The current duplication is small and each copy is well documented. Raise it only if a third reference-join resolver is planned.

test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py (1)

58-58: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Derive MARKDOWN_DEP_TYPES from DependencyType.

This tuple mirrors the four non-import members of the DependencyType enum. Three tests sweep over it to prove that no markdown kind leaks into the python resolver. If a sixth dependency type is added, the sweep keeps passing and the new type goes untested.

Derive the population as the enum values minus the resolver's own PYTHON_DEP_TYPE, and add a non-vacuity guard.

♻️ Proposed change
-MARKDOWN_DEP_TYPES = ('script', 'skill', 'path', 'implements')
+MARKDOWN_DEP_TYPES = tuple(
+    sorted({member.value for member in DependencyType} - {_extension_module.PYTHON_DEP_TYPE})
+)
+assert MARKDOWN_DEP_TYPES, 'no non-import dependency types; the sweeps below would be vacuous'

Based on learnings: "For every set-guarding detector, derive the population from its authoritative source at build time or runtime rather than maintaining hard-coded lists that mirror parser arguments, registry entries, build goals, enum constants, registered handlers, or dispatch tables. When a derived population is required for a sweep, add a non-vacuity guard to ensure the detector does not pass trivially over an empty set."

Sources: Path instructions, Learnings

test/pm-plugin-development/plan-marshall-plugin/test_markdown_derivation_resolver.py (1)

93-104: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider mirroring the unconditional-registration tests for the markdown resolver.

test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py lines 374-425 assert two properties for the python resolver: the resolver is discovered even when its domain does not apply, and discover_derivation_resolvers() never calls applies_to_module. This file asserts neither for the markdown resolver.

The markdown Extension.applies_to_module returns not-applicable for any tree without a marketplace structure, so the same independence property is load-bearing here. Add the equivalent pair so a future applicability change cannot silently drop the markdown resolver from the roster.

Do you want me to draft those two tests?

test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py (1)

143-145: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Single-source the resolver count from EXPECTED_RESOLVER_IDS.

The roster size 3 is repeated at line 145 and line 321, while EXPECTED_RESOLVER_IDS at line 46 already states it. Registering a fourth resolver requires four coordinated edits, and a missed one fails in a place that does not name the cause.

Line 46 itself is a deliberate pin rather than a mirrored list, because deriving it from discover_derivation_resolvers() would make test_three_resolvers_are_discovered tautological. Keep it as the single source and derive the counts from it.

♻️ Proposed change
-def test_resolver_count_is_three():
+def test_resolver_count_matches_the_expected_roster():
     """resolver_count is the anti-vacuity numerator carried on every response."""
-    assert len(_pipeline()['resolvers']) == 3
+    assert len(_pipeline()['resolvers']) == len(EXPECTED_RESOLVER_IDS)
     assert ran_edges == []
-    assert len(ran_reports) == 3
+    assert len(ran_reports) == len(EXPECTED_RESOLVER_IDS)
     assert len(ran_reports) != len(no_resolver_reports)

Separately, on the PR's third review request: the marketplace precondition is asserted independently. test_gate_precondition_marketplace_is_plan_marshall at lines 87-97 calls _is_plan_marshall_marketplace directly and does not touch _pipeline(), so a non-marketplace tree fails there with its own message before any edge claim is read. test_gate_precondition_discovery_returns_modules and test_gate_precondition_modules_carry_component_refs add the non-zero module count and the populated-field check as separate failures. An empty fixture cannot produce vacuous success across those three gates.

As per path instructions: "Treat a hardcoded list that must mirror a set defined elsewhere — build goals, enum constants, registered handlers, dispatch tables — as a defect unless it is derived from that source at build or run time."

Also applies to: 319-322

Source: Path instructions

test/default/test_build_verify.py (1)

232-256: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Consider a failure-propagation test for cmd_test_compile mirroring the cmd_compile one.

test_scoped_compile_propagates_mypy_failure_when_files_survive (Lines 247-256) confirms the guard never maps a real mypy failure to success for cmd_compile. cmd_test_compile applies the identical guard-then-run pattern but has no analogous failure-propagation test in this file. Adding one would close the same regression class for the test-compile path.


ℹ️ Review info
⚙️ Run configuration

Configuration used: Repository: cuioss/coderabbit/.coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: 4330706b-1220-4858-b4fb-fdc844b69b6c

📥 Commits

Reviewing files that changed from the base of the PR and between 8db7b42 and 1decffb.

📒 Files selected for processing (16)
  • build.py
  • doc/concepts/extension-architecture.adoc
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/ext-point-derivation-resolver.md
  • marketplace/bundles/plan-marshall/skills/extension-api/standards/module-discovery.md
  • marketplace/bundles/plan-marshall/skills/script-shared/scripts/extension/extension_base.py
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/SKILL.md
  • marketplace/bundles/pm-dev-python/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/SKILL.md
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/extension.py
  • marketplace/bundles/pm-plugin-development/skills/plan-marshall-plugin/scripts/plugin_discover.py
  • marketplace/bundles/pm-plugin-development/skills/tools-marketplace-inventory/SKILL.md
  • test/default/test_build_verify.py
  • test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py
  • test/pm-plugin-development/plan-marshall-plugin/test_component_ref_materialization.py
  • test/pm-plugin-development/plan-marshall-plugin/test_graph_family_bundle_project.py
  • test/pm-plugin-development/plan-marshall-plugin/test_markdown_derivation_resolver.py

Comment thread build.py
Comment thread doc/concepts/extension-architecture.adoc
Comment thread test/default/test_build_verify.py
Comment thread test/pm-dev-python/plan-marshall-plugin/test_python_derivation_resolver.py Outdated
@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

Triage dispositions

In reply to comment_id: IC_kwDOQ3xasM8AAAABMyJI9Q

Acknowledged - PR-Agent Reviewer Guide reports no actionable finding (no security concerns, no major issues, tests present). It names no standards violation, so no FIX-Eligible category applies per ext-triage-plugin pr-comment-disposition.md, and it carries no file or thread anchor to suppress against. Noted for the record: this is the only participating automated review on this PR (CodeRabbit rate-limited, Sourcery quota-exhausted), so the clean result is uncorroborated rather than independently confirmed.

In reply to comment_id: PRR_kwDOQ3xasM8AAAABIDl07A

Review body triaged nitpick by nitpick. ACTIONED: (5) resolver count single-sourced from EXPECTED_RESOLVER_IDS at L145 and L321 - agreed, the roster is already pinned and asserted at L140 so the literal 3 is a mirror with no source, TASK-012. (6) failure-propagation test for cmd_test_compile - agreed and the sharpest of the six, the guard-then-run pattern is identical on both arms but only cmd_compile has the test that proves a real mypy failure stays red, which is exactly the risk flagged in the review request, TASK-011. (3) MARKDOWN_DEP_TYPES derived from DependencyType - folded into the same fix as the Major inline finding on the four hardcoded dep-type populations, TASK-012. DECLINED: (1) module-scoped pytest fixture for the fixture marketplace - a real duplication, but trading per-test filesystem isolation for build-once speed in a nine-test module is not a trade we take by default, and the tests are fast. (2) lifting the reference-join loop onto DerivationResolverBase - declining on your own stated condition, raise it only if a third reference-join resolver is planned. None is, the two copies are small and separately documented, and hoisting shared behaviour into script-shared is the shape of a retained lesson in this repo about a centralizing refactor silently widening a lazy contract. (4) mirroring the unconditional-registration tests onto the markdown resolver - the load-bearing property is that discover_derivation_resolvers never calls applies_to_module, and that is asserted once at the collector level in the python file. While that holds, no per-resolver applicability change can drop a resolver from the roster, so the markdown mirror re-tests the same mechanism through a second instance. Worth adding if the markdown Extension ever gains its own discovery-time guard.

@cuioss-oliver

Copy link
Copy Markdown
Collaborator Author

/review

Requesting a re-review of the current head 7f2bcd860f8ecf1bc55d6ab10e5bb1a88b2bf64d.

Since the earlier Reviewer Guide was published against d3aea0fd2 (two heads ago), the branch has been rebased onto the current main tip and has taken four fixes from CodeRabbit's review:

  • build.py_mypy_exclude_patterns now guards a non-str/non-list [tool.mypy] exclude value, which previously raised an unhandled TypeError and crashed both compile and test-compile, contradicting the function's documented fail-open contract.
  • test/default/test_build_verify.py — added the missing cmd_test_compile failure-propagation test (its cmd_compile counterpart already existed), plus a parametrised malformed-exclude test.
  • The four resolver test modules — DependencyType membership is now derived from the enum rather than restated as literals at four sites, the overflow fixtures derive both ends of the sample/limit coupling, and the resolver-count assertions read from the already-pinned expected roster.

cuioss-oliver and others added 12 commits August 1, 2026 23:17
…rived data

Extend discover_plugin_modules() so every bundle module carries a
component_refs list of {target_bundle, dep_type, resolved} entries.

The marketplace dependency-detection engine is reused by import — no
detection logic is duplicated. Its component-granular vocabulary
(bundle:skill:script) is projected onto the bundle granularity the
architecture graph keys on, and each reference is stamped resolved so
unresolved targets are surfaced rather than dropped. Entries are
deduplicated on the (target_bundle, dep_type, resolved) triple, keeping
the stored field proportional to the bundle count.

The engine reads from disk, so it runs once here at discovery time: an
Axis-C derivation resolver is a pure function of its arguments and must
read this pre-materialized field instead of the filesystem.

Materialization is unconditional — no flag, environment variable, or
feature gate guards it.

Co-Authored-By: Claude <noreply@anthropic.com>
Opt the pm-plugin-development Extension into Axis-C by multiple
inheritance, the same shape build-maven ships on Axis-B.

derive_edges is a pure join over the component_refs field discovery
materializes: the four markdown reference kinds (script, skill, path,
implements) contribute edges, while import entries belong to the sibling
python resolver and are skipped without a note, since they are outside
this resolver's scope rather than suppressed by it.

Unresolved targets, unknown endpoints, and self-edges are suppressed and
reported as aggregated notes — one per category with a count and a
bounded sample. Aggregation is load-bearing: merge_resolver_edges appends
one note per dropped candidate, so per-reference notes would make the
per-resolver report unreadable and defeat the reporting obligation
instead of satisfying it.

Document the two new faces in the plan-marshall-plugin manifest's
Extension API table, which now indexes every face the extension
implements, and record in tools-marketplace-inventory that the detection
engine has a second consumer plus the component-to-module vocabulary
projection, cross-referencing the resolver roster rather than restating
it.

Co-Authored-By: Claude <noreply@anthropic.com>
Opt the pm-dev-python Extension into Axis-C by multiple inheritance.
Python-import edges are Python-language knowledge, so they belong to the
Python domain bundle rather than to a build-system bundle.

derive_edges is a pure join over the same component_refs field module
discovery materializes, selecting only import entries. The four markdown
reference kinds belong to the sibling markdown resolver on
pm-plugin-development and are skipped without a note. Unresolved targets,
unknown endpoints, and self-edges are suppressed and reported as
aggregated notes, one per category with a count and a bounded sample.

The resolver reads the pre-materialized field only — it imports nothing
from the bundle that materializes it, so the second registration creates
no coupling between the two bundles.

Document both new faces plus the already-shipped provides_arch_gate in
the manifest, and record that Axis-C registration is discovered
unconditionally: applies_to_module gates skill loading, not resolver
registration. Two tests pin that independence, one of them by making
applies_to_module fatal during discovery so the property is proven
positively rather than inferred from a result that came out right.

Co-Authored-By: Claude <noreply@anthropic.com>
…ed edges

End-to-end test running the whole shipped pipeline against the
plan-marshall marketplace itself: real module discovery materializing
component_refs, real resolver discovery spanning both hierarchies, and
the real core merge.

The gate is asserted as three separately-failing preconditions.
discover_plugin_modules() early-returns [] for any tree that is not the
plan-marshall marketplace, so without them every edge assertion would
pass vacuously over an empty map — the failure would read as a broken
graph rather than a broken gate.

Characterization the live tree surfaced, pinned rather than smoothed
over: the python resolver never produces an edge ALONE. Every module
pair its import join derives is also derived by the markdown join,
because a bundle whose Python imports a plan-marshall module invariably
references that bundle in markdown too. Its contribution is real but
fully corroborated, which is precisely what per-edge provenance exists
to make visible. The test therefore asserts that python contributes, that
its own edge_count is non-zero (counted before the producer union, so it
stays honest under collapse), and that every python edge on this tree is
corroborated — so a future pair markdown cannot see fails the test as a
finding rather than passing silently.

The collapse property is proven twice: once over the live tree and once
over a controlled module map driven through the same two real resolvers
and the same real merge, so a tree that stopped containing a corroborated
pair could not be mistaken for a broken union.

Co-Authored-By: Claude <noreply@anthropic.com>
ext-point-derivation-resolver.md carried a single-implementor status
clause and an "Out of scope — not implemented" block naming the markdown
and python resolvers as future work. Both are now shipped, so the block
is deleted rather than amended and the roster table carries one row per
resolver with its axis and owner.

Also rewrites the framings the second and third implementors made stale:
the header status clause, the hierarchy diagram (which connected only the
build side to Axis-C), the ambiguous-identity-key obligation's future
tense, and the union-semantics passage that described markdown/python
corroboration as hypothetical when it is now the common case on this
tree.

module-discovery.md gains component_refs as an explicitly optional,
extension-specific field — array or absent — with its element shape, the
five dep_type values, the stamped-not-dropped rule for unresolved
targets, and the empty-array-not-missing-key rule. It is deliberately NOT
promoted into the shared required field set: only extensions shipping an
Axis-C resolver that joins over it populate it, and consumers must read
absence as "no references known" rather than as an error.

Co-Authored-By: Claude <noreply@anthropic.com>
The extension-architecture concept doc described Axis-C as subclassed by
"whichever bundle owns the relevant domain knowledge" and argued the
three-axis split by reference to a "future markdown resolver". Both
framings predate the second and third resolvers.

The Axis-C bullet now names the shipped roster and the hierarchy each
resolver opts in from — maven on Axis-B, markdown and python on Axis-A —
which is what makes the span-both-hierarchies argument concrete rather
than anticipated. It states explicitly that build-pyproject registers no
resolver: Python-import edges are language knowledge owned by the Python
domain bundle, so naming the build bundle here would invert exactly the
ownership split ADR-004 protects.

The per-resolver roster is cross-referenced rather than restated, so the
contract doc stays the single source for it.

Co-Authored-By: Claude <noreply@anthropic.com>
Scoped `compile {bundle}` and `quality-gate {bundle}` were unsatisfiable on any
tree for a "thin" bundle whose only .py file is removed by the [tool.mypy]
exclude patterns: mypy collected nothing and exited 2 with "There are no .py[i]
files in directory". Confirmed on pm-dev-python and pm-dev-frontend-cui; the
affected set is derived from the real tree by the tests rather than hand-listed,
because a listed pair is a sample, not an enumeration.

Chose Option B (treat an empty COLLECTED file set as a legitimate no-op) over
Option A (narrow or remove the exclude). Option A's recorded objection in
pyproject.toml still holds against the current tree: 10
plan-marshall-plugin/extension.py files plus 4 build-*/scripts/extension.py
files all resolve to the bare module name "extension" under
explicit_package_bases, so type-checking them yields duplicate-module errors.
Option B weakens no type coverage — those files are excluded tree-wide, so the
whole-tree run does not check them either; the scoped run now reports that same
truth instead of a defect that does not exist.

The predicate is exclude-AWARE. _mypy_collects_any asks "does any file survive
mypy's configured excludes", reading the patterns from the single [tool.mypy]
declaration and matching them the way mypy's own crawl does. The exclude-blind
any(*.py) shape cannot detect this class — pm-dev-python plainly contains a .py
file. The pre-existing .claude/ guard in the whole-tree branch carried exactly
that blind shape and is tightened to the same predicate: it would pass for a
.claude/ whose Python lives only under the excluded .claude/worktrees/.

cmd_test_compile carries the same guard. test/pm-dev-python holds only
plan-marshall-plugin/ files, which the excludes drop wholly, so verify {bundle}
stayed unsatisfiable without it.

Exit 2 is not blanket-suppressed and no bundle is special-cased: mypy still
runs, and its non-zero exit still propagates, whenever any file survives the
excludes. An unreadable or malformed config fails open to running mypy.
_mypy_exclude_patterns hardcoded a "compile:" prefix on its two stderr
diagnostics, but is reachable from cmd_test_compile via
_skip_empty_mypy_scope('test-compile', path). An operator running
test-compile against a malformed pyproject.toml was sent to the wrong
command.

Thread a label from the calling command through _mypy_exclude_patterns /
_mypy_collects_any / _skip_empty_mypy_scope, defaulting to the
command-neutral "mypy" so a caller that cannot name a command emits an
honest prefix rather than a wrong one. cmd_compile passes "compile"
explicitly for the whole-tree .claude probe.

Pinned by test_exclude_patterns_diagnostic_names_the_calling_command,
which asserts BOTH the test-compile and compile prefixes — asserting one
in isolation would pass against a hardcoded literal.

Found by pre-submission-self-review (finding 226af8).

Co-Authored-By: Claude <noreply@anthropic.com>
NOTE_SAMPLE_LIMIT and the ~23-line _aggregate_notes body were
byte-identical duplicates across the pm-dev-python and
pm-plugin-development plan-marshall-plugin extensions, with no mechanism
keeping them in sync. Hoist both into DerivationResolverBase, which
already declares the obligation they discharge and which both bundles
already import from, so no bundle-to-bundle coupling is introduced.

The base version iterates the suppressed mapping's own key order instead
of a fixed category tuple, so it needs no category vocabulary of its own.
That is deliberate: the three categories are the vocabulary of a
component_refs join, and the third shipped resolver (maven) suppresses on
coordinate collision instead — a SUPPRESSION_CATEGORIES constant on the
universal Axis-C anchor would have read as "the Axis-C suppression
vocabulary" while being wrong for one of the three resolvers. Each
resolver keeps its own one-line tuple.

Both test modules now read NOTE_SAMPLE_LIMIT from extension_base rather
than off the extension module; no re-export shim is left behind.

The two derive_edges bodies are also near-identical but are NOT collapsed:
doing so would require inventing a parameterized component_refs-join
helper that exists nowhere today and putting a marketplace-specific data
shape on the shared base that maven would inherit unused. Reported rather
than edited.

Co-Authored-By: Claude <noreply@anthropic.com>
Two statements in the Axis-C anchor's docstrings went stale the moment
this branch shipped the markdown and Python resolvers:

- The class docstring still called them "a future markdown or Python
  import resolver" while both now ship. The identical phrasing was
  already corrected in the four sibling contract docs, so the anchor was
  the last place still describing them as unbuilt. Reworded to name the
  three shipped joins by what each joins over.

- derive_edges' Args block enumerated the derived-data fields as
  metadata / dependencies / paths / packages / stats, omitting
  component_refs — the sixth field this branch adds and the one both new
  resolvers actually read. It is now named, marked optional, and xrefs
  module-discovery.md for the element schema and the
  absent-vs-empty-array contract rather than restating them.

Found by pre-submission-self-review on its HEAD-dependent re-fire over
the simplify commit (findings bf3a67, 062dc2).

Co-Authored-By: Claude <noreply@anthropic.com>
A non-str, non-list exclude (e.g. the typo `exclude = true`) slipped past
the `isinstance(raw, str)` branch in `_mypy_exclude_patterns` and reached
`for entry in raw`, raising an unhandled TypeError out through
`_mypy_collects_any` into BOTH `cmd_compile` and `cmd_test_compile`.
`pyproject.toml` is externally-sourced config and the helper's own
docstring documents a fail-open contract, so add the missing type guard:
emit the same style of `{label}: ... - assuming none` diagnostic (naming
the offending type) and treat the value as no exclusions.

Also close the symmetric-pair test gap: `cmd_compile` carried a
failure-propagation test proving a genuine mypy failure stays red, while
`cmd_test_compile` applied the identical guard-then-run pattern with no
analogous test. Adds the mirrored test plus a parametrised
malformed-exclude test spanning bool/int/dict.

Co-Authored-By: Claude <noreply@anthropic.com>
…ative sources

Every set-guarding sweep in the derivation-resolver tests restated its
population as literals, so a sixth `DependencyType` would leave all four
sweeps green while the new kind went untested and the markdown-vs-import
resolver split went unverified for it.

- `DependencyType` is now imported from `_dep_detection` (the authoritative
  enum) at all four sites. The python resolver's out-of-scope population is
  the enum minus its own `PYTHON_DEP_TYPE`; the markdown resolver's owned
  population is read from its own `MARKDOWN_DEP_TYPES` declaration and its
  out-of-scope population is the enum remainder, so a new kind lands in one
  of the two sweeps automatically. Each file carries a non-vacuity + partition
  guard so an empty or invented population fails loudly instead of sweeping
  nothing.
- Both overflow fixtures now derive BOTH ends from `NOTE_SAMPLE_LIMIT`
  (`NOTE_SAMPLE_LIMIT + NOTE_OVERFLOW` entries, asserting the total and the
  `(+N more)` remainder). The previous half-derived form stopped exercising
  the overflow branch entirely once the cap reached 5.
- The resolver-count assertions now use `len(EXPECTED_RESOLVER_IDS)` rather
  than a second literal `3`; `EXPECTED_RESOLVER_IDS` stays a hand-written pin.
- `test_markdown_derivation_resolver.py`'s `spec_from_file_location` loader is
  guarded like its python sibling, so a wrong `EXTENSION_FILE` names the real
  cause instead of an `AttributeError` on `NoneType.loader`.

Co-Authored-By: Claude <noreply@anthropic.com>
@cuioss-oliver
cuioss-oliver force-pushed the feature/marketplace-dependency-resolver branch from 7f2bcd8 to 188fcbe Compare August 1, 2026 21:27
@cuioss-oliver
cuioss-oliver added this pull request to the merge queue Aug 1, 2026
Merged via the queue into main with commit 021305e Aug 1, 2026
11 checks passed
@cuioss-oliver
cuioss-oliver deleted the feature/marketplace-dependency-resolver branch August 1, 2026 21:55
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant