Skip to content

feat(ops): add ActiveSync health evaluation - #107

Merged
TDannhauer merged 4 commits into
FRAMEWORK_6_0from
feat/ops-health-evaluator
Sep 28, 2026
Merged

TDannhauer merged 4 commits into
FRAMEWORK_6_0from
feat/ops-health-evaluator

Conversation

@TDannhauer

Copy link
Copy Markdown
Contributor

Summary

  • add pure ActiveSync device and collection health DTOs and evaluator
  • classify heartbeat, FolderSync, backlog, wipe, and block signals
  • add a legacy Device/SyncCache compatibility factory and operator design document

Motivation

Operators need shared, testable health semantics that can be reused by Core, CLI, and web consumers without changing protocol behavior.

Changes

  • add PSR-4 Horde\ActiveSync\Ops classes alongside the legacy tree
  • derive active, warning, critical, stuck, and abandoned states from persisted facts
  • document reason codes, tunables, limits, and operational workflows

Test plan

  • vendor/bin/phpunit -c vendor/horde/activesync/phpunit.xml.dist --bootstrap vendor/autoload.php vendor/horde/activesync/test/unit/Horde/ActiveSync/Ops
  • 14 tests, 78 assertions

Closes #100
Part of #99

Add a pure state-derived health model for devices and collections, including heartbeat, FolderSync, backlog, wipe, and block signals.

Document reason codes, tuning, operational limits, and the operator workflow.
@github-actions

github-actions Bot commented Sep 18, 2026 •

Copy link
Copy Markdown

🔍 CI Results

Overall: ❌ 12/12 lanes failed

TL;DR: ❌ Quality issues: PHP-CS-Fixer: 44 files.

Summary by PHP Version

PHP dev stable
8.1 ❌ ❌
8.2 ❌ ❌
8.3 ❌ ❌
8.4 ❌ ❌
8.5 ❌ ❌
8.6 ❌ ❌

Quality Metrics

  • PHPUnit: did not run (12 lanes exited non-zero with no test output) ❌
  • PHPStan: No errors found ✅ — 8 advisories at level 2
  • PHP-CS-Fixer: 44 unique files with issues (of 231 checked) ⚠️
❌ Failed Lanes

php8.1-dev

  • PHPUnit: 0 failures, 0 errors

php8.1-stable

  • PHPUnit: 0 failures, 0 errors

php8.2-dev

  • PHPUnit: 0 failures, 0 errors

php8.2-stable

  • PHPUnit: 0 failures, 0 errors

php8.3-dev

  • PHPUnit: 0 failures, 0 errors

php8.3-stable

  • PHPUnit: 0 failures, 0 errors

php8.4-dev

  • PHPUnit: 0 failures, 0 errors
  • PHP-CS-Fixer: 44 files with issues

php8.4-stable

  • PHPUnit: 0 failures, 0 errors

php8.5-dev

  • PHPUnit: 0 failures, 0 errors

php8.5-stable

  • PHPUnit: 0 failures, 0 errors

php8.6-dev

  • PHPUnit: 0 failures, 0 errors

php8.6-stable

  • PHPUnit: 0 failures, 0 errors

CI powered by horde-components • View full results

final class CollectionFacts
{
public function __construct(
public readonly string $id,

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you please investigate why the phpstan pipeline would complain here? It shouldn't.

@TDannhauer TDannhauer Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

The annotation on this constructor is the PHP 8.0 lane parsing readonly. That keyword is valid from PHP 8.1, so the 8.0 parser reports unexpected T_STRING here. PHPStan on 8.1 through 8.5 does not report CollectionFacts.

The 8.0 lane was right by the declared constraint, though: composer.json still said ^7.4 || ^8, and this is the first code in the package that needs 8.1. The php8.0-stable lane could not resolve dependencies anyway. composer.json and .horde.yml now require ^8.1 and the 8.0 lane is dropped, matching horde/db and horde/horde.

The level-1 error the 8.1–8.5 lanes did report is Horde_Db_Migration_Base::announce() called with a second 'cli.message' argument. That call now passes only the message.

The same arity pattern exists in lib/, which CI does not scan: new Horde_ActiveSync_Request_Autodiscover($this, new Horde_ActiveSync_Device(...)). The request constructor takes the server only and reads the device from it. Autodiscover is now constructed the same way as the other commands.

Comment thread src/Ops/DeviceFacts.php Outdated
public readonly ?int $lasthbsyncstarted,
public readonly ?int $lastsyncendnormal,
public readonly int $foldersyncrequired,
public readonly array $collections

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Can you further document the expected shape? Would it make sense to use a typed object with an ActiveSyncCollection interface [extends ArrayAccess] ?

@TDannhauer TDannhauer Sep 28, 2026 •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

$collections is now a CollectionFactList: an immutable map of CollectionFacts, keyed by collection id. Array access returns the snapshot for that id.

Each snapshot copies class, serverid, lastsynckey, backlog, backlogpings, and pingable from the SyncCache row. Window size, filter type, truncation, body preferences, and conflict policy stay on the cache row. Those keys are documented on CollectionFacts and in doc/ops-monitor.md.

On the ActiveSyncCollection extends ArrayAccess idea: I put array access on the list rather than on each fact. A fact is a seven-field read-only snapshot for the evaluator, not a stand-in for the cache row, so $collection['windowsize'] on it would have to fail. A typed collection object that replaces the associative arrays in SyncCache and Sync.php is the item already noted in doc/todo.md; it makes sense, but as its own change with Horde_ActiveSync_Collections as the consumer. If you would rather have the facts themselves implement ArrayAccess for the copied keys, that is a small addition here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

habe das Zielbild in der todo.md ergänzt.

Replace the raw collection array with an immutable id-keyed CollectionFactList. Construct Autodiscover with the server only, and call migration announce() with the message alone.
The Ops health classes use readonly properties, which need PHP 8.1. Declare that floor in composer.json and .horde.yml, and drop the 8.0 CI lane, matching horde/db and horde/horde.
Describe the four concerns mixed into the associative collection array, the target split into SyncCollection, Sync_Options, and the Changes object, the array-backed storage boundary, ArrayAccess as a removable migration shim, the conversion order, and how the Ops CollectionFacts projection folds into SyncCollection later.
@TDannhauer
TDannhauer merged commit a1ac7d6 into FRAMEWORK_6_0 Sep 28, 2026
1 check failed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

feat(ops): device/collection health evaluator and DTOs

2 participants