Skip to content

feat: Phase 3 community domain — Cycle kind + MTL seed#3

Merged
Cervator merged 9 commits into
mainfrom
feat/phase3-cycle-kind-and-seed
Jul 6, 2026
Merged

feat: Phase 3 community domain — Cycle kind + MTL seed#3
Cervator merged 9 commits into
mainfrom
feat/phase3-cycle-kind-and-seed

Conversation

@agent-refr

Copy link
Copy Markdown

AI-assisted change proposal. Filed by agent driven by @Cervator via GDD.

Summary

Implements Phase 3 — community domain model (design + plan already on main):

  • Cycle custom kind (apiVersion: siliconsaga.org/v1alpha1) — a CycleProcessor
    (packages/backend/src/modules/cycle/) that validates the kind (spec.type/of/timeframe
    required) and emits built-in relation types so the graph integrates for free:
    spec.ofpartOf/hasPart, spec.ownerownedBy/ownerOf, spec.happensAtdependsOn/dependencyOf.
    Wired via a new-backend-system catalog module (catalogProcessingExtensionPoint).
  • MTL seed (examples/mtl.yaml) — a typed Group tree (org→sport→division→team, with a
    divisionless small sport), facilities as System+Resource(bookable-space), a "real" software
    System, and one season Cycle. Registered as a catalog location with a per-location rule
    allowing the Cycle kind.
  • Tests — a CycleProcessor unit suite (validation + relation emission) under
    make test-app, and a BDD checkpoint-3 (jest-cucumber) source-assertion suite under the
    envelope (make test); make ci now runs both. Adds ADR 0007.

Occurrences (matches, single events) are queried-not-minted; ResourceType dropped; Activity
dissolved. The narrated Saga kind and TeamSnap ingestion are designed-but-deferred.

Test plan

  • make ci green — config-check, lint, envelope BDD (23/23), app+backend units (7/7).
  • tsc clean.
  • Headless backend boot-smoke: the Cycle ingests via the catalog API with all three
    relations (partOf/ownedBy/dependsOn) and the Group tree present, no catalog errors.
    (This caught and fixed a YAML flow-comma bug in the Cycle description.)
  • Optional manual UI check: make dev, filter catalog by kind → Cycle; open
    soccer-2026-spring and confirm its relations render; open Group mtl and see the tree.

Related

  • Design + plan (already on main): docs/plans/2026-07-06-leidangr-phase3-community-domain-{design,plan}.md.
  • Follow-up (separate, realm-side): wire ws test leidangr to the full make ci gate.
  • Deferred: Saga kind, TeamSnap scrape→provider, curated Cycle entity page, real
    startTestBackend catalog-ingestion tests.

Cervator added 7 commits July 6, 2026 12:52
First slice of the custom `Cycle` catalog kind (Phase 3). Adds a `CycleProcessor`
implementing `CatalogProcessor.validateEntityKind`: it recognises `kind: Cycle`,
requires `spec.type`, `spec.of`, and `spec.timeframe.{start,end}`, and throws a
descriptive error on malformed entities (returning false for other kinds so it
stays out of their way).

Adds the backend deps needed to author a catalog module — `@backstage/backend-plugin-api`,
`@backstage/catalog-model`, `@backstage/plugin-catalog-node` (previously transitive).
`LocationSpec` is imported from `plugin-catalog-node` (which re-exports it), avoiding
an extra `plugin-catalog-common` dependency.

Relation emission and module registration follow in subsequent commits.

Co-Authored-By: Cervator <cervator@gmail.com>
Implements `CycleProcessor.postProcessEntity` to emit relations for `Cycle`
entities, reusing built-in relation types so the catalog graph/relations cards
integrate for free (rather than inventing new relation strings):

- `spec.of`       → `partOf` / `hasPart`      (the Cycle is part of its league/app)
- `spec.owner`    → `ownedBy` / `ownerOf`
- `spec.happensAt`→ `dependsOn` / `dependencyOf` (the field/resource it uses)

Refs are parsed with sensible default kinds (`of`/`owner` → Group, `happensAt` →
Resource). The test asserts the emitted relation types and endpoints; note
`getCompoundEntityRef` preserves the entity's kind casing (`Cycle`) while
`parseEntityRef` on the ref strings yields lowercase `group`/`resource` — the
catalog normalises on read, so this is correct.

Co-Authored-By: Cervator <cervator@gmail.com>
Wires the `CycleProcessor` into the catalog via a new-backend-system module
(`createBackendModule`, `pluginId: 'catalog'`) that adds the processor through
`catalogProcessingExtensionPoint`. Registered in the backend entrypoint with
`backend.add(import('./modules/cycle/catalogModuleCycle'))`.

