Skip to content

feat: add proactive Astra item engine - #725

Merged
thomasluizon merged 5 commits into
redesign/mainfrom
thomasluizon/ticket-1477-astra
Oct 17, 2026
Merged

thomasluizon merged 5 commits into
redesign/mainfrom
thomasluizon/ticket-1477-astra

Conversation

@thomasluizon

@thomasluizon thomasluizon commented Oct 17, 2026 •

Copy link
Copy Markdown
Owner

Implements thomasluizon/orbit-tickets#1477 with persistent proactive Astra items, six hourly local-time triggers, push limits and recorded outcomes.

Change and approach

  • ProactiveItem, ProactiveCalendarDay and ProactiveRules hold the lifecycle, persisted calendar-read claim and named trigger thresholds. OrbitDbContext and the replacement migration remove SentProactiveCheckins, add cascade deletion and enforce one item per trigger per local day plus one pushed item per local day. The migration also removes the historical database default of false while preserving stored choices.
  • ProactiveEngineService batches eligible users and facts, evaluates the six triggers, enforces priority, push limits and rest periods, and records outcomes and PostHog events. A scoped engine serves the hourly scheduler and current-item revalidation. Both background-job registrations replace the old scheduler while retaining its recurring-job identity.
  • ProactiveAstraController dispatches the current, opened and acted operations through Application handlers and validators. Owner checks return 404, and first timestamps are idempotent.
  • AiProactiveCheckinMessageService serves all triggers with English and pt-BR templates, a temperature of 0.3 and conservative output validation. Tests stub the repository-owned completion port instead of inventing provider response payloads.
  • The agent catalog in AgentContracts.cs and AgentCatalogService.Capabilities.cs declares dedicated proactive read and outcome capabilities for the three controller actions, using notification scopes and no agent tools. Restart facts explicitly store the scheduled-yesterday, positive-logs-yesterday and recent-activity predicates in ProactiveFacts.cs and ProactiveEngineService.cs.
  • Profile reads keep the effective setting as a boolean; stored choices are nullable. Reset and deletion remove the new rows, and export adds item projections without facts JSON plus the stored setting. openapi.json is regenerated.

Assumptions

  • Current-item reads revalidate existing items and mark shown time; they do not create items or make model calls. The alternative was creating items from GET requests.
  • The single goal-behind slot chooses an eligible goal by deadline and id, respecting each goal's cooldown. The alternative was one item for every behind goal on the same day.
  • Calendar reads use a durable attempt claim for the local morning, including failed attempts. The alternative was retrying each hour after a failed read.
  • Timed-event minutes sum elapsed durations clipped to the person's local day. The alternative was merging overlapping event ranges.
  • The query-volume guard counts SQL reads; inserts necessarily scale with created items. The alternative was treating write volume as an N+1 read regression.
  • Removing the obsolete scheduler suppression entry is required metadata cleanup for deleting that source file; retaining it fails the closed-set suppression gate.
  • Proactive catalog entries reuse notification read/write scopes with dedicated capability ids. The alternative was introducing scopes for endpoints used directly by clients.

Retained the ledger through an EF model-only mapping rather than restoring its retired domain entity or scheduler. The later contract migration belongs to #1491.

