You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
feat: support catalog-installed external agent adapters - #4862
Support trusted, catalog-installed external AI-agent adapters without source-registry edits, pip installation, or copied core command inventories. A standalone archive containing root integration.yml and __init__.py exports a matching IntegrationBase subclass and renders Spec Kit's host templates through the existing integration bases.
The adapter-only descriptor requires identity, version, metadata, and host/tool requirements. Existing optional provides metadata remains compatible. Catalog identity/version/metadata, package descriptors, class configuration, checksums, source policy, download restrictions, and safe extraction are validated before installation.
Executable packages and provenance are stored separately from generated-file manifests. Fresh CLI processes load verified, trusted project-local implementations before rendering, registration, selection, status, or workflow dispatch; catalog metadata operations do not import adapter code. Project changes unload synthetic imports and refresh registry/configuration caches.
Lifecycle operations preserve existing built-in and generic behavior, active-integration handling, extension/preset contributions, script variants, and edited-file preservation. Failed operations restore only operation-owned writes, not independent workflow progress or unowned user files. Concurrent managed-file conflicts retain explicit recovery snapshots. Forced upgrade/uninstall can recover a damaged target using validated ownership metadata without bypassing source policy or replacement trust.
Tests use neutral sample-agent packages and loopback HTTP fixtures. Directly related integration design, catalog, contribution, and reference documentation describes the final contract and public commands. The branch is rebased onto upstream main.
Testing
Tested locally with uv run specify --help
Ran existing tests with uv sync && uv run pytest
Tested with a sample project (if applicable)
The full suite uses this worktree's own virtualenv, per CONTRIBUTING.md, rather than the literal uv run pytest checklist command, which can resolve an editable install from another checkout.
Command/check
Result
uv sync --extra test
Passed; local distribution metadata matches the rebased 1.1.2.dev0 manifest
ruff check --select F src/specify_cli/presets/_manager.py src/specify_cli/presets/_manager_commands.py src/specify_cli/presets/_manager_skills.py src/specify_cli/shared_infra.py
Passed
git diff upstream/main...HEAD --check
Passed
uv build --out-dir dist
Passed; source and wheel distributions built; temporary outputs removed
The 114 adapter cases cover adapter-only descriptors, actual catalog installation, trust denial/discovery-only sources, fresh-process loading, host command/skill rendering, extension/preset registration and cleanup, runtime dispatch with harmless process doubles, upgrades/uninstall, edited files, rollback, invalid metadata/classes/imports, built-in collisions, unsafe paths/symlinks, damaged-target recovery, and project/cache isolation.
Sixteen scope regressions were also run against the earlier implementation: all failed before the fixes and passed afterward. Sample-project scaffolding/lifecycle checks use temporary executable/process doubles, not authenticated model execution.
AI Disclosure
I did not use AI assistance for this contribution
I did use AI assistance (fill in the disclosure below)
AI disclosure: Implemented with GitHub Copilot, powered by GPT-6.1 Sol (gpt-6.1-sol), in autonomous agent mode; reasoning-effort setting was not explicitly selected. AI assistance covered investigation, implementation, regression tests, documentation, rebase, validation, and PR-description drafting. No human line-by-line review is claimed.
Use the rebased 1.1.2 development version as the minimum in external adapter examples rather than implying the new catalog-install capability ships in an earlier release.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Portable member validation now runs before filesystem access for adapter output paths and retained package entries, rejecting Win32 aliases, device names, invalid characters, and oversized components.
Authoritative consent is user-local in ~/.specify/integration-trust.json, bound to the canonical project root, adapter ID, and verified full-package digest. Project trusted metadata cannot authorize execution, and cache reuse rechecks consent. Managed ignore rules exclude executable packages and their provenance registry. Copied projects can reauthorize through a reviewed, install-enabled catalog using integration upgrade sample-agent --force --trust-integration.
Artifact list/info/lookup load trusted adapter configuration, resolve materialized extension/preset output in fresh processes, and retain a single JSON error envelope with useful adapter failure details.
Stable lifecycle locks are user-local and project-keyed rather than creating target scaffolding before init confirmation. Failed external initialization uses the scoped journal, removes only an empty newly created root, and preserves independent files.
Forced uninstall recovers an entirely absent package directory. Other filesystem errors remain explicit; an unchanged package is not destructively recopied during rollback after denied removal.
Added 41 adapter cases without removing existing tests. The initial targeted run reproduced 31 failures before fixes; final focused validation passed all 308 adapter/artifact/shared-ignore cases. Windows CI also exposed UTF-8 decoding errors in the adapter test harness; rendered-file reads and captured subprocess output now explicitly use UTF-8.
The new-head platform CI is separate from these local results. Reviewer conversations are left open for the reviewer.
Posted on behalf of @mnriem by GitHub Copilot, powered by GPT-6.1 Sol (gpt-6.1-sol), in autonomous mode; reasoning effort was not explicitly selected. AI authored the fixes, tests, documentation, and this response. No human line-by-line review is claimed.
Host writes now reject symlinked destinations and ancestors before directory creation or writes; snapshots and forced removal preserve/unlink owned leaf links without traversing their targets. Registry loading and registrar configuration snapshots are synchronized, recursive-load suppression is context-local, and runtime dispatch pins each project's adapter and verified imports without serializing independent agent processes.
Damaged-adapter cleanup uses user-local registrar/path ownership bound to the previously trusted package, rejecting edited project claims and overlap with other registered integrations, including legacy destinations. Copied projects and older grant-only stores preserve old-only artifacts with an explicit manual-cleanup warning. Ownership advances only after durable loading succeeds, so failed upgrades retain the previous recovery authority. Execute/resume reload OSError failures now produce one JSON failure envelope with no stderr.
Added 25 regressions, including overlapping prompt/command dispatch with lazy relative imports, cleanup after failed processes, forged recovery claims, missing local ownership, leaf-link snapshots, and failed durable-upgrade rollback. Before-fix runs reproduced the original eleven regressions; additional before/after checks reproduced the snapshot-traversal and recovery-ownership rollback gaps. Also corrected the prior Windows assertion to check directory entries rather than querying a Win32 alias for an invalid pathname. Existing fresh-process artifact/extension/preset registration coverage remains passing.
Source and wheel builds also passed, with build artifacts kept outside the checkout. New cross-platform CI is pending; Windows was not executed locally.
Posted on behalf of @mnriem by GitHub Copilot (model: GPT-6.1 Sol, autonomous mode). The implementation, regression tests, documentation, validation, and this response were AI-generated/executed; no human line-by-line review is claimed.
Addressed the six new findings in review 5435405580 in commit 8678402f3b066bde9ff921ba2e0e3ef548579177, pushed to the existing PR branch.
Validate optional legacy output directories for type, canonical project-relative paths, symlinks, and reserved roots; reject home-relative external output syntax.
Compare current and legacy output roots using case-folded path components, including other integrations' legacy roots.
Load and pin the project's installed adapters during native event refresh, including fresh-process event-only extension add/remove and enable/disable. Adapter-load failures are reported through EventRefreshError.
Atomically load and snapshot extension/preset registrar configuration. Extension candidate folders come from pinned integration metadata rather than stale global configuration. Preserve generic cleanup with missing/malformed settings and the existing exclusion of generic preset registration.
Catch missing workflow runs before broader I/O failures, preserving text/JSON diagnostics. Normalize adapter-loading filesystem failures at the engine boundary so a missing adapter file is not incorrectly reported as a missing run.
Added 30 regression cases. The corrected before-fix run reproduced 23 failures with one passing positive control. An additional regression caught the missing-adapter/missing-run classification conflict during remediation. Full-suite validation also caught three generic behavior regressions introduced by the initial root-aware snapshot change; those were fixed without removing or weakening the existing cases.
uv build --out-dir with a session-artifact directory outside the checkout
Source distribution and wheel built successfully
.venv/bin/specify --help
Passed
git diff --check
Passed
The earlier workflow-dispatch and artifact-resolution findings remain covered by the published scoped-dispatch/root-aware resolution changes and passing regressions. Scoped dispatch pins both project lookup and verified lazy-import namespaces without serializing parallel agent execution. Artifact list/info/lookup loads the project adapter, uses a root-aware registrar, and retains JSON error envelopes for damaged packages.
The PR head matches the pushed commit. Cross-platform CI is pending for this revision. Reviewer conversations remain unresolved for the reviewer to reassess.
AI disclosure: Posted on behalf of @mnriem by GitHub Copilot, powered by GPT-6.1 Sol, operating autonomously. The agent authored the fixes, tests, documentation, commit, and this response and executed the reported checks. No human line-by-line review is claimed.
The documented external-catalog workflow is still contradicted by integration search: for an install-enabled external entry, command_search.py:98-104 prints “Only built-in integration IDs can be installed,” and test_command_search.py:32-33 still asserts that obsolete behavior. Update search to advertise specify integration install <id> when the source permits installation, while keeping discovery-only entries blocked.
Rollback misses direct writes during transactional lifecycle commands
src/specify_cli/integrations/_lifecycle.py:234
The rollback journal only records writes that call these observer hooks, but lifecycle commands still perform unobserved mutations. For example, switching from an external adapter to Copilot can merge an existing .vscode/settings.json via a direct write_text (integrations/copilot/__init__.py:680); if the later package-registry commit fails, _restore_snapshots has no journal entry for that file, so the failed switch leaves the user's settings changed. Instrument every write reachable inside the transaction (including existing built-in and extension/preset paths), or make the transaction restore those touched scopes independently of hook coverage.
Addressed review 5436613967 in commit a72c6bc15a0a1a73a3d1105b89005ff3f4edd19a, pushed to the existing PR branch.
Workflow engine construction no longer loads executable adapters. workflow status (text, all-runs JSON, and individual-run JSON) and workflow info inspect metadata even when an adapter is damaged. Run/resume load adapters inside their existing structured-error boundary and reload immediately before execution. Existing dispatch, cross-project, and JSON failure regressions remain passing.
Catalog search now advertises specify integration install <id> for install-enabled entries, including external adapters. Discovery-only results do not advertise installation; search does not import candidate code. Updated the obsolete assertion and added real registered-catalog policy regressions.
Expanded the mutation journal coverage rather than restoring entire directories indiscriminately. Host settings merges/copies, native event writes/removals, legacy migrations, extension skill/dev caches, registrar dev caches, preset composition caches, and built-in post-processing now notify the journal. Existing built-in home-scoped output is permitted only for participating built-in destinations and receives the same safe-path and concurrent-edit checks. Project-local detection markers also roll back. Merged user settings remain unowned by uninstall; concurrent edits are preserved and reported with recovery snapshots.
Added 21 cases, including positive settings ownership behavior and negative rollback/metadata/discovery coverage. Before-fix evidence captured 11 failures for the initial findings. Running the rollback regressions against the published 8678402f source snapshot reproduced eight failures, and a separate comparison isolated the preset cache mutation and missing concurrent-merge warning. These failures are fixed in the final suite.
21 passed before the final marker-specific refinement; all 21 also pass in the final full suite
Affected integration/workflow/event/extension/preset and built-in test selection
2,000 passed before the final additional cases/refinements; covered again by the full suite
uvx --offline ruff@0.15.0 check src tests
Passed
uv build --out-dir with a session-artifact directory outside the checkout
Source distribution and wheel built successfully
.venv/bin/specify --help
Passed
git diff --check
Passed
The remaining event-refresh finding was already implemented in 8678402f: refresh loads/pins the project adapter itself, and fresh-process event-only add/remove/enable/disable regressions pass. This revision additionally journals the native configuration mutations during transactional integration lifecycle operations. Reviewer conversations remain unresolved for reassessment.
AI disclosure: Posted on behalf of @mnriem by GitHub Copilot, powered by GPT-6.1 Sol, operating autonomously. The agent authored the fixes, tests, documentation, commit, and this response and executed the reported checks. No human line-by-line review is claimed.
Validate invoke_separator and dev_no_symlink class attributes
src/specify_cli/integrations/installer.py:563
Class-level invoke_separator and dev_no_symlink bypass _validate_registrar_config, yet they are copied into agent configuration and persisted recovery metadata. An adapter can therefore pass installation with invoke_separator=None (later breaking command-reference rendering) or a truthy non-boolean dev_no_symlink. Validate these public class attributes alongside multi_install_safe.
Bind local recovery ownership to verified package hashes and require its local trust grant. Preserve legacy bindings and damaged-package cleanup while rejecting substituted recovery identities and invalid public adapter attributes.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
The implementation validator now checks class-level invoke_separator (non-empty string) and dev_no_symlink (boolean), even when registrar configuration has optional overrides. Invalid values fail installation before they can enter rendering or persisted recovery configuration.
New local recovery records retain their verified package hashes. Recovery recomputes the package identity using those local hashes, the project root, and the adapter key, then requires the corresponding local trust grant. This deliberately does not compare against mutable project hashes: forced cleanup must still work when an installed package is missing, damaged, incompatible, or fails import. Substituted identities, hash mappings, and cross-project bindings fail explicitly. Legacy hash-less bindings remain supported with a local grant; revoked grants cause generated files to be preserved through the existing ownership-unavailable warning path. Added positive and negative coverage for both attribute propagation and recovery.
The event-refresh item remains addressed by the existing project_integrations(project_root) scope in refresh_integration_events, with fresh-process extension-event regression coverage included in the passing suite. No reviewer conversations were resolved.
Regression evidence: the initial 13 new cases produced 12 failures and one legacy-compatibility pass on a72c6bc before the fixes. All 17 new cases now pass within the complete adapter suite.
Source and wheel distribution builds also passed, with artifacts stored outside the checkout. Requesting another review after this update.
AI disclosure: posted on behalf of contributor @mnriem by GitHub Copilot, model GPT-6.1 Sol, in autonomous mode. The agent authored the implementation, regression tests, documentation changes, commit, and this response, and executed the validation and publication commands. No human line-by-line review is claimed.
Escape the package-provided display name before interpolating it into Rich markup. Unlike the catalog branch above, this offline installed-package fallback uses validated-but-unescaped metadata, so a legitimate name containing Rich syntax (or an unmatched closing tag) can be rendered incorrectly or raise MarkupError instead of showing integration info.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Patch the gate step class instead of the shared registry instance in dispatch-error regressions. Restoring an instance-level monkeypatch left a bound execute attribute that hid subsequent class-level mocks, causing CI's resume workflow-directory test to remain paused.
Reproduced the CI order locally: twelve new cases pass but the following existing resume test fails before this correction; all thirteen pass afterward. Running the dispatch regressions before the complete workflow suites in one process now passes all 1529 tests. CI-pinned Ruff and git diff --check pass; no tests were removed.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Clean up temporary registry files when serialization fails
src/specify_cli/integrations/installer.py:303
The cleanup finally starts only after the temporary registry file has been fully serialized. If json.dump() or stream.write() fails (for example, disk exhaustion or a non-serializable accepted registrar value), control never reaches that finally, leaving an untracked temporary file under .specify/integrations; the lifecycle journal does not remove unobserved files. Include creation and serialization in the cleanup scope so failed installs/upgrades do not leave residue.
Reject mandatory host parameters in adapter signatures
src/specify_cli/integrations/installer.py:615
The signature check accepts implementations where all five host keyword parameters are mandatory, because bind() supplies every one here. That adapter still fails later: specify check and the workflow command/prompt steps intentionally pass only subsets of these keywords, producing a TypeError instead of dispatching. Reject explicitly declared host parameters without defaults (while continuing to allow **kwargs), or make every host call pass the complete signature.
🧠 Review effort: Balanced
Give feedback about Copilot approvals in this survey to enter a drawing for a $150 gift card.
Address all seven findings in review 5459486464. Validate lexical containment and reject symlinked ancestors before manifest cleanup reads and immediately before unlinking, including direct built-in uninstall and stale upgrade cleanup without a journal. Preserve owned leaf-link removal and report unsafe files as skipped.
Escape external display names in info fallback, normal/catalog lists, and install/switch/uninstall/upgrade success messages. Regression cases cover unmatched tags, balanced styling, hyperlinks, plain-name controls, outside and in-project symlink targets, observer-time parent replacement, and built-in cleanup.
Validation: 33 new cases fail at reviewed commit 65b4b29 and seven controls pass; all 40 pass after fixes. Full suite LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q --tb=short passed: 10340 passed, 19 skipped. CI-pinned Ruff and git diff --check passed. Total collection increased from 10319 to 10359.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Detect permission-only changes during rollback conflict checks
src/specify_cli/integrations/_lifecycle.py:77
Rollback conflict detection ignores permission-only edits. If another process runs chmod after this operation writes a managed file and the lifecycle later fails, the hash still matches written_identity, so _restore_snapshots() deletes the current file and restores the snapshot's old mode without reporting a conflict. Include the permission bits in the identity and add a rollback regression covering a concurrent mode change.
Bring current upstream main into the external-adapter branch without rewriting history. The upstream artifact.info shared-operation refactor discarded explicit ArtifactResolutionError diagnostics and caused two existing fresh-process damaged-adapter CI regressions. Preserve those typed domain messages in the shared operation while retaining generic I/O/preset error behavior and structured error codes/details.
Reproduced both CI failures against the unmodified merge result; corrected shared-operation behavior and added a diagnostic-preservation test. Selected artifact suites: 283 passed. Full merged suite: LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q --tb=short passed with 10762 passed and 18 skipped. CI-pinned Ruff and git diff --check passed; 10780 tests collected. No PR description edits or history rewrites.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Address review 5460558986. Include preset/extension package roots, extension project config, and bundled workflow source in the lifecycle census. Observe package replacement and its backup/rescue directories through complete install, coalescing descendant writes into the bounded package snapshot so late adapter commit failures remove new sources or restore original packages and configuration.
Regression evidence: twenty cases fail on reviewed commit f59559b and nine successful-init controls pass; all twenty-nine pass after fixes. Full local suite LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q --tb=short passed with10791 passed and18 skipped;1479 preset/extension manager tests pass. No-op snapshot and independent-workflow controls pass. CI-pinned Ruff and git diff --check pass; total collection increased10780 to10809.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Preserve directory symlink type during snapshot and rollback
src/specify_cli/integrations/_lifecycle.py:149
On Windows, Path.symlink_to() defaults to target_is_directory=False, so snapshotting a leaf symlink to a directory records it as a file symlink. The restore path at line 244 also recreates every saved link with that default. If a later lifecycle step fails after replacing/removing a directory link, rollback can therefore fail or restore the wrong reparse-point type, contrary to the cross-platform rollback contract. Preserve the link kind in the snapshot metadata and pass it when both creating the backup link and restoring it.
Retain supplied run ID when loading execution state fails
If RunState.load() raises an OSError (for example, a permission/read failure), _execution_state is still None, so this emits a JSON envelope with "run_id": null even though resume was invoked with a known run ID. Pre-state resume failures should retain the supplied run_id (as the adapter-load and missing-run branches do) so automation can correlate the failure; pass run_id through the state-less failure path.
Fix native Windows CI verifier failures caused by decoding UTF-8 generated skills with the default code page. Read the generated skill explicitly as UTF-8 and exercise the init rollback matrix with simulated cp1252 defaults for omitted encoding. No production rollback code or test skips changed.
All 56 focused init rollback/force-replacement cases pass with native and simulated Windows encodings. CI-pinned Ruff and git diff --check pass; collection increased from 10809 to 10836. The preceding production commit's full local suite passed 10791 tests with 18 skips; this follow-up is test-only.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Forced upgrade cannot recover when the recorded package directory has been replaced by a regular file or leaf symlink. The initial load correctly enters damaged-adapter recovery, but this strict path check rejects a leaf symlink and shutil.rmtree() rejects a regular file, so the replacement never reaches os.replace(). Validate ancestors while allowing the owned leaf, then unlink non-directory leaves before installing the staged directory.
Forced uninstall fails on regular-file or symlink package leaves
src/specify_cli/integrations/installer.py:1004
Forced uninstall has the same damaged-target gap: a package path replaced by a regular file makes rmtree() raise, while a leaf symlink is rejected before cleanup. Since records[key] and validated local recovery metadata establish ownership of this package leaf, unlink the leaf without following it while continuing to reject symlinked ancestors.
Address all four findings in review 5461371377. Complete package copy/config-restore observations before registration and commit, including copy failures. Coalesce later descendant writes into the directory snapshot while rejecting concurrent changes except newly created empty destination parents. Journal forced config restoration and cleanup at their actual boundaries.
Per the user's explicit decision, preset/extension install errors abort external init atomically; builtin init outside a lifecycle transaction retains best-effort optional installs. Forced adapter upgrade/uninstall now allow owned regular-file and symlink package leaves, unlinking them without following targets and continuing to reject symlinked ancestors. Failed commits restore the original damaged leaf.
Validation: 35 regression cases fail at reviewed commit 910ad95 and five healthy/negative controls pass; all 45 focused cases pass after fixes. Full suite LC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q --tb=short passed: 10863 passed, 18 skipped. CI-pinned Ruff and git diff --check passed; collection increased from 10836 to 10881.
Assisted-by: GitHub Copilot (model: GPT-6.1 Sol, autonomous)
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
triage-nice-to-haveVerdict: evidence-backed fix or greenlit feature — land after review
2 participants
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.
Description
Support trusted, catalog-installed external AI-agent adapters without source-registry edits, pip installation, or copied core command inventories. A standalone archive containing root
integration.ymland__init__.pyexports a matchingIntegrationBasesubclass and renders Spec Kit's host templates through the existing integration bases.The adapter-only descriptor requires identity, version, metadata, and host/tool requirements. Existing optional
providesmetadata remains compatible. Catalog identity/version/metadata, package descriptors, class configuration, checksums, source policy, download restrictions, and safe extraction are validated before installation.Executable packages and provenance are stored separately from generated-file manifests. Fresh CLI processes load verified, trusted project-local implementations before rendering, registration, selection, status, or workflow dispatch; catalog metadata operations do not import adapter code. Project changes unload synthetic imports and refresh registry/configuration caches.
Lifecycle operations preserve existing built-in and generic behavior, active-integration handling, extension/preset contributions, script variants, and edited-file preservation. Failed operations restore only operation-owned writes, not independent workflow progress or unowned user files. Concurrent managed-file conflicts retain explicit recovery snapshots. Forced upgrade/uninstall can recover a damaged target using validated ownership metadata without bypassing source policy or replacement trust.
Tests use neutral
sample-agentpackages and loopback HTTP fixtures. Directly related integration design, catalog, contribution, and reference documentation describes the final contract and public commands. The branch is rebased onto upstreammain.Testing
uv run specify --helpuv sync && uv run pytestThe full suite uses this worktree's own virtualenv, per
CONTRIBUTING.md, rather than the literaluv run pytestchecklist command, which can resolve an editable install from another checkout.uv sync --extra test1.1.2.dev0manifestuv run specify --helpLC_ALL=en_US.UTF-8 .venv/bin/python -m pytest tests -q --tb=shortruff check src/specify_cli/integrations/_file_changes.py src/specify_cli/integrations/_lifecycle.py src/specify_cli/integrations/installer.py tests/specify_cli/integrations/test_installed_adapters.pyruff check --select F src/specify_cli/presets/_manager.py src/specify_cli/presets/_manager_commands.py src/specify_cli/presets/_manager_skills.py src/specify_cli/shared_infra.pygit diff upstream/main...HEAD --checkuv build --out-dir distThe 114 adapter cases cover adapter-only descriptors, actual catalog installation, trust denial/discovery-only sources, fresh-process loading, host command/skill rendering, extension/preset registration and cleanup, runtime dispatch with harmless process doubles, upgrades/uninstall, edited files, rollback, invalid metadata/classes/imports, built-in collisions, unsafe paths/symlinks, damaged-target recovery, and project/cache isolation.
Sixteen scope regressions were also run against the earlier implementation: all failed before the fixes and passed afterward. Sample-project scaffolding/lifecycle checks use temporary executable/process doubles, not authenticated model execution.
AI Disclosure
AI disclosure: Implemented with GitHub Copilot, powered by GPT-6.1 Sol (
gpt-6.1-sol), in autonomous agent mode; reasoning-effort setting was not explicitly selected. AI assistance covered investigation, implementation, regression tests, documentation, rebase, validation, and PR-description drafting. No human line-by-line review is claimed.