fix(pack): preserve remote metadata outcomes (supersedes #2661) - #2693
Open
Daniel Meppiel (danielmeppiel) wants to merge 5 commits into
Open
fix(pack): preserve remote metadata outcomes (supersedes #2661)#2693Daniel Meppiel (danielmeppiel) wants to merge 5 commits into
Daniel Meppiel (danielmeppiel) wants to merge 5 commits into
Conversation
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: abb3728c-0b82-4dbc-9977-031de5196627
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Daniel Meppiel (danielmeppiel)
requested a review
from Sergio Sisternes (sergio-sisternes-epam)
as a code owner
August 26, 2026 08:06
Copilot started reviewing on behalf of
Daniel Meppiel (danielmeppiel)
August 26, 2026 08:07
View session
Contributor
There was a problem hiding this comment.
Pull request overview
This PR tightens apm pack’s marketplace metadata handling by preserving explicit remote-metadata enrichment outcomes (fetched/empty/failed/offline/local), surfacing certifiability in JSON, and preventing --check-clean from “passing” when regeneration is based on incomplete remote metadata. It also adds a strict pre-write gate (--strict-metadata, exit 5) that fails before any producer writes when metadata cannot be certified.
Changes:
- Introduces canonical
MetadataEnrichmentOutcome/MetadataEnrichmentResultinmarketplace/builder.pyand threads that state through pack JSON and drift checking. - Makes
--check-cleanrefuse byte comparison when remote metadata is uncertifiable (uncertifiabledrift status; exit 4) and adds a strict pre-write gate (--strict-metadata; exit 5). - Adds/updates unit + integration coverage, documentation, changelog entry, and an architecture boundary guard to keep metadata-certifiability ownership centralized.
Show a summary per file
| File | Description |
|---|---|
| tests/unit/marketplace/test_metadata_enrichment.py | New regression tests for outcome preservation, JSON envelope, strict-mode preflight, and drift “uncertifiable” behavior. |
| tests/unit/marketplace/test_builder.py | Updates auth mocking to AuthResolver.try_with_fallback() and adjusts warning expectations for offline metadata enrichment. |
| tests/integration/test_remote_marketplace_metadata_outage_lifecycle.py | New installed-binary lifecycle test proving outage behavior is visible and non-certifying across runs. |
| tests/integration/test_architecture_authorities.py | Adds authority tests ensuring metadata-certifiability remains owned by marketplace/builder.py and guarded by lint. |
| src/apm_cli/marketplace/drift_check.py | Adds uncertifiable drift state and carries metadata enrichment result for pack JSON/error shaping. |
| src/apm_cli/marketplace/builder.py | Adds durable enrichment outcome/result types, preserves warnings, and routes metadata fetch through resolver-selected fallback. |
| src/apm_cli/core/build_orchestrator.py | Adds strict-metadata preflight and separates marketplace preparation from write to fail before mutations. |
| src/apm_cli/commands/pack.py | Adds --strict-metadata (exit 5), makes --check-clean non-mutating via effective dry-run, and enriches JSON with metadata certifiability/outcomes. |
| scripts/lint-architecture-boundaries.sh | Adds AC34 guard preventing parallel metadata-enrichment owners/consumers outside marketplace/builder.py. |
| packages/apm-guide/.apm/skills/apm-usage/commands.md | Documents strict-metadata and updated gate behavior/exit codes for CI usage. |
| docs/src/content/docs/reference/cli/pack.md | Updates CLI reference for JSON shape, --check-clean uncertifiable behavior, and --strict-metadata. |
| docs/src/content/docs/producer/releasing-from-any-ci.md | Updates CI release sequence to verify (--dry-run) then generate with --strict-metadata. |
| docs/src/content/docs/producer/publish-to-a-marketplace.md | Adds --strict-metadata to publishing guidance. |
| CHANGELOG.md | Adds an Unreleased “Fixed” entry for the remote-metadata outcome + gating behavior change. |
| .github/instructions/architecture.instructions.md | Adds marketplace metadata certifiability as a single-owner decision (builder-owned). |
| .apm/instructions/architecture.instructions.md | Mirrors the architecture instruction update for repo-local instructions. |
Review details
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
Suppressed comments (1)
src/apm_cli/marketplace/builder.py:1151
- This debug log says we're "Skipping" metadata fetch for non-GitHub hosts, but the function returns a failed outcome. Align the log message with the actual behavior so debugging doesn't imply a harmless skip.
logger.debug(
"Skipping metadata fetch for %s (non-GitHub host: %s)",
pkg.name,
effective_host,
)
- Files reviewed: 16/16 changed files
- Comments generated: 4
- Review effort level: Lite
Comment on lines
+77
to
+81
| def test_metadata_outcomes_preserve_success_empty_failed_offline_and_local( | ||
| tmp_path: Path, | ||
| monkeypatch, | ||
| ) -> None: | ||
| """Only failed and intentionally offline remote enrichment is uncertifiable.""" |
Comment on lines
+1564
to
+1568
| assert all( | ||
| "Duplicate marketplace package name" not in warning for warning in report.warnings | ||
| ) | ||
| assert len(report.warnings) == 2 | ||
| assert all( |
Comment on lines
1121
to
1123
| GHES and GHE Cloud packages use the GitHub REST API on the | ||
| package's host. For non-GitHub-class hosts, metadata enrichment | ||
| is skipped. |
Comment on lines
+66
to
+69
| - `apm pack` now reports unavailable remote package metadata, exposes its | ||
| certifiability in JSON, and prevents `--check-clean` from certifying a | ||
| degraded regeneration. `--strict-metadata` exits before writing an incomplete | ||
| marketplace artifact. (closes #2524) |
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
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.
fix(pack): preserve remote metadata outcomes
TL;DR
apm packnow preserves whether remote marketplace metadata was fetched, empty, unavailable, intentionally skipped, or local. Default packing remains best-effort with actionable warnings, while--strict-metadatafails before writing and--check-cleanrefuses to certify a degraded regeneration. This credited successor preserves the work from #2661, rebases it onto currentmain, and repairs the auth-fallback regression tests needed for the final CI proof.Important
Supersedes #2661 and closes #2524. The original branch is not maintainer-writable, so this PR preserves its commits and contributor attribution instead of rewriting that branch.
Problem (WHY)
apm.ymlfetch failures were collapsed into missing optional fields, so pack output could silently omitdescriptionandversion.--check-cleancould compare two equally degraded documents and report a clean marketplace even though regeneration was not certifiable.main.AuthResolver.try_with_fallback(), but two unchanged tests still mocked onlyresolve()and hung in YAML parsing.Why these matter: #2524 records the observed silent metadata loss. The recovery follows "Grounding outputs in deterministic tool execution transforms probabilistic generation into verifiable action." and keeps the change bounded because "Add what the agent lacks, omit what it knows".
Approach (WHAT)
--check-cleandry-run-only and reject uncertifiable regeneration before comparing output bytes.AuthResolver.try_with_fallback()and test both token and tokenless callback paths.Implementation (HOW)
src/apm_cli/commands/pack.pyadds--strict-metadata, exit code 5, JSON outcome reporting, and non-mutating--check-clean.src/apm_cli/core/build_orchestrator.pyseparates marketplace preparation from production so strict validation runs before any producer writes.src/apm_cli/marketplace/builder.pyaddsMetadataEnrichmentOutcomeandMetadataEnrichmentResult, retains mapping compatibility, and records auth-backed fetch outcomes.src/apm_cli/marketplace/drift_check.pyadds theuncertifiablestate and refuses byte comparison when remote metadata is incomplete.docs/src/content/docs/reference/cli/pack.md,docs/src/content/docs/producer/publish-to-a-marketplace.md,docs/src/content/docs/producer/releasing-from-any-ci.md, andpackages/apm-guide/.apm/skills/apm-usage/commands.mddocument warnings, strict mode, exit codes, and CI use.CHANGELOG.mdrecords the #2524 behavior change..apm/instructions/architecture.instructions.md,.github/instructions/architecture.instructions.md, andscripts/lint-architecture-boundaries.shestablish one metadata-outcome authority and prevent bypasses.tests/unit/marketplace/test_metadata_enrichment.py,tests/unit/marketplace/test_builder.py,tests/integration/test_remote_marketplace_metadata_outage_lifecycle.py, andtests/integration/test_architecture_authorities.pycover outcome states, auth callbacks, no-write behavior, lifecycle recovery, and static ownership.Diagrams
Legend: the dashed nodes are the new decisions that prevent incomplete metadata from being mistaken for a clean marketplace.
flowchart LR subgraph Prepare[Prepare] P1[Resolve marketplace packages] P2[Fetch metadata outcomes] end subgraph Certify[Certify] C1{Metadata certifiable} C2[Report warnings]:::new C3[Fail before writes]:::new end subgraph Produce[Produce] W1[Write marketplace outputs] W2[Compare check-clean output] end P1 --> P2 P2 --> C1 C1 -->|yes| W1 C1 -->|"no, default mode"| C2 C1 -->|"no, strict mode"| C3 C2 --> W1 C1 -->|"no, check-clean"| C3 W1 --> W2 classDef new stroke-dasharray: 5 5; class C2,C3 new;Trade-offs
apm packautomation keeps best-effort behavior, while--strict-metadataprovides an explicit fail-closed contract.--check-cleanrejects unavailable metadata even if regenerated bytes happen to match; a false clean result was rejected as less trustworthy.maintainerCanModify=false; a successor preserves commits and attribution without mutating an inaccessible branch.Benefits
--check-cleanexits 4 when metadata cannot be certified and reports the affected package.metadata_enrichment.certifiableplus per-package outcomes.Validation
git merge-tree --write-tree 7c7e08327437b251572a8146377a0894182db667 0ede4c593b7ad24d6b45da1bade375fe7013eb6c:This confirms the credited successor applies cleanly to the exact current base.
node --test packages/apm-contributor-dashboard/tests/logic.test.mjs packages/apm-contributor-dashboard/tests/server.test.mjs:PYTHONPATH=src python3 -m pytest tests/unit/marketplace/test_metadata_enrichment.py tests/integration/test_remote_marketplace_metadata_outage_lifecycle.py -q -x --tb=short:PYTHONPATH=src python3 -m pytest <two repaired prefetch callback tests>:Local gate and environment evidence
The final reserved remote CI run is required to prove the exact Shard 1 and canonical lint environments.
Scenario Evidence
tests/unit/marketplace/test_metadata_enrichment.py::test_pack_json_warns_and_strict_metadata_prevents_writes(regression-trap for #2524)tests/unit/marketplace/test_metadata_enrichment.py::test_strict_metadata_preflight_prevents_all_producer_writes--check-cleannever certifies two equally degraded marketplace documentstests/unit/marketplace/test_metadata_enrichment.py::test_drift_refuses_uncertifiable_metadata_before_comparing_outputtests/integration/test_remote_marketplace_metadata_outage_lifecycle.py::test_remote_metadata_outage_never_certifies_degraded_marketplacetests/unit/marketplace/test_builder.py::TestResolveGitHubToken::test_prefetch_metadata_resolves_token_before_fetchingtests/unit/marketplace/test_builder.py::TestResolveGitHubToken::test_prefetch_metadata_works_without_tokenHow to test
apm pack --dry-runwith remote metadata unavailable — expect success plus a package-specific metadata warning.apm pack --strict-metadataunder the same outage — expect exit 5 and no changed output bytes.apm pack --check-cleanunder the outage — expect exit 4 withcannot certify regenerated metadata.apm pack --json --dry-run— expectmetadata_enrichment.certifiableand per-package outcome entries.--check-clean— expect actualdescriptionandversiondrift to be reported.Co-authored-by: Copilot 223556219+Copilot@users.noreply.github.com