Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ Run syntax, provider/registration, manifest/package tests and coordinated instal
## Source installation and releases

- This repository is exclusively the console plugin. Its GitHub source ZIP must install directly into a Joomla site with the compatible component installed. Keep every manifest file tracked; no build, Composer run or repacking is required downstream.
- Component and plugin releases have independent versions. Keep the explicit minimum component version and shared typed runtime contract; never infer component compatibility from the plugin's own major version. Verify dependency checks against independently versioned releases.
- Do not add plugin/package builders, copied component dependencies, combined package manifests or package feeds. The component's release invokes OctoJPack using its standalone `.octojpack` configuration, which selects this plugin's latest tag. The resulting Joomla package belongs to its own repository.
- The manual Release workflow accepts the next version, freezes manifest/changelog metadata, creates an immutable tag, adds its source ZIP to this plugin's Joomla update feed, and waits for OctoShoom to commit the checksum. This workflow never invokes OctoJPack. Keep the fixed `joomengine/mcp_plugin` repository, `main` branch and feed path in the workflow; never move an existing tag.
- Use `octoleo/git-user@v2` and `octoleo/octoshoom@master` directly as actions, following their quick starts. Configure authentication once through git-user's documented secrets; let OctoShoom inherit it. Do not add custom SSH setup, action checkouts, repository discovery, hash rechecks or wrappers around the shared tools. Local release support only freezes version metadata and appends the Joomla update entry. Engine changes require an explicit request.
Expand Down
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# Changelog

## [[[NEXT_VERSION]]]

### Fix

- Allow independently versioned component and console plugin releases to install together; retain the component 0.1.1 minimum, enabled state and runtime dependency checks.

## 1.0.0

### Fix
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

PHP-only local Joomla console integration for `com_joomengine_mcp`.

Requires component version **0.1.1 or later in the same major version**, including its explicit JCB synchronization runtime.
Requires component version **0.1.1 or later**, including its explicit JCB synchronization runtime. Component and plugin release versions are independent; the shared console runtime interfaces establish compatibility.

**Element:** `joomengine_mcp`
**Group:** `console`
Expand Down
8 changes: 5 additions & 3 deletions docs/IMPLEMENTATION.md
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
# Implementation status — 28 September 2026
# Implementation status — 29 September 2026

## Branch

