Skip to content
Draft
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
7 changes: 3 additions & 4 deletions lib/Lifecycle/MeetingTransitionGuard.php
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
* Meeting Transition Guard
*
* Guards the meeting lifecycle's open transition by reading the declaratively
* computed quorumMet field instead of invoking QuorumService.
* computed quorumMet field from the Meeting schema.
*
* @category Lifecycle
* @package OCA\Decidesk\Lifecycle
Expand All @@ -28,9 +28,8 @@
/**
* Guard for meeting lifecycle transitions.
*
* Chain spec 2 of 3: reads quorumMet from the declarative Meeting schema
* (computed by x-openregister-calculations in chain spec 1) rather than
* calling QuorumService.
* Reads quorumMet from the declarative Meeting schema
* (computed by x-openregister-calculations).
*
* @spec openspec/changes/spec/tasks.md#task-1
*/
Expand Down
168 changes: 0 additions & 168 deletions lib/Service/QuorumService.php

This file was deleted.

57 changes: 23 additions & 34 deletions openspec/changes/spec/design.md
Original file line number Diff line number Diff line change
@@ -1,54 +1,43 @@
# Design: Quorum — Guard rewrite (chain spec 2 of 3)
# Design: Quorum — Service deletion (chain spec 3 of 3)

## Status
pr-created

## Spec kind & chain position (ADR-032)

- `kind: code` (small) — ~30 LOC change + test fixtures.
- Chain position: 2 of 3. `depends_on: [quorum-schema-declaration]`.
Hydra blocks this from building until issue for spec 1 is closed.
- `kind: code` (small) — file deletion + DI line removal.
- Chain position: 3 of 3 (tail). `depends_on: [quorum-guard-rewrite]`.
Hydra blocks this until chain spec 2's issue is closed.

## Declarative-vs-imperative decision (ADR-031)

| Behaviour | Path | Rationale |
|---|---|---|
| Read quorum status during transition | **Code (existing PHP guard)** | ADR-031 explicitly preserves lifecycle guards as a legitimate PHP seam. The guard stays in PHP; it just reads from a declarative field instead of calling a service. |
| Quorum computation itself | **Already declarative** (chain spec 1) | Lives in `x-openregister-aggregations` + `x-openregister-calculations` on Meeting after spec 1. |
| Quorum computation | **Already declarative** | Done in chain spec 1 (`x-openregister-aggregations` + `x-openregister-calculations` on Meeting). |
| Guard data source | **Code (declarative read)** | Done in chain spec 2 (`MeetingTransitionGuard` reads `meeting.quorumMet`). |
| QuorumService class lifecycle | **Delete** | This spec. The service has no remaining callers after chain spec 2. |

## Impact on existing code

- `lib/Lifecycle/MeetingTransitionGuard.php`:
- Drop `private readonly QuorumService $quorumService` constructor param
- Replace `$this->quorumService->validateQuorum($meetingId)` body in
the `open`-transition check with `($meeting['quorumMet'] ?? false) === true`
- Update `@spec` PHPDoc tag to point at this change's tasks.md
- `lib/AppInfo/Application.php`:
- Remove the QuorumService argument from MeetingTransitionGuard's DI
construction (Container should auto-wire from public type-hints,
so removing the explicit arg is enough)
- `tests/Unit/Lifecycle/MeetingTransitionGuardTest.php`:
- Drop QuorumService mock setup
- Add three fixture cases: meeting with `quorumMet = true` (allow
transition), `quorumMet = false` (block), `quorumMet` not set
AND `quorumRequired` null (allow — null-required path)
- `lib/Service/QuorumService.php`:
- **Unchanged.** Still exists, still registered, still callable by
anyone who happens to call it. It just becomes unused. Chain spec
3 deletes it.
- **Deleted**: `lib/Service/QuorumService.php`.
- **Deleted**: `tests/Unit/Service/QuorumServiceTest.php` (if exists).
- **Edited**: `lib/AppInfo/Application.php` — remove QuorumService
Container registration line(s).
- **Verified clean**: `grep -rn QuorumService lib/ src/ tests/` returns
zero hits after this spec lands.

## Risks

