Skip to content

feat!: v1.0.0 — Graycore CI, FieldDescriptor schema, demo-verified release#1

Open
DavidLambauer wants to merge 38 commits intomainfrom
feat/v1.0.0-release
Open

feat!: v1.0.0 — Graycore CI, FieldDescriptor schema, demo-verified release#1
DavidLambauer wants to merge 38 commits intomainfrom
feat/v1.0.0-release

Conversation

@DavidLambauer
Copy link
Copy Markdown

@DavidLambauer DavidLambauer commented Apr 21, 2026

Summary

Cuts mage-os/module-ai-base to a production-ready v1.0.0:

  • CI: Graycore check-extension reusable workflow pinned at v5.1.0, matrix-targeted at project: mage-os so every PR gets tested against the current Mage-OS supported-version matrix.
  • Breaking API cleanup: AiServiceConfigurationInterface::getConfigurationTemplate(): stringgetConfigurationFields(): FieldDescriptorInterface[] + getSupportedModels(): array. Eleven AiServices/* classes rewritten to the new schema; stored-config on-disk format unchanged so AiServiceSelectorInterface consumers are fully backwards-compatible.
  • Admin form hardening: phtml client-side escapeHtml() on every schema-sourced value, input-type whitelist, and legacy-stored-value preservation when the model list has changed. Block\Adminhtml\Configuration\Services marked final with runtime instanceof guard.
  • Selector hardened: four-guard defensive parsing in Model\AiServiceSelector against null scope values / malformed JSON / malformed rows / bad row shapes. All four covered by unit tests.
  • Tests: 21 unit tests, 154 assertions across AiServiceSelector, FieldDescriptor, and the parametrised ServicesTest hitting all eleven providers. One integration test covers full config round-trip through ScopeConfigInterface with failure-safe cleanup.
  • Demo-verified: installed into the Mage-OS 2.2.0 demo, admin UI loads all 11 service buttons, save round-trip confirmed (password masking + correct select rehydration), consumer API resolves 2 saved services with correct codes and configuration.

Design + plan docs: docs/plans/2026-04-20-done-done-design.md and docs/plans/2026-04-20-done-done-plan.md.

CI status

3 of 5 jobs pass. The two failing jobs are pre-existing Graycore v5.1.0 infrastructure issues unrelated to this module's code — they would fail on any Mage-OS module using the @v5.1.0 reusable workflow.

Job Status Notes
compute-matrix ✅ pass
check-extension / compile-extension ✅ pass Module's di.xml + etc/* configs compile cleanly on a fresh Mage-OS 2.1.0 install
check-extension / unit-test-extension ✅ pass 21 tests, 154 assertions green
check-extension / coding-standard ❌ fail Graycore v5.1.0 doesn't handle mage-os-namespaced packaging (magento/magento-coding-standard aliases to mage-os/magento-coding-standard; Graycore hardcodes the vendor/magento/* path in installed_paths, so the Magento2 sniff can't be found). v6.0.0 didn't fix this; v5.1.0 is still the least-broken tag. Locally composer validate --strict passes and phpcs --standard=Magento2 src/ works through the auto-installed paths.
check-extension / integration_test ❌ fail Graycore v5.1.0 references matrix.services in its reusable template at line 202 even when include_services: false (the default from supported-version). Fails in 2s before any setup runs. v6.0.0 introduced a template-validation regression that made this worse, so we stayed on v5.1.0.

Upstream follow-up: both issues should be filed against graycoreio/github-actions-magento2 for mage-os-ecosystem support. This PR ships with the test coverage that matters (unit + DI compile) on green, and documents the infra failures as known-issues.

Test plan

  • check-extension / compile-extension passes on Mage-OS 2.1.0 in CI.
  • check-extension / unit-test-extension passes (21 tests, 154 assertions).
  • Demo smoke test: installed into /Users/david/Herd/mage-os-typesense (Mage-OS 2.2.0), admin UI loads all 11 service buttons, save round-trip works (password masked, select correctly rehydrated), AiServiceSelectorInterface::getAll() returns 2 configured services with correct codes + configuration, getByCode() filters correctly.
  • composer validate --no-check-publish --strict exits 0.
  • Post-merge: tag v1.0.0, draft GitHub Release with the CHANGELOG 1.0.0 body, submit repo to packagist.org.

Out of scope for v1.0.0

  • No HTTP calls to any provider (consumer modules do that).
  • No release-please workflow (deferred).
  • No ui_component rewrite of the admin form (rejected in design doc §3).
  • Model lists ship as curated defaults; admins override per-install via <preference>.
  • Graycore coding-standard + integration_test job compatibility with mage-os-namespaced packages — upstream fix needed.

Documents the two-interface split (AiServiceConfigurationInterface vs
AiServiceInterface), the stored-config data flow, and how to register
a new AI backend. Flags the README's mis-attributed API example.
Captures the four locked brainstorming decisions (full release scope,
getSupportedModels method, FieldDescriptor schema, one-off tag) and
spells out CI, tests, release mechanics, and demo smoke test.
12 tasks decomposed into TDD-style steps with exact file paths, full
code, and commit boundaries. Task 7 consolidates the breaking
interface change with the 11 service rewrites and admin form update
into a single atomic commit so the repo never lands in a broken state.
Packagist convention is to derive the version from git tags, not a
hardcoded composer.json field. Leaving it in triggers a warning that
composer validate --strict escalates to a failure. Task 12 already
handles tagging as the source of truth.
Packagist derives the version from git tags (see Task 12). Leaving a
hardcoded version field triggers a warning that composer validate
--strict escalates to a failure.
Reviewer flagged CHANGELOG hygiene (Unreleased + compare-links +
Keep-a-Changelog preamble), .gitattributes for export-ignore of dev
paths, and a LICENSE split decision. All Minor, all batched for the
pre-tag polish commit.
Graycore's supported-version action defaults to project:
magento-open-source. Without this override the check-extension
matrix tested against Magento Open Source versions, not the
Mage-OS versions this module is actually built against.
Replaces the Laravel-style array_first() with native array_key_first()
and guards every boundary (null scope value, non-string config, failed
json_decode, non-array rows, missing code key) before constructing
AiService DTOs. Covered by 4 new unit tests.
…tract

Adds a parametrised negative test for the 'malformed row' and 'bad row
shape' guards so a future refactor can't silently remove them. Also
documents on AiServiceSelectorInterface that results are returned in
insertion order — the positional assertions in the unit and integration
tests depend on that being a real contract, not an incidental behaviour.
BREAKING CHANGE: AiServiceConfigurationInterface::getConfigurationTemplate()
is removed. Implementers now return FieldDescriptor[] from getConfigurationFields()
and (optionally) a model list from getSupportedModels().

Admin form phtml now renders fields by type from a JSON schema rather than
substituting HTML template strings. Storage format is unchanged, so the
AiServiceSelector consumer API is fully backwards compatible.
Code review follow-ups from Task 7:
- escapeHtml() helper wraps every schema-sourced string emitted into the DOM
  via innerHTML-style concatenation. Whitelists input types to password/text.
- Select-field rehydration preserves stored values that are no longer in the
  options list by prepending a synthetic '(legacy)' option, preventing silent
  data loss when model lists get refreshed.
- Services block is now final and validates that each injected service
  implements AiServiceConfigurationInterface at construction time, with the
  closure in getServicesSchemaJson gaining the corresponding type hint.
Review follow-up on Task 9:
- Fixture delete now lives in tearDown() so a failed assertion cannot
  leak config rows into the test DB.
- Cache-clean uses Magento\Framework\App\Config directly rather than
  calling ->clean() on a ScopeConfigInterface-typed variable, which
  is not a method on that interface.
AiService gains final, and AiServiceInterface::getConfiguration() gets
an array-shape docblock. registration.php only needed the declare line.
…GELOG hygiene

composer.json support URLs and CHANGELOG compare-links now point at
the real mage-os-lab org. CHANGELOG gains Keep-a-Changelog preamble,
[Unreleased] section, compare-link footer, and refreshes the 1.0.0
body to reflect review follow-ups (guard coverage, final Block,
admin-form hardening). Release date bumped to 2026-04-21.

.gitattributes keeps .github/, docs/, src/Test/, phpcs.xml.dist,
phpunit.xml.dist, and CLAUDE.md out of the tarball Packagist serves
from a tagged release.
Graycore's check-extension reusable workflow defaults
magento_repository to https://mirror.mage-os.org/, but that mirror
only proxies magento/* packages. The mage-os/project-community-edition
metapackage (emitted by supported-version) is published to
https://repo.mage-os.org/, so composer create-project fails with
"Could not find package mage-os/project-community-edition" and all
four check-extension jobs abort in <15s with zero test signal.

Overriding magento_repository on the reusable workflow call points
Composer at the correct mage-os Composer repository. Staying pinned
at @v5.1.0 (supply-chain) - the matrix itself is fine, only the
repo URL was wrong.
Move Test/ to package root so Graycore's PHPUnit test-discovery
(...vendor/<pkg>/Test/Unit) locates our suites. Add the mage-os
composer repo so the coding-standard job's composer install at
module root can resolve magento/framework and mage-os/magento-
coding-standard.

Namespace (MageOS\\AiBase\\Test\\*) is unchanged; only the autoload-
dev path mapping shifts from src/Test/ to Test/.
coding-standard job installs magento/magento-coding-standard +
magento/php-compatibility-fork at module root, which pulls in
magento/framework. That lives on mirror.mage-os.org (serves
magento/* namespace), not repo.mage-os.org (serves mage-os/*).
Adding both repos with mirror listed first.
With mirror.mage-os.org added, composer require pulls in
magento/composer-dependency-version-audit-plugin as a transitive
dependency. Composer 2.2+ requires explicit allow-plugins opt-in,
and the coding-standard job was aborting on the interactive prompt.

Also pre-allows dealerdirect/phpcodesniffer-composer-installer
(used by magento-coding-standard) and magento/composer-root-update-plugin
so future installs don't re-prompt.
v6.0.0 adds explicit MageOS 2.2.0 to the supported-version matrix,
which matches our target demo environment. Also potentially fixes
two v5.1.0 bugs: coding-standard hardcoding vendor/magento/* paths
instead of handling mage-os-namespaced packages, and integration_test
referencing undefined matrix.services when include_services is false.

Re-pinning to a tag (not @main) keeps supply-chain risk bounded.
v6.0.0 introduced a template-validation regression in the
integration_test job (line 202 references an unset include_services
value) and didn't fix the coding-standard Magento2-sniff-not-found
issue either. v5.1.0 had 3/5 jobs passing; v6.0.0 had the same 2
failures plus one new one. Net regression.

Reopening the upstream-compat question as a follow-up; v5.1.0 ships.
@DavidLambauer
Copy link
Copy Markdown
Author

CI status — parked pending upstream Graycore fixes

Filed both blocking failures upstream as reproducible bugs:

  • coding-standardgraycoreio/github-actions-magento2#213 — workflow hardcodes vendor/magento/* path for installed_paths, overwriting the dealerdirect auto-installer's correct vendor/mage-os/* path, so the Magento2 sniff can't resolve.
  • integration_testgraycoreio/github-actions-magento2#214 — reusable template references matrix.services which isn't emitted when include_services: false (the default). Runtime-undefined on v5.1.0, template-validation error on v6.0.0.

Both reproduce on any Mage-OS module that consumes the Graycore reusable workflow; neither is specific to this PR.

This PR sits until Graycore ships a patch that makes those two jobs pass against mage-os packaging. In the meantime:

  • ✅ The module itself is correct — DI compiles cleanly on Mage-OS 2.1.0 (compile-extension job green), all 21 unit tests pass (unit-test-extension job green, 154 assertions).
  • ✅ End-to-end demo-verified in Task 11 via Playwright against Mage-OS 2.2.0 (admin UI save round-trip, selector consumer API, null-guard branch).

Release path is blocked only by the CI infra, not by any code correctness question.

@DavidLambauer
Copy link
Copy Markdown
Author

Added GitHub Community Standards in the latest 8 commits (793c8f9..45fe0a4):

  • CODE_OF_CONDUCT.md (Contributor Covenant 2.1)
  • CONTRIBUTING.md
  • SECURITY.md (GitHub Private Vulnerability Reporting + fallback emails david@run-as-root.sh, security@mage-os.org)
  • .github/ISSUE_TEMPLATE/{config,bug_report,feature_request}.yml
  • .github/PULL_REQUEST_TEMPLATE.md
  • README.md: Contributing + Security sections linked

Design and plan: docs/plans/2026-04-21-community-standards-*.md.

Before merge: enable Private Vulnerability Reporting in repo Settings > Code security and analysis, otherwise the link in SECURITY.md won't resolve.

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