Skip to content

feat(automation-approval-steps): approval action kind, on-approve/reject follow-ups, My Approvals widget - #10

Merged
rubenvdlinde merged 1 commit into
developmentfrom
wip/automation-approval-steps
Jul 24, 2026
Merged

feat(automation-approval-steps): approval action kind, on-approve/reject follow-ups, My Approvals widget#10
rubenvdlinde merged 1 commit into
developmentfrom
wip/automation-approval-steps

Conversation

@rubenvdlinde

Copy link
Copy Markdown
Contributor

Summary

Implements OpenSpec change automation-approval-steps: a fourth automation action kind, approval, that compiles to an OpenRegister ApprovalChain and consumes OR's existing approval engine end to end — no new approval logic is built in OpenBuild (ADR-022 consume-not-rebuild).

  • Compiler (AutomationCompilerService): v1 matrix extended so approval is supported on object-created|object-updated|object-deleted|lifecycle-transition triggers only (schedule/manual stay fail-closed). A new compile branch upserts an OR ApprovalChain (aut-<slug>, one step, role = assignee group) via ApprovalChainMapper, idempotently. provenance.approvalChainName is recorded; enable/disable/delete removes exactly that chain. New approvalState() reads the live aggregate state (none|pending|approved|rejected) for status/dry-run.
  • Trigger-fire (AutomationApprovalTriggerListener, new): subscribes to ObjectCreatedEvent/ObjectUpdatedEvent/ObjectDeletedEvent/ObjectTransitionedEvent, matches enabled automations by trigger shape, and calls ApprovalService::initializeChain() for the fired object — with an idempotency guard so an object-updated-triggered automation never spawns duplicate chain instances.
  • Follow-up dispatch (ApprovalOutcomeListener, new): on ApprovalStepApprovedEvent/ApprovalStepRejectedEvent, resolves the originating automation by its aut-<slug> chain name (single lookup, no scan for unrelated chains) and dispatches the automation's onApprove/onReject follow-up actions through the same RuleActionDispatcher the rules backend already uses.
  • Editor UI (AutomationEditDialog.vue): approval action type with an NC-group assignee picker (live OCS fetch, degrades to free-text) plus onApprove/onReject nested action-list editors (new reusable AutomationActionListEditor.vue). Matrix validation blocks approval on schedule/manual with an explicit message.
  • My Approvals widget (MyApprovalsWidget.vue, new runtime widget): lists the viewer's pending ApprovalSteps (client-side group filter via IInitialState, never a DOM read) with approve/reject buttons that call OpenRegister's /api/approval-steps/{id}/approve|reject directly — no OpenBuild pass-through controller (ADR-022 redundant-controller gate). Registered in runtimeRegistry.js with the required ADR-049 _note + @custom-widget-ratchet exclude justification.
  • Status/dry-run: AutomationsController::status() and dryRun() both surface approvalState; the dry-run panel shows approval as "dry-run, skipped" and creates no real ApprovalStep (added approval to ConditionActionExecutor::SIDE_EFFECT_ACTIONS, used only by the dry-run synthetic rule path — the real compile path never routes approval through the rules engine).
  • Extended tests/stubs/openregister-stubs.php with ApprovalChain/ApprovalChainMapper/ApprovalStep/ApprovalStepMapper/ApprovalService and the four new Event stubs, so the unit suite is self-contained (no sibling openregister checkout required in CI).

Archives the change: delta specs synced into automation-designer (REQ-AUTD-002/003/004/007) and the new automation-approval-action capability spec (Status: done).

Test plan

  • PHPUnit: docker run --rm -v $PWD:/app -w /app nextcloud:34.0.0-apache php vendor/bin/phpunit -c phpunit-unit.xml672/672 pass, 0 failures/errors (1 benign "no coverage driver" warning).
  • PHPCS: vendor/bin/phpcs --standard=phpcs.xml on all touched/new lib/ files → 0 errors, 0 warnings.
  • PHPStan: vendor/bin/phpstan analyse --memory-limit=1G on touched files → no errors.
  • Psalm: vendor/bin/psalm --threads=1 --no-cache (whole repo) → no errors (psalm.xml's OCA\OpenRegister\* suppression allow-list extended for the new Approval*/Event classes).
  • PHPMD: only pre-existing baselined debt + accepted class-level coupling/complexity on the two new listener classes (7 constructor deps each, matching OR's own ApprovalChainGateListener precedent) — zero new method-level violations after refactor.
  • ESLint: npx eslint <touched .vue/.js>0 errors (only pre-existing repo-wide @spec jsdoc-tag warnings).
  • Stylelint: clean.
  • Vitest: npx vitest run (full suite) → 126 files / 1224 tests pass, including 5 new/updated files covering the matrix, dialog, action-list editor, and My Approvals widget.
  • openspec validate "automation-approval-steps" → valid; openspec status → all artifacts done.
  • All 39 Hydra mechanical gates (run-hydra-gates.sh --scope-to-diff origin/development) → green (spec-coverage, redundant-controller, no-admin-idor, custom-widget-ratchet, route-auth, etc.).
  • Newman: contract collection written (tests/integration/openbuild-automation-approval-steps.postman_collection.json) but not executed against a live instance this session (no deploy to the shared dev instance per project policy).
  • Playwright: e2e scenarios written in tests/e2e/automations.spec.ts but not executed this session (CI-run only, matching the pre-existing suite's own documented convention).

🤖 Generated with Claude Code

…ect follow-ups, My Approvals widget

Adds a new `approval` automation action kind (event/lifecycle-transition
triggers only, group-only assignee) that compiles to an OpenRegister
ApprovalChain and initialises a step via ApprovalService::initializeChain()
at trigger-fire time (AutomationApprovalTriggerListener) — consume-not-rebuild
per ADR-022, no new approval engine in OpenBuild.

- AutomationCompilerService: matrix extended, approval-backend compile
  branch (upsert ApprovalChain, idempotent), provenance.approvalChainName,
  approvalState() for status/dry-run surfacing.
- ApprovalOutcomeListener: dispatches an automation's onApprove/onReject
  follow-up actions on ApprovalStepApprovedEvent/RejectedEvent via the
  shared RuleActionDispatcher.
- AutomationEditDialog: approval action type, NC-group assignee picker
  (degrades to free-text), on-approve/on-reject nested action-list editors
  (new AutomationActionListEditor.vue).
- MyApprovalsWidget.vue: runtime widget listing the viewer's pending
  approval steps (client-side group filter via IInitialState), approve/
  reject calling OpenRegister's /api/approval-steps directly — no
  OpenBuild pass-through controller.
- AutomationsController::status()/dryRun(): approvalState field.
- OpenRegister test stubs extended (ApprovalChain/Step/Mapper/Service,
  ApprovalStep{Approved,Rejected}Event, Object{Created,Updated}Event) so
  the unit suite is self-contained without a sibling checkout.

Archives openspec change automation-approval-steps; syncs delta specs into
automation-designer (REQ-AUTD-002/003/004/007) and the new
automation-approval-action capability spec (Status: done).
@rubenvdlinde
rubenvdlinde merged commit 1b66367 into development Jul 24, 2026
2 checks passed
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