The migration [PR #1](https://github.com/joomengine/mcp_plugin/pull/1) is merged. First-package release readiness is on `fix/first-package-release`; the [component acceptance checklist](https://github.com/joomengine/mcp_component/pull/1#issuecomment-5732685349) tracks coordinated Joomla/JCB execution evidence.
The migration [PR #1](https://github.com/joomengine/mcp_plugin/pull/1) and first-package readiness changes are merged. Independent release compatibility is being corrected on `fix/independent-plugin-version`; the [component acceptance checklist](https://github.com/joomengine/mcp_component/pull/1#issuecomment-5732685349) tracks coordinated Joomla/JCB execution evidence.

Plugin version 0.1.0 requires component version 0.1.1 or later within the same major version, because the explicit JCB synchronization operation is part of that runtime contract.
The plugin requires component version 0.1.1 or later, because the explicit JCB synchronization operation is part of that runtime contract. Component and plugin versions are independent. Installation checks the minimum component version, enabled state and runtime autoloader; execution retains the shared ConsoleRuntimeProviderInterface and ConsoleRuntimeInterface checks.

The [v1.0.0 installed CI failure](https://github.com/joomengine/mcp_plugin/actions/runs/36455842922/job/109042215043) exposed an incorrect major-version equality check: plugin 1.0.0 rejected component 0.1.1 despite its compatible runtime. This fix removes that coupling without changing either published version or tag. A subsequent plugin release is required to distribute the corrected installer.

## Implemented runtime

Expand Down
9 changes: 9 additions & 0 deletions joomengine_mcp_changelog.xml
Original file line number Diff line number Diff line change
@@ -1,5 +1,14 @@
<?xml version="1.0" encoding="utf-8"?>
<changelogs>
<changelog>
<element>joomengine_mcp</element>
<type>plugin</type>
<folder>console</folder>
<version>[[[NEXT_VERSION]]]</version>
<fix>
<item>Allow independently versioned component and console plugin releases to install together; retain the component 0.1.1 minimum, enabled state and runtime dependency checks.</item>
</fix>
</changelog>
<changelog>
<element>joomengine_mcp</element>
<type>plugin</type>
Expand Down
4 changes: 1 addition & 3 deletions src/Installer/InstallerScript.php
Original file line number Diff line number Diff line change
Expand Up @@ -61,13 +61,11 @@ public function preflight(string $type, InstallerAdapter $adapter): bool
$row = $db->setQuery($query)->loadAssoc();
$manifest = json_decode($row['manifest_cache'] ?? '{}', true);
$componentVersion = (string) ($manifest['version'] ?? '0');
$pluginVersion = (string) $adapter->getManifest()->version;

if ($row === null || (int) $row['enabled'] !== 1 || version_compare($componentVersion, '0.1.1', '<')
|| explode('.', $componentVersion)[0] !== explode('.', $pluginVersion)[0]
|| !is_file(JPATH_ADMINISTRATOR . '/components/com_joomengine_mcp/vendor/autoload.php'))
{
throw new RuntimeException('Install and enable JoomEngine MCP component version 0.1.1 or later in the same major version before its console plugin.');
throw new RuntimeException('Install and enable JoomEngine MCP component version 0.1.1 or later before its console plugin.');
}

return true;
Expand Down
51 changes: 51 additions & 0 deletions tests/installed.php
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@
* @license GNU General Public License version 3 or later; see LICENSE
*/

use Joomla\CMS\Installer\Adapter\PluginAdapter;
use Joomla\CMS\Installer\Installer;
use Joomla\Database\DatabaseInterface;
use Mcp\Client;
use Mcp\Client\Transport\StdioTransport;
use VDM\Plugin\Console\JoomEngineMcp\Installer\InstallerScript;

$component = realpath((string) getenv('MCP_COMPONENT_SOURCE'));

Expand Down Expand Up @@ -42,6 +45,54 @@
echo 'PASS ' . $message . PHP_EOL;
};

// Exercise dependency checks against the real disposable extension registry.
require_once JPATH_PLUGINS . '/console/joomengine_mcp/src/Installer/InstallerScript.php';
$adapter = new PluginAdapter(new Installer(), $db);
$adapter->setManifest(simplexml_load_file(JPATH_PLUGINS . '/console/joomengine_mcp/joomengine_mcp.xml'));
$installer = new InstallerScript($db, $app);
$componentRecord = $db->setQuery($db->createQuery()
->select($db->quoteName(['extension_id', 'element', 'enabled', 'manifest_cache']))
->from($db->quoteName('#__extensions'))
->where($db->quoteName('type') . ' = ' . $db->quote('component'))
->where($db->quoteName('element') . ' = ' . $db->quote('com_joomengine_mcp')))->loadObject();

if ($componentRecord === null)
{
throw new RuntimeException('The installed component registry record is missing.');
}

try
{
$cache = json_decode($componentRecord->manifest_cache, true, 512, JSON_THROW_ON_ERROR);

foreach (['0.1.1' => true, '1.0.0' => true, '0.1.0' => false] as $version => $supported)
{
$record = clone $componentRecord;
$cache['version'] = $version;
$record->manifest_cache = json_encode($cache, JSON_THROW_ON_ERROR);
$db->updateObject('#__extensions', $record, 'extension_id');

foreach (['install', 'update'] as $operation)
{
$check($installer->preflight($operation, $adapter) === $supported,
'Installer ' . $operation . ' checks component ' . $version . ' independently of the plugin release version');
}
}

$record = clone $componentRecord;
$record->enabled = 0;
$db->updateObject('#__extensions', $record, 'extension_id');
$check(!$installer->preflight('install', $adapter), 'Installer rejects a disabled component');
$record->element .= '_fixture_missing_' . $record->extension_id;
$db->updateObject('#__extensions', $record, 'extension_id');
$check(!$installer->preflight('install', $adapter), 'Installer rejects a missing component');
$check($installer->preflight('uninstall', $adapter), 'Plugin removal remains possible without its component');
}
finally
{
$db->updateObject('#__extensions', $componentRecord, 'extension_id');
}

/** Run the real installed CLI with bounded pipes, a deadline and no shell. */
$run = static function (array $command, string $input = '') use ($arguments): array
{
Expand Down
Loading