Verified with `yarn tsc` (clean — confirms the module + LocationSpec import
resolve and the wiring is type-safe); the CycleProcessor unit tests remain green.
Runtime boot is verified in the manual step (make dev).

Co-Authored-By: Cervator <cervator@gmail.com>
Adds a hand-authored MTL community-domain seed and registers it as a catalog
location. Shapes (identical to what the future TeamSnap entity provider will emit):

- typed `Group` tree — organization → sport → division → team, with a small sport
  (basketball) whose team hangs directly (no division level);
- asset graph — `Domain: mtl`, facilities as `System` + `Resource`(bookable-space),
  and a "real" software `System` (mtl-registration) with two `Component`s;
- one `Cycle` (soccer-2026-spring, type season) wired to its league and field.

The new location carries per-location rules allowing `Group, Domain, System,
Resource, Component, Cycle` so the custom kind ingests. `config:check` passes.

Co-Authored-By: Cervator <cervator@gmail.com>
Adds checkpoint-3 to the envelope BDD suite (jest-cucumber), mirroring the
checkpoint-1/2 source-assertion style: asserts the MTL seed declares the org
Group tree (organization → … → team), a season `Cycle` wired to its league
(`of`) and field (`happensAt`), and that app-config allows the `Cycle` kind on
the mtl.yaml location. `ws test leidangr` = 23/23 green.

These are source-assertion smokes (per the design); a real startTestBackend
catalog-ingestion test is deferred.

Co-Authored-By: Cervator <cervator@gmail.com>
Closes out the Phase 3 build with docs + CI coverage.

- `make ci` now also runs `test-app` (`backstage-cli repo test`), so the
  `CycleProcessor` unit tests are part of the gate — previously the gate ran only
  the envelope suite, which doesn't discover `packages/*`.
- ADR 0007 records the `Cycle` decision (single custom kind, open `spec.type`
  vocabulary, reused built-in relation types; occurrences queried-not-minted;
  Saga/TeamSnap deferred), indexed in the ADR README.
- testing.md notes the CycleProcessor units run under `make test-app` and that
  `make ci` runs the full gate.

Verified: `make ci` green (config-check, lint, envelope 23/23, app+backend units).

Co-Authored-By: Cervator <cervator@gmail.com>
The soccer-2026-spring Cycle's description sat in a flow mapping
(`{ name: …, description: MTL Soccer, Spring 2026 season }`) with an unquoted
comma, so YAML split it — the entity ingested with `description: "MTL Soccer"`
plus a bogus metadata key `"Spring 2026 season": null`. Quote the value.

Caught by a headless backend boot-smoke that asserts the seed ingests via the
catalog API (the source-assertion BDD checks the seed file text, not the
ingested description). Post-fix the entity ingests cleanly with its three
relations (partOf/ownedBy/dependsOn) and correct description.

Co-Authored-By: Cervator <cervator@gmail.com>

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Implements Phase 3 of the community-domain model for this Backstage control-plane by introducing a custom Cycle catalog kind (validated + graph-wired via a catalog processor), seeding an MTL example hierarchy, and extending CI/testing/docs to cover the new behavior.

Changes:

  • Added a CycleProcessor (custom Cycle kind) and registered it via a new-backend-system catalog module.
  • Added an MTL multi-entity seed (examples/mtl.yaml) + catalog location/rules to allow Cycle.
  • Added unit tests for the processor and new envelope BDD acceptance checks; updated make ci and docs/ADR to reflect the decision and test strategy.

Reviewed changes