1. **`quorumMet` not yet materialised on Meeting.** Won't happen —
spec 1 must close before this spec builds (Hydra `depends_on`).
2. **Other callers of QuorumService surface during the audit.**
Currently only MeetingTransitionGuard calls it (verified via
`grep -rn QuorumService lib/ src/`). If a new caller appears
during chain spec 1's wait, this spec catches it in tasks.md task
3 (regression scan).
1. **A caller appeared between chain spec 2 closing and this spec
building.** Mitigated by task 1's regression scan; if a caller
exists, this spec stops and the new caller is added to the
migration scope (back to chain spec 2's territory).
2. **A `@spec` PHPDoc tag elsewhere references the deleted file.**
Caught by `composer check:strict`; tasks.md task 3 fixes any.

## Out of scope

- Deleting QuorumService (chain spec 3).
- Updating other lifecycle guards (none currently use QuorumService).
- Frontend changes (the guard is server-side only).
- Adding new behaviour. This spec deletes only.
- Frontend changes.
- Documentation updates beyond "remove QuorumService from the data-model
doc" (one-line edit).
79 changes: 30 additions & 49 deletions openspec/changes/spec/proposal.md
Original file line number Diff line number Diff line change
@@ -1,77 +1,58 @@
---
kind: code
depends_on:
- quorum-schema-declaration
- quorum-guard-rewrite
chain:
- quorum-schema-declaration # head (predecessor)
- quorum-guard-rewrite # this spec
- quorum-service-deletion # last
- quorum-schema-declaration # head (closed before this builds)
- quorum-guard-rewrite # predecessor (closed before this builds)
- quorum-service-deletion # this spec (last)
---

# Quorum — Guard rewrite (chain spec 2 of 3)
# Quorum — Service deletion (chain spec 3 of 3)

## Problem

Chain spec 1 (`quorum-schema-declaration`) lands `quorumMet` as a
declarative boolean on every Meeting object. `MeetingTransitionGuard`
still calls `QuorumService::validateQuorum()` to make the same
decision — duplicated logic.
After chain spec 2 (`quorum-guard-rewrite`) lands,
`lib/Service/QuorumService.php` exists but has no callers. Dead code.

This spec switches the guard to read `meeting.quorumMet` from the
object directly. It does NOT delete `QuorumService` (that's chain
spec 3). It only rewires the single caller.
This spec deletes the service file, its DI registration, and its
covering test. Closes the chain.

## Proposed Solution

Edit `lib/Lifecycle/MeetingTransitionGuard.php`:

1. Drop the constructor parameter `private readonly QuorumService $quorumService`.
2. Replace the `open` transition's quorum check:
- Before: `return $this->quorumService->validateQuorum($meetingId);`
- After: `return ($meeting['quorumMet'] ?? false) === true;`
3. Update the guard's covering test to fixture-load Meeting objects
with `quorumMet` populated.

Update `lib/AppInfo/Application.php` to remove the QuorumService
constructor argument from MeetingTransitionGuard's DI registration
(keep the QuorumService registration itself — chain spec 3 deletes it).

After this spec lands: QuorumService still exists in the codebase but
is unused. The guard now relies on the declarative field landed in
chain spec 1.
1. Delete `lib/Service/QuorumService.php`.
2. Delete `tests/Unit/Service/QuorumServiceTest.php` (if it exists).
3. In `lib/AppInfo/Application.php`, remove the QuorumService
registration (constructor injection / Container::register / etc.).
4. Verify no remaining references via `grep -rn QuorumService lib/ src/ tests/`.

## Why this is `kind: code` (small)

- ~30 LOC change across 2 files (the guard + Application.php).
- Test fixture updates (~50 LOC).
- No new classes, no signature changes on public APIs, no schema work.
- Strictly mechanical: copy the existing guard pattern, swap the
service call for an array read.
- One file deletion, one test deletion, one DI line removal.
- ~10 LOC removed, no LOC added.
- No design judgment — purely "remove the code that has no callers".

Per ADR-032 small-code-spec rules: default Hydra budget (200 turns
Sonnet) is sufficient. Two files + their tests should complete in
~60-100 turns.
Per ADR-032 small-code-spec rules: default Hydra budget plenty;
expected ~30-60 turns.

## Capabilities

### Modified Capabilities

- `meeting-management` — `MeetingTransitionGuard` switches data source
for quorum check from service call to object field read. No external
contract change (the guard's pass/fail behaviour for any given
Meeting state is identical).
- `meeting-management` — QuorumService removed from the codebase. No
external API change (the service was internal; its sole consumer
was migrated to declarative reads in chain spec 2).

## Stakeholders

- **Decidesk maintainers** — own the guard rewrite.
- **Hydra reviewers** — validates that small `kind: code` chain specs
land cleanly in default budget after a `kind: config` predecessor
has done the schema work.
- **Decidesk maintainers** — own the deletion.
- **Hydra reviewers** — validates the chain-tail "deletion" pattern.

## References

- ADR-031 (hydra) — Schema-declarative business logic
- ADR-032 (hydra) — Spec sizing taxonomy and chained-spec routing
- Predecessor in chain: `quorum-schema-declaration` (must close before
this builds; Hydra's `depends_on` enforces)
- Successor in chain: `quorum-service-deletion`
- ADR-031 (hydra) — closes a service migration started in chain spec 1
- ADR-032 (hydra) — chain pattern, last spec
- Predecessors in chain: `quorum-schema-declaration` (closed),
`quorum-guard-rewrite` (closed before this builds)
- Original superseded spec: `quorum-declarative-migration` (closed
as superseded; chain replaces it)
Loading
Loading