Test evidence

  • Final infrastructure project: dotnet test tests/Orbit.Infrastructure.Tests --no-build --no-restore exited 0 with 3,308 passed and none failed.
  • Final application project: dotnet test tests/Orbit.Application.Tests --no-build --no-restore exited 0 with 5,460 passed and none failed.
  • Final solution build: dotnet build Orbit.slnx exited 0 with zero errors and 18 existing warnings.
  • Final domain project: dotnet test tests/Orbit.Domain.Tests --no-build --no-restore exited 0 with 668 passed and none failed.
  • Catalog and proactive verification: dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter 'FullyQualifiedName~AgentCatalogServiceTests|FullyQualifiedName~Proactive' exited 0 with 91 passed, including the controller coverage check and the strengthened restart case.
  • Focused infrastructure command: dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter 'FullyQualifiedName~Proactive|FullyQualifiedName~AccountDeletionServiceDbTests|FullyQualifiedName~NotificationVoiceTests': 202 passed.
  • Focused application/profile tests: 255 passed. Focused domain tests: seven passed. The engine suite exercises fixed local windows, priority after logging, push caps, quiet hours, rest periods, ownership, the kill switch, reset/deletion, DST and invariant SQL read counts over 10 and 1,000 accounts.
  • Before the DST fix, dotnet test tests/Orbit.Infrastructure.Tests --no-build --filter FullyQualifiedName~BusyCalendar_DurationThresholdAndAllDayExclusion passed unchanged with the defect present. The strengthened command dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter 'FullyQualifiedName~BusyCalendar_DurationCountsElapsedHoursAcrossDst|FullyQualifiedName~WeeklyReview_IncludesWeeksWithOnlyBadOrChildHabits' failed three cases: three elapsed hours instead of four, and missing weekly items. After correcting the child fixture to use a scheduled parent with a future due date, dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter FullyQualifiedName~WeeklyReview_IncludesWeeksWithOnlyBadOrChildHabits still failed the child case before removing the parent-only filter. All these cases pass in the 202-test focused run after the fixes.
  • The unchanged migration test passed before its metadata assertion was strengthened. dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter FullyQualifiedName~Migration_OnlyRelaxesStoredSettingAndReplacesTheLedger then failed because the old default was null instead of false. It passes after correcting the migration metadata. dotnet ef migrations script 20261001074038_AddHabitSkipUndo 20261017083409_ReplaceProactiveCheckinsWithItems --project src/Orbit.Infrastructure --startup-project src/Orbit.Api --no-build initially emitted only DROP NOT NULL for the setting; after the fix it also emits DROP DEFAULT.
  • Restart and catalog baseline: dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter 'FullyQualifiedName~EachTrigger_RequiresItsConditionAndWindow|FullyQualifiedName~EveryControllerAction_IsMappedToTheCatalog' exited 1 before changes: the six unchanged trigger cases passed with the missing facts, while the catalog test failed for Current, Opened and Acted.
  • Restart red: after adding assertions over the actual persisted facts JSON, dotnet test tests/Orbit.Infrastructure.Tests --no-restore --filter FullyQualifiedName~EachTrigger_RequiresItsConditionAndWindow exited 1 with five passes and the restart case failing at the missing HadScheduledHabitsYesterday property. Restart green: the same command exited 0 with all six cases passing after persisting the three supporting predicates.
  • All four commits passed the formatting, dash, timeless, root allowlist and suppression allowlist hooks.

Focused command:

dotnet test tests/Orbit.Infrastructure.Tests --filter 'FullyQualifiedName~ProactiveEngineServiceTests|FullyQualifiedName~ProactiveMigrationTests|FullyQualifiedName~AccountResetRepositoryTests' --verbosity minimal

Unchanged tests passed with defects present: 45 passed. Repeated with --no-restore after strengthening tests: 12 failed, 45 passed before fixes; 57 passed afterward.

Regression evidence:

Tests Before fix After fix
Migration_OnlyRelaxesStoredSettingAndExpandsTheLedger, Migration_DownLeavesTheRetainedLedgerUntouched, Models_RetainTheLegacyLedgerUntilTheContractMigration 3 failed Passed
BadHabitOnly_WithRecentSlipAndAbstinenceYesterday_NeverPromptsStartingOrResuming, RestartAfterMiss_OnlyCountsSuccessfulActivity, corrected WeeklyReview_IncludesWeeksWithOnlyBadOrChildHabits 5 failed Passed
FlexibleQuota_UsesTheOwnersWeekStartBeforePrompting, Current_ResolvesFlexibleItemWhenQuotaIsMetOnAnotherDay 4 failed Passed