Copilot reviewed 13 out of 14 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
yarn.lock Locks new Backstage deps needed for the Cycle catalog module/processor.
packages/backend/package.json Adds runtime deps for backend module + catalog processor APIs.
packages/backend/src/index.ts Registers the Cycle catalog backend module into the backend runtime.
packages/backend/src/modules/cycle/catalogModuleCycle.ts New backend module wiring that installs CycleProcessor into catalog processing.
packages/backend/src/modules/cycle/CycleProcessor.ts New catalog processor: validates Cycle required fields and emits built-in relations.
packages/backend/src/modules/cycle/CycleProcessor.test.ts Unit tests for Cycle validation and relation emission.
app-config.yaml Registers the MTL seed as a catalog location and allows Cycle kind for that location.
examples/mtl.yaml Adds Phase 3 seed: Group tree + facilities/resources + real software + one Cycle.
tests/acceptance/checkpoint-3-community-domain.feature New BDD feature describing Phase 3 seed expectations.
tests/acceptance/checkpoint-3.steps.ts Step implementations for the Phase 3 seed contract checks.
Makefile Expands make ci gate to include test-app (app/backend unit tests).
docs/development/testing.md Documents that make test-app includes backend module unit tests and make ci runs both suites.
docs/adrs/README.md Indexes new ADR 0007.
docs/adrs/0007-cycle-custom-kind.md New ADR capturing the Cycle-kind design decision and consequences.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread tests/acceptance/checkpoint-3.steps.ts Outdated
Comment thread tests/acceptance/checkpoint-3.steps.ts
Comment thread tests/acceptance/checkpoint-3.steps.ts
Comment thread tests/acceptance/checkpoint-3.steps.ts
Comment thread packages/backend/src/modules/cycle/CycleProcessor.ts
@Cervator

Cervator commented Jul 6, 2026

Copy link
Copy Markdown
Member

@CodeRabbit full review

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
✅ Action performed

Full review finished.

@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: f9f4df90-a935-488b-ad0b-6c3a63f4a10c

📥 Commits

Reviewing files that changed from the base of the PR and between 020f2ef and d0d7c6d.

📒 Files selected for processing (1)
  • tests/acceptance/checkpoint-3.steps.ts

📝 Walkthrough

Summary by CodeRabbit

  • New Features
    • Introduced a new catalog kind for bounded, time-based groupings (“Cycle”).
    • Added an example catalog seed with linked groups, assets, and scheduled cycles.
    • Enabled backend processing for this new kind and updated catalog import rules to allow it.
  • Documentation
    • Added an ADR for the new “Cycle” kind and clarified what the CI gate runs.
  • Tests
    • Added unit and acceptance coverage for cycle validation, relation wiring, and config rule permissions.
  • Chores
    • Updated the CI gate to run the additional app/backend test suite.

Walkthrough

This PR adds a backend Cycle catalog kind, wires its processor into the backend, seeds example catalog data and catalog import rules, and updates docs, tests, and CI coverage for the new kind.

Changes

Cycle catalog kind implementation

Layer / File(s) Summary
CycleProcessor validation and relation logic
packages/backend/src/modules/cycle/CycleProcessor.ts, packages/backend/src/modules/cycle/CycleProcessor.test.ts
Defines CYCLE_KIND and CycleSpec, implements validateEntityKind and postProcessEntity, and adds unit tests for validation and relation emission.
Backend module registration and dependencies
packages/backend/src/modules/cycle/catalogModuleCycle.ts, packages/backend/src/index.ts, packages/backend/package.json
Registers CycleProcessor through catalogModuleCycle, adds the module to backend startup, and adds the required Backstage backend dependencies.
Example catalog seed and app-config wiring
examples/mtl.yaml, app-config.yaml
Adds the MTL catalog seed with Group, Domain, System, Resource, Component, and Cycle entities, and registers the seed location in catalog import rules.
ADR, docs, and acceptance tests
docs/adrs/0007-cycle-custom-kind.md, docs/adrs/README.md, docs/development/testing.md, tests/acceptance/checkpoint-3-community-domain.feature, tests/acceptance/checkpoint-3.steps.ts, Makefile
Adds the Cycle ADR, updates the ADR index and testing docs, adds acceptance coverage for the seed and config, and extends ci to run test-app.

Estimated code review effort: 3 (Moderate) | ~25 minutes

Possibly related PRs

  • SiliconSaga/leidangr#1: Also changes the root Makefile CI target, adding test-app to the ci prerequisite list.

