Skip to content

catalog: import full platform engineering product catalog - #10

Merged
srbouffard merged 67 commits into
mainfrom
chore-add-missing-products
Jul 21, 2026
Merged

catalog: import full platform engineering product catalog#10
srbouffard merged 67 commits into
mainfrom
chore-add-missing-products

Conversation

@srbouffard

Copy link
Copy Markdown
Collaborator

Summary

This PR imports the complete current Platform Engineering product catalog from internal docs into PQF product definitions, and adds temporary migration tooling to keep imports repeatable until source-of-truth cutover is complete.

What changed

  • Imported all current products from canonical/platform-engineering-docs:data/products into products/*.yaml
  • Added temporary tooling:
    • tools/fetch_platform_engineering_docs_products.py
    • tools/import_platform_engineering_docs_products.py
  • Added Make targets for migration workflow:
    • catalog-discovery-fetch
    • catalog-import-products
    • catalog-discovery-report
  • Regenerated discovery artifact:
    • docs/superpowers/artifacts/2026-07-20-product-catalog-discovery.json
  • Added monorepo support in import mapping (source.subpath from GitHub tree paths)
  • Added import tests:
    • engine/__tests__/test_import_platform_engineering_docs_products.py
  • Updated product schema ID regex to allow IDs starting with digits (needed for 12-factor)

Result

Discovery now reports full parity with current docs catalog:

  • docs_count: 34
  • pqf_count: 34
  • missing_in_pqf: []

Temporary migration boundary (intentional)

Docs fetch/import logic is isolated in tools/ + dedicated Make targets and is not part of normal engine scoring flow.
This is temporary and designed for clean removal after cutover.

Validation

  • make catalog-import-products
  • make catalog-discovery-report
  • make lint
  • make test ✅ (180 passed)
  • make test-ui ✅ (70 passed)

Known migration assumptions

  • Product ownership mapped to ownership.squad (+ stakeholder names where available)
  • Private/non-public docs fields remain excluded from PQF migration surface
  • Non-GitHub Mattermost source is mapped via explicit override to canonical GitHub repo

Follow-up checklist (post-cutover cleanup)

  • Remove tools/fetch_platform_engineering_docs_products.py
  • Remove tools/import_platform_engineering_docs_products.py
  • Remove temporary Make targets (catalog-discovery-fetch, catalog-import-products)
  • Keep/adjust discovery reporting as needed for audit-only use
  • Confirm internal docs consume PQF product definitions as source of truth

srbouffard and others added 28 commits July 20, 2026 11:35
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…ctural fields in PQF normalization

Preserve product_type, ownership/squad, source, lifecycle, composed_of, context_refs in normalize_pqf_product; include documentation_url fallback/lookup in normalize_docs_product. Keep existing service_level->target_medal mapping and exclusions for deployments/communication.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…use built-in generics, clearer variable name\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…af'; add tests for default root and invalid override

Fixes Task 3 reviewer findings

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…icalize id when checking overrides in classify_product_role; add tests

\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…legacy/canonical override keys both directions\n\nDefine lookup precedence: exact product id -> canonical id -> legacy id. Honor overrides deterministically and validate values.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…hip.squad equivalent and make field mapping source-driven

Fixes: build_gap_report false-positive for UI ownership; build_field_mapping_report uses docs_fields input

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…uff I001 and E501 in Task 4

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…licit empty docs_fields\n\nFollow-up fixes for Task 4: keep mapped ui_field names even when missing from UI fields, and treat an explicit empty docs_fields as a deliberate source-driven input.\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…o-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…squad ownership and other excluded fields; expose DOCS_PRODUCTS_DIR override; regenerate artifact\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…xplicit empty docs_fields in mapping

Load PQF schema fields from config/schemas/product.schema.json and parse UI Product interface from ui/src/types.ts when provided. Pass explicit (even empty) docs_fields through to build_field_mapping_report. Add parsing helpers and tests.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…rrors\n\n- Makefile: pass --pqf-schema-path and --ui-types-path to tools/generate_catalog_discovery.py so gap detection uses authoritative schema and UI type inputs\n- tools/generate_catalog_discovery.py: fail fast when explicit schema/ui paths are provided and parsing fails (do not swallow exceptions)\n- Regenerated discovery artifact (may be partial if docs dataset unavailable)\n\nCo-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Add temporary tools-based catalog import workflow and import all current products from platform-engineering-docs into products/. Includes monorepo subpath extraction, discovery refresh, and validation updates for the expanded catalog.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@github-actions

github-actions Bot commented Jul 20, 2026

Copy link
Copy Markdown
Contributor
PR Preview Action v1.8.1

QR code for preview link

🚀 View preview at
https://canonical.github.io/pqf/pr-preview/pr-10/

Built to branch gh-pages at 2026-07-21 19:32 UTC.
Preview will be ready when the GitHub Pages deployment is complete.

Prevent PR branch workflow runs from attempting to push generated artifacts to main, which now fails repository signed-commit rules.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
srbouffard and others added 28 commits July 21, 2026 10:03
Adds a single target to run lint + format-check + test + test-ui locally,
exactly matching what the CI 'Python (lint + format + tests)' job runs.

Previously only 'make lint' was being run locally before pushing,
missing the separate 'make format-check' step and causing repeated
CI failures from formatting violations.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Based on GitHub repo topics (platform-engineering, product-*, squad-*):

matrix.yaml:
- Add maubot (canonical/maubot-operator) to composed_of — PE-owned,
  product-matrix, squad-amer topic confirms it's a matrix subproduct
- Add irc-bridge (canonical/irc-bridge-operator) to composed_of — same
- Remove both from context_refs (they weren't context-only dependencies)
- smtp-integrator stays context_ref (product-mail-server topic, different owner)

12-factor.yaml:
- Move paas-charm (canonical/paas-charm) from context_refs to composed_of
  It's a PE charm (product-12-factor, squad-emea topic)

httprequest-lego-provider.yaml:
- Remove self-referencing context_ref (own repo was listed as a dependency)

indico.yaml:
- Remove 3 duplicate context_refs pointing to canonical/indico-operator
  (already the main composed_of component as indico-charm)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
compute-metrics was triggering on every push to any branch that touched
products/**, scorers/**, engine/**, or config/**. This caused 10+ full
runs on the PR branch today, each invoking the LLM documentation scorer
for all 34 products — a significant OpenRouter cost.

Push trigger is now restricted to main. The workflow still runs:
- Nightly (cron)
- On merge to main (push to main with path filter)
- Manually (workflow_dispatch)

PR branches should use make ci-check locally and the CI lint/test job,
not the expensive compute pipeline.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…h trigger

The root problem this morning was not that compute-metrics ran on PR
branches, but that 10+ runs executed sequentially — each paying LLM cost
for all 34 products before the next fix commit even started.

Proper fix: concurrency group scoped to branch + event type, with
cancel-in-progress=true for push/dispatch events. When a new commit
arrives, any running scorer jobs for that branch are cancelled and only
the latest runs to completion.

The nightly schedule run uses a unique group (includes run_id) so it is
never cancelled by concurrent pushes.

This restores the ability to see full computed results on a PR when
tweaking scorer logic, medal thresholds, or product definitions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
context_refs were all labelled 'Context only — not scored by this team'
regardless of whether the dependency was a PE-owned product (smtp-integrator,
saml-integrator, nginx-ingress-integrator) or a truly external dependency
(postgresql, redis, s3).

Now the UI auto-detects at render time whether a context_ref's repo matches
any product in the portfolio (directly or via a leaf's source):
- Match found  → 'Also scored by this team' with a link to that product page
- No match     → 'External dependencies — not scored by this team'

No schema or data changes needed — the classification is derived entirely
from the portfolio data already present.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Replace flat list with three visually distinct tiers:

- Sub-products: 4-column CSS grid (medal | name | type chip | repo),
  bold section label. Medal badge for each sub-product gives an at-a-glance
  quality signal without having to navigate into each one.
- Also scored by this team: compact inline pill row with medals and links
  to the PE product pages. Thin border-top separator.
- External dependencies: compact inline row with repo links. Lightest
  visual weight since these don't affect the medal.

The vertical rhythm and section label weight now clearly signals
sub-products as primary, PE dependencies as secondary, external as footnote.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Previously, every inline sub-product in composed_of required its own
target_medal field — always a copy of the parent's. This was fragile:
raising a product's target required updating all its sub-products too,
and a mismatch would cause drift to fire at different thresholds on the
leaf page vs the root page.

New rule:
- Inline sub-products (defined under composed_of) have NO target_medal.
  They inherit from their parent root at engine evaluation time.
- Standalone products (their own *.yaml, referenced via ref:) keep their
  own target_medal — they have independent quality accountability.

Changes:
- config/schemas/product.schema.json: removed target_medal from inline
  ComposedEntry required + properties (additionalProperties:false makes it
  a schema error to include it now)
- products/*.yaml (33 files): stripped target_medal from all inline entries
- engine/graph.py: ProductNode.target_medal is now str | None for inline
  nodes; both resolve_leaf_units functions substitute the parent root's
  target when target_medal is None
- engine/assemble.py: same substitution before passing to compute_leaf_product
- Tests: updated fixtures, added 4 new tests covering inheritance behaviour

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The PR preview pages were showing all products as Unrated because the
compute-metrics workflow ran scorers and regenerated portfolio.json but
only committed the artifacts back on main. On PR branches the computed
data was uploaded to the GHA artifact store and then discarded.

Fix: allow commit-artifacts to run on any push/dispatch event (not just
main) by checking event_name != schedule instead of the branch name.
The checkout now uses github.ref so it targets the triggering branch.
The [skip ci] tag in the commit message prevents re-trigger loops.

Schedule runs are excluded — they always target main and are handled by
the existing nightly flow.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
… fresh data on PR builds

- Revert commit-artifacts to only run on main branch
- Add workflow_run trigger to deploy-pages to catch compute-metrics completions
- Download engine-artifacts from compute-metrics run to get fresh portfolio.json
- Keeps PR branches clean (no automated commits) while ensuring PR previews show fresh medals

This solves the 'unrated' issue on PR previews without causing merge conflicts on PR branch development.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Remove workflow_run approach (limited for PR branches in GitHub Actions)
- Add pull_request trigger to deploy-pages
- Always run engine to generate fresh portfolio.json before build
- Only deploy to gh-pages on main (PR builds still generate fresh data for preview)
- Fixes 'unrated' issue on PR previews by ensuring fresh medal computation

Also fix: improve Dependencies section column proportions

- Change grid from '6rem 1fr auto auto' to 'auto 1fr minmax(80px, auto) minmax(120px, auto)'
- Medal badge now uses auto width instead of fixed 6rem
- Product name properly handles long text with ellipsis
- Type label and repo link have minimum widths for better alignment
- Fixes column width layout issues in Dependencies panel

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Changes:
- compute-metrics.yml: Remove main-only restriction on commit-artifacts job
  - Now commits to all branches where compute-metrics runs
  - Uses [skip ci] in commit message to prevent workflow re-triggering
- deploy-pages.yml: Simplify to only deploy main to GitHub Pages
  - Remove complex workflow_run and pull_request triggers
  - Deploy only runs on main push events

This ensures:
- PR branches get fresh computed artifacts and portfolio.json
- [skip ci] flag prevents merge conflicts from re-triggering workflows
- PR previews automatically show fresh medals from the compute-metrics run
- Main deployment is clean and simple

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
- Add pull_request trigger with same path filters as push
- This makes compute-metrics show up as a status check on PRs
- Only commits artifacts on push events (not on pull_request)
- PR preview will show fresh medals when relevant files change

This ensures:
- PR shows 'compute-metrics' running as a status check
- PR cannot be merged until compute-metrics completes successfully
- Fresh medals appear in PR preview

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…tifacts

Changes:
- Replace pull_request trigger with workflow_run (after compute-metrics)
- Download fresh engine-artifacts from compute-metrics run
- Use fresh portfolio.json and computed/ data instead of stale branch versions
- Only deploy if compute-metrics succeeded
- Filter to only PRs (github.event.workflow_run.event == 'pull_request')

Flow:
1. Developer pushes changes to PR branch
2. compute-metrics runs (shows in PR status checks) ✓
3. When compute-metrics succeeds, preview.yml auto-triggers ✓
4. Preview downloads fresh artifacts and rebuilds ✓
5. PR preview shows fresh medals at https://canonical.github.io/pqf/pr-preview/pr-10/

Removed cleanup-preview job since PR closing will naturally stop preview generation.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Allows manual triggering of PR preview builds if workflow_run event doesn't
fire reliably. Supports providing explicit run_id or using latest PR compute-metrics.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Skip PR preview deployment on forks (which don't have write permissions to
the canonical/pqf gh-pages branch). Build succeeds but deployment is skipped
with helpful message.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The preview workflow needs write permissions to push to gh-pages branch.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…mport

The platform-engineering-docs catalog import overwrote 8 existing product YAMLs,
stripping PQF-specific fields (allure_report_url, target_medal overrides).
Without allure_report_url, the test_verification scorer returns 0% coverage and
all these products compute as 'unrated' instead of their actual medal.

Restored for:
- discourse (discourse-k8s sub-product)
- matrix (synapse sub-product, also restored target_medal: gold)
- mattermost (mattermost-charm sub-product)
- wazuh (wazuh-server sub-product)
- indico (indico-charm sub-product)
- jenkins (jenkins-k8s sub-product)
- netbox (netbox-k8s sub-product)
- wordpress-k8s (wordpress-k8s-charm sub-product)

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…nature requirements

On branches with required signed commits, the github-actions[bot] unsigned
commits are rejected. This should not fail the whole workflow since the
engine-artifacts are already uploaded before this job runs.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
The workflow_run event only fires from the default branch, making it useless
for PR previews. Instead:

1. compute-metrics.yml: After a successful pull_request run, fires a
   repository_dispatch event with run_id, pr_number, and head_branch.

2. preview.yml: Now responds to repository_dispatch (preview-pr type)
   and workflow_dispatch (for manual triggers). Both accept run_id and
   pr_number as inputs, fixing the missing PR number that caused deployment
   to 'pr-preview/pr-' instead of 'pr-preview/pr-10'.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
repository_dispatch hits rate limits with GITHUB_TOKEN. Use gh workflow run
instead, which uses workflow_dispatch. Also add actions:write permission to
compute-metrics so it can dispatch workflow events.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
…spatch

GITHUB_TOKEN cannot trigger workflow_dispatch events on other workflows
(GitHub security restriction). Instead, fold the PR preview build directly
into compute-metrics as a 'build-preview' job that runs after run-engine
for pull_request events.

This eliminates the cross-workflow dispatch entirely:
- compute-metrics: runs scorers → engine → deploys preview (on pull_request)
- preview.yml: kept for manual workflow_dispatch if needed

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
On PRs with many products, running all 111 scorers in parallel exhausts
the GITHUB_TOKEN rate limit (1000 req/hr per repo). Products scored after
the limit is hit return all-false values -> everything appears unrated.

Changes:
- On pull_request events: detect changed products via git diff
  Unchanged products reuse their committed computed/*.json file
  Only changed/new products are rescored
- max-parallel: 10 to limit concurrent API load on push/schedule runs
- fetch-depth: 0 so git diff can compare against base branch

This ensures PR previews show accurate medals for the existing products
while only incurring scorer costs for truly changed product definitions.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
PR-scoped GITHUB_TOKENs can fail cross-repo documentation lookups even for
public repos, which made existing bronze products like discourse and wazuh
show as unrated in PR artifacts. Retry public GitHub content requests without
auth when the authenticated request returns 401/403/404.

Also update the matrix integration test to match the restored gold target.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Constrain the dependency repo column and opt shrinkable cells into
min-width: 0 so grid items can ellipsize instead of forcing awkward
column sizing in some browser/layout combinations.

Add a regression test covering the narrow-browser row styles.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@srbouffard
srbouffard merged commit b6b348d into main Jul 21, 2026
41 checks passed
@srbouffard
srbouffard deleted the chore-add-missing-products branch July 21, 2026 19:33
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