Additional verification:

  • dotnet build Orbit.slnx --verbosity minimal: 0 errors.
  • dotnet test Orbit.slnx --no-build --no-restore --verbosity minimal: 9,480 passed.
  • EF reports no pending model changes.
  • All commit hooks passed.
  • npm_config_yes=false npx turbo run type-check --force: unavailable because Turbo is absent; no package installed.

External interface evidence

No new provider response fields are read. The engine consumes the repository-owned calendar, token and AI completion interfaces. PostgreSQL migration behavior was checked through the actual EF-generated SQL, including the missing and then present default removal. No new external response fixture was authored.

Eval scenarios for #26

Run each scenario in English and pt-BR, requiring one sentence of at most 140 characters, only supported facts, no sales language, and feminine agreement when Astra refers to herself in pt-BR:

  • Restart: yesterday had a scheduled habit and no positive log, with activity in the preceding 14 days; make returning clear without blame.
  • Evening: two open habits and a streak of at least two days; name only supplied habits and use only supplied numbers.
  • Busy calendar: four timed events, or at least 240 timed minutes; exclude all-day events and ground the line in the supplied calendar counts.
  • Goal behind: one supplied active goal with behind or at_risk status; do not invent progress, velocity or a different goal.
  • Weekly review: the previous local week, scheduled occurrences, positive completions and slips; distinguish completion counts from slips and preserve the supplied period.
  • Morning: the supplied habits due today and their count; offer a localized next action without promising automatic logging.

Deploy the completed API before thomasluizon/orbit-tickets#1481. The operational kill-switch key is astra_proactive_engine_disabled.

Existing System.Security.Cryptography.Pkcs advisories are tracked separately in thomasluizon/orbit-tickets#1488.

Manual steps

None. The existing startup migration path applies the expand migration.

@pullfrog pullfrog 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.

Caution

Three high-priority issues need fixes before merging: bad-habit abstinence produces return-to-habit guidance, completed flexible quotas still produce reminders, and the migration breaks old-instance account resets during deploy overlap.

Reviewed changes Reviewed the persistent proactive engine, all six triggers, delivery and lifecycle handling, migration, API contracts, and tests.

  • Persistent lifecycle: Adds proactive items, calendar read claims, outcome timestamps, rest periods, goal cooldowns, and daily push uniqueness.
  • Hourly evaluation: Replaces the old scheduler with local-time trigger evaluation, batched facts, calendar checks, and analytics.
  • Client operations: Adds authenticated current/opened/acted endpoints and corresponding agent catalog capabilities.
  • Messages and settings: Adds grounded localized generation and fallback actions, nullable stored preferences, and additive export fields while retaining public boolean settings.
  • Verification: Focused proactive suites passed: 70 infrastructure, six application, and seven domain tests. Separate SQLite probes reproduced the bad-habit and completed-flexible failures; the full solution suite was not rerun.

Pullfrog  | Fix all ➔ | Fix 👍s ➔ | View workflow run | Using gpt-6.1-sol | 𝕏

Keep the legacy check-in ledger during deploy overlap, exclude slips and bad habits from restart and action prompts, and honor flexible habit quotas during creation and current-item revalidation.
@sonarqubecloud

Copy link
Copy Markdown

@pullfrog pullfrog 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.

✅ No new issues found.

Reviewed changes Reviewed 62e3bf4e, the commit added since the prior Pullfrog review, and confirmed that the three resolved findings are addressed.

  • Retained rollout compatibility: Preserved SentProactiveCheckins in the migration and EF models so previous instances can continue using the ledger during deployment overlap.
  • Separated habit semantics: Excluded bad habits from positive-action guidance and excluded slips from successful restart activity while preserving weekly slip reporting.
  • Applied flexible quotas: Reused the quota-aware scheduling helper with the owner's week start for item creation and current-item revalidation.
  • Verified regressions: Passed all 57 focused engine, migration and account-reset tests, then all 114 tests in the broader proactive, account-cleanup and catalog selection. The full solution suite was not rerun in this review.

Pullfrog  | View workflow run | Using gpt-6.1-sol | 𝕏

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