Suggested reviewers: Cervator

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: adding the Cycle kind and the MTL seed for Phase 3.
Description check ✅ Passed The description is detailed and directly matches the changeset, covering the new kind, seed, tests, CI, and ADR.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/phase3-cycle-kind-and-seed

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/src/modules/cycle/CycleProcessor.ts`:
- Around line 41-56: Update validateEntityKind in CycleProcessor so spec.of,
spec.owner, and spec.happensAt are validated as proper entity refs instead of
only non-empty strings. Use parseEntityRef (or equivalent ref validation) inside
validateEntityKind to reject malformed refs early and add the failure to the
existing errors array, so invalid Cycle entities still fail with the processor’s
Invalid Cycle entity message rather than later in postProcessEntity.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1a3de906-cb8b-43de-b149-fbbec3810381

📥 Commits

Reviewing files that changed from the base of the PR and between 6f3ca32 and 0e3e541.

⛔ Files ignored due to path filters (1)
  • yarn.lock is excluded by !**/yarn.lock, !**/*.lock
📒 Files selected for processing (13)
  • Makefile
  • app-config.yaml
  • docs/adrs/0007-cycle-custom-kind.md
  • docs/adrs/README.md
  • docs/development/testing.md
  • examples/mtl.yaml
  • packages/backend/package.json
  • packages/backend/src/index.ts
  • packages/backend/src/modules/cycle/CycleProcessor.test.ts
  • packages/backend/src/modules/cycle/CycleProcessor.ts
  • packages/backend/src/modules/cycle/catalogModuleCycle.ts
  • tests/acceptance/checkpoint-3-community-domain.feature
  • tests/acceptance/checkpoint-3.steps.ts

Comment thread packages/backend/src/modules/cycle/CycleProcessor.ts
…, scope BDD assertions

Addresses CR #3 review (all findings valid).

CycleProcessor.validateEntityKind:
- CodeRabbit: validate `spec.of`/`spec.owner`/`spec.happensAt` as parseable entity
  refs (via parseEntityRef in a helper), so malformed refs fail early with the
  processor's "Invalid Cycle entity …" message instead of a generic error later in
  postProcessEntity.
- Copilot: require non-empty `spec.timeframe.start`/`end` (previously an empty
  string passed, unlike type/of). Added unit tests for both.

checkpoint-3 BDD steps:
- Copilot: import `readFileSync` from `node:fs` to match the other step files.
- Copilot: scope the Group and Cycle assertions to a single YAML document (found by
  kind + name) instead of matching name/type independently across the file, and
  escape interpolated regex values — removes false-positive risk as the seed grows.
- Copilot: tie the app-config allow-rule check to the same location stanza that
  targets mtl.yaml, so the kind can't be satisfied by a different location's rules.

Verified: `make ci` green (config-check, lint, envelope 23/23, app+backend units 9/9).

Co-Authored-By: Cervator <cervator@gmail.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/backend/src/modules/cycle/CycleProcessor.ts`:
- Around line 29-37: The refs in CycleProcessor are parsed twice: once in
isParseableRef for validation and again in postProcessEntity when building
relations. Update the CycleProcessor flow so postProcessEntity can reuse the
parsed EntityRef values collected during validation of spec.of, spec.owner, and
spec.happensAt, or otherwise cache them instead of re-running parseEntityRef on
the same strings. Keep the existing symbols isParseableRef and postProcessEntity
as the main places to wire this reuse.

In `@tests/acceptance/checkpoint-3.steps.ts`:
- Around line 56-59: The current matcher in the checkpoint-3 step only accepts a
single inline happensAt entry, so it fails on valid YAML reformatting or
additional refs. Update the assertion inside the step definition that uses
cycleDoc! and toMatch so it matches the Cycle’s happensAt content more flexibly,
allowing multiline/reflowed arrays and extra entries while still confirming the
expected resource is present.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro

Run ID: 41615de5-9b5a-4cfc-8e24-a330e6ba3c42

📥 Commits

Reviewing files that changed from the base of the PR and between 0e3e541 and 020f2ef.

📒 Files selected for processing (3)
  • packages/backend/src/modules/cycle/CycleProcessor.test.ts
  • packages/backend/src/modules/cycle/CycleProcessor.ts
  • tests/acceptance/checkpoint-3.steps.ts

Comment thread packages/backend/src/modules/cycle/CycleProcessor.ts
Comment thread tests/acceptance/checkpoint-3.steps.ts

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 13 out of 14 changed files in this pull request and generated no new comments.

CodeRabbit (Major): the `happensAt` assertion only matched a single inline
`[resource:default/field-1]`, so a reflowed array or an added ref would fail the
step even with valid Cycle data. Match the resource *within* the array
(`\[[^\]]*\bresource:default/<res>\b[^\]]*\]`), tolerating extra entries.

Declined the sibling nitpick (parse refs once in validation, reuse in
postProcessEntity): validate and postProcess are separate processor lifecycle
calls, so reuse would mean caching parsed refs across phases — added state on a
stateless processor for an O(short-string) parse off any hot path. Explained on
the thread.

Co-Authored-By: Cervator <cervator@gmail.com>
@Cervator
Cervator merged commit 73aa2a0 into main Jul 6, 2026
1 check passed
@Cervator
Cervator deleted the feat/phase3-cycle-kind-and-seed branch July 6, 2026 20:31
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.

3 participants