Repository: canyongbs/advisingapp
Pull request #1538: [ADVAPP-1490]: Feature Flag Cleanup: RefactorEngagementCampaignSubjectToJsonb
Author: login: ketan-canyon
Labels: name: Change Type | Feature Cleanup
description: Cleanup and/or removal or feature flagging or guardrail logic
Created: 2025-05-12T09:44:43Z | Merged: 2025-05-16T13:29:25Z

### Ticket(s) or GitHub Issue

- https://canyongbs.atlassian.net/browse/ADVAPP-1490

### Technical Description

Feature Flag Cleanup: RefactorEngagementCampaignSubjectToJsonb

### Any deployment steps required?

No

### Are any Feature Flags and/or Data Migrations that can eventually be removed Added?

No

_______________________________________________

#### Before contributing and submitting this PR, make sure you have Read, agree, and are compliant with the [contributing guidelines](https://github.com/canyongbs/advisingapp/blob/main/README.md#contributing).



Diff:
diff --git a/_ide_helper_models.php b/_ide_helper_models.php
index f31d814cfd..0174f0acc6 100644
--- a/_ide_helper_models.php
+++ b/_ide_helper_models.php
@@ -607,6 +607,8 @@ class IdeHelperTenant {}
  * @property \Illuminate\Support\Carbon $password_last_updated_at
  * @property bool $is_signature_enabled
  * @property array<array-key, mixed>|null $signature
+ * @property string|null $team_id
+ * @property bool $is_submit_ai_chat_on_enter_enabled
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\Ai\Models\AiAssistantUpvote> $aiAssistantUpvotes
  * @property-read int|null $ai_assistant_upvotes_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\Ai\Models\AiThreadFolder> $aiThreadFolders
@@ -702,6 +704,7 @@ class IdeHelperTenant {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIsExternal($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIsPhoneNumberVisibleOnProfile($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIsSignatureEnabled($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereIsSubmitAiChatOnEnterEnabled($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereJobTitle($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereLastChatPingAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereLastLoggedInAt($value)
@@ -723,6 +726,7 @@ class IdeHelperTenant {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User wherePublicProfileSlug($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereRememberToken($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereSignature($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTeamId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereTimezone($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereType($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|User whereUpdatedAt($value)
@@ -1874,6 +1878,7 @@ class IdeHelperCampaignAction {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam whereCareTeamRoleId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam whereCreatedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam whereEducatableId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeam whereEducatableType($value)
@@ -1891,7 +1896,12 @@ class IdeHelperCareTeam {}
  * 
  *
  * @property bool $is_default
+ * @property string $id
+ * @property string $name
  * @property \App\Enums\CareTeamRoleType $type
+ * @property \Illuminate\Support\Carbon|null $created_at
+ * @property \Illuminate\Support\Carbon|null $updated_at
+ * @property \Illuminate\Support\Carbon|null $deleted_at
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\Audit\Models\Audit> $audits
  * @property-read int|null $audits_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\CareTeam\Models\CareTeam> $careTeams
@@ -1901,6 +1911,13 @@ class IdeHelperCareTeam {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole onlyTrashed()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole query()
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereCreatedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereDeletedAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereId($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereIsDefault($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereName($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereType($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole whereUpdatedAt($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole withTrashed()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|CareTeamRole withoutTrashed()
  * @mixin \Eloquent
@@ -2648,7 +2665,7 @@ class IdeHelperEmailTemplate {}
  * @property string|null $engagement_batch_id
  * @property string|null $recipient_id
  * @property string|null $recipient_type
- * @property string|null $subject
+ * @property array<array-key, mixed>|null $subject
  * @property array<array-key, mixed>|null $body
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
@@ -2713,7 +2730,7 @@ class IdeHelperEngagement {}
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property \AdvisingApp\Notification\Enums\NotificationChannel|null $channel
- * @property string|null $subject
+ * @property array<array-key, mixed>|null $subject
  * @property array<array-key, mixed>|null $body
  * @property \Illuminate\Support\Carbon|null $scheduled_at
  * @property int|null $total_engagements
@@ -2848,6 +2865,7 @@ class IdeHelperEngagementFileEntities {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereSenderId($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereSenderType($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereSentAt($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereStatus($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereSubject($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereType($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EngagementResponse whereUpdatedAt($value)
@@ -2980,7 +2998,7 @@ class IdeHelperFormAuthentication {}
  * 
  *
  * @property string $id
- * @property string|null $subject
+ * @property array<array-key, mixed>|null $subject
  * @property array<array-key, mixed>|null $body
  * @property bool $is_enabled
  * @property string $form_id
@@ -3971,6 +3989,7 @@ class IdeHelperEmailMessage {}
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property-read \AdvisingApp\Notification\Models\EmailMessage|null $message
+ * @method static \AdvisingApp\Notification\Database\Factories\EmailMessageEventFactory factory($count = null, $state = [])
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EmailMessageEvent newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EmailMessageEvent newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|EmailMessageEvent query()
@@ -4041,6 +4060,7 @@ class IdeHelperSmsMessage {}
  * @property \Illuminate\Support\Carbon|null $created_at
  * @property \Illuminate\Support\Carbon|null $updated_at
  * @property-read \AdvisingApp\Notification\Models\SmsMessage|null $message
+ * @method static \AdvisingApp\Notification\Database\Factories\SmsMessageEventFactory factory($count = null, $state = [])
  * @method static \Illuminate\Database\Eloquent\Builder<static>|SmsMessageEvent newModelQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|SmsMessageEvent newQuery()
  * @method static \Illuminate\Database\Eloquent\Builder<static>|SmsMessageEvent query()
@@ -4247,8 +4267,8 @@ class IdeHelperPipelineStage {}
  * @property string $full_name
  * @property string|null $preferred
  * @property string|null $description
- * @property bool $sms_opt_out
- * @property bool $email_bounce
+ * @property bool|null $sms_opt_out
+ * @property bool|null $email_bounce
  * @property \Illuminate\Support\Carbon|null $birthdate
  * @property string|null $hsgrad
  * @property string|null $created_by_id
@@ -5032,16 +5052,16 @@ class IdeHelperProgram {}
  * @property string|null $last
  * @property string|null $full_name
  * @property string|null $preferred
- * @property bool $sms_opt_out
- * @property bool $email_bounce
+ * @property bool|null $sms_opt_out
+ * @property bool|null $email_bounce
  * @property \Illuminate\Support\Carbon|null $birthdate
  * @property int|null $hsgrad
- * @property bool $dual
- * @property bool $ferpa
+ * @property bool|null $dual
+ * @property bool|null $ferpa
  * @property \Illuminate\Support\Carbon|null $dfw
- * @property bool $sap
+ * @property bool|null $sap
  * @property string|null $holds
- * @property bool $firstgen
+ * @property bool|null $firstgen
  * @property string|null $ethnicity
  * @property string|null $lastlmslogin
  * @property string|null $f_e_term
@@ -5054,6 +5074,7 @@ class IdeHelperProgram {}
  * @property string|null $primary_email_id
  * @property string|null $primary_phone_id
  * @property string|null $primary_address_id
+ * @property string|null $gender
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\StudentDataModel\Models\StudentAddress> $additionalAddresses
  * @property-read int|null $additional_addresses_count
  * @property-read \Illuminate\Database\Eloquent\Collection<int, \AdvisingApp\StudentDataModel\Models\StudentEmailAddress> $additionalEmailAddresses
@@ -5144,6 +5165,7 @@ class IdeHelperProgram {}
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereFirst($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereFirstgen($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereFullName($value)
+ * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereGender($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereHolds($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereHsgrad($value)
  * @method static \Illuminate\Database\Eloquent\Builder<static>|Student whereLast($value)
diff --git a/app-modules/campaign/src/Filament/Blocks/EngagementBatchEmailBlock.php b/app-modules/campaign/src/Filament/Blocks/EngagementBatchEmailBlock.php
index d007732b9b..322854d566 100644
--- a/app-modules/campaign/src/Filament/Blocks/EngagementBatchEmailBlock.php
+++ b/app-modules/campaign/src/Filament/Blocks/EngagementBatchEmailBlock.php
@@ -40,7 +40,6 @@
 use AdvisingApp\Campaign\Settings\CampaignSettings;
 use AdvisingApp\Engagement\Models\EmailTemplate;
 use AdvisingApp\Notification\Enums\NotificationChannel;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use Carbon\CarbonImmutable;
 use Filament\Forms\Components\Actions;
 use Filament\Forms\Components\Actions\Action;
@@ -48,7 +47,6 @@
 use Filament\Forms\Components\DateTimePicker;
 use Filament\Forms\Components\Hidden;
 use Filament\Forms\Components\Select;
-use Filament\Forms\Components\TextInput;
 use Filament\Forms\Get;
 use Filament\Forms\Set;
 use FilamentTiptapEditor\TiptapEditor;
@@ -71,11 +69,6 @@ public function generateFields(string $fieldPrefix = ''): array
         return [
             Hidden::make($fieldPrefix . 'channel')
                 ->default(NotificationChannel::Email->value),
-            TextInput::make($fieldPrefix . 'subject')
-                ->columnSpanFull()
-                ->placeholder(__('Subject'))
-                ->required()
-                ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
             TiptapEditor::make($fieldPrefix . 'subject')
                 ->recordAttribute('data.subject')
                 ->label('Subject')
@@ -91,8 +84,7 @@ public function generateFields(string $fieldPrefix = ''): array
                 ->showMergeTagsInBlocksPanel(false)
                 ->required()
                 ->helperText('You can insert recipient information by typing {{ and choosing a merge value to insert.')
-                ->columnSpanFull()
-                ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                ->columnSpanFull(),
             TiptapEditor::make($fieldPrefix . 'body')
                 ->recordAttribute('data.body')
                 ->disk('s3-public')
diff --git a/app-modules/engagement/database/migrations/2025_04_17_142824_data_migration_refactor_engagement_subject_to_jsonb.php b/app-modules/engagement/database/migrations/2025_04_17_142824_data_migration_refactor_engagement_subject_to_jsonb.php
deleted file mode 100644
index ce5ff10069..0000000000
--- a/app-modules/engagement/database/migrations/2025_04_17_142824_data_migration_refactor_engagement_subject_to_jsonb.php
+++ /dev/null
@@ -1,226 +0,0 @@
-<?php
-
-/*
-<COPYRIGHT>
-
-    Copyright © 2016-2025, Canyon GBS LLC. All rights reserved.
-
-    Advising App™ is licensed under the Elastic License 2.0. For more details,
-    see https://github.com/canyongbs/advisingapp/blob/main/LICENSE.
-
-    Notice:
-
-    - You may not provide the software to third parties as a hosted or managed
-      service, where the service provides users with access to any substantial set of
-      the features or functionality of the software.
-    - You may not move, change, disable, or circumvent the license key functionality
-      in the software, and you may not remove or obscure any functionality in the
-      software that is protected by the license key.
-    - You may not alter, remove, or obscure any licensing, copyright, or other notices
-      of the licensor in the software. Any use of the licensor’s trademarks is subject
-      to applicable law.
-    - Canyon GBS LLC respects the intellectual property rights of others and expects the
-      same in return. Canyon GBS™ and Advising App™ are registered trademarks of
-      Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks
-      vigorously.
-    - The software solution, including services, infrastructure, and code, is offered as a
-      Software as a Service (SaaS) by Canyon GBS LLC.
-    - Use of this software implies agreement to the license terms and conditions as stated
-      in the Elastic License 2.0.
-
-    For more information or inquiries please visit our website at
-    https://www.canyongbs.com or contact us via email at legal@canyongbs.com.
-
-</COPYRIGHT>
-*/
-
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
-use Illuminate\Database\Migrations\Migration;
-use Illuminate\Support\Facades\DB;
-
-return new class () extends Migration {
-    public function up(): void
-    {
-        DB::transaction(function () {
-            DB::table('engagements')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    $json = [
-                        'type' => 'doc',
-                        'content' => [[
-                            'type' => 'paragraph',
-                            'attrs' => ['class' => null, 'style' => null],
-                            'content' => [[
-                                'type' => 'text',
-                                'text' => $row->subject,
-                            ]],
-                        ]],
-                    ];
-                    DB::table('engagements')
-                        ->where('id', $row->id)
-                        ->update(['subject' => json_encode($json)]);
-                }
-            });
-
-            DB::table('engagement_batches')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    $json = [
-                        'type' => 'doc',
-                        'content' => [[
-                            'type' => 'paragraph',
-                            'attrs' => ['class' => null, 'style' => null],
-                            'content' => [[
-                                'type' => 'text',
-                                'text' => $row->subject,
-                            ]],
-                        ]],
-                    ];
-                    DB::table('engagement_batches')
-                        ->where('id', $row->id)
-                        ->update(['subject' => json_encode($json)]);
-                }
-            });
-
-            DB::table('form_email_auto_replies')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    $json = [
-                        'type' => 'doc',
-                        'content' => [[
-                            'type' => 'paragraph',
-                            'attrs' => ['class' => null, 'style' => null],
-                            'content' => [[
-                                'type' => 'text',
-                                'text' => $row->subject,
-                            ]],
-                        ]],
-                    ];
-                    DB::table('form_email_auto_replies')
-                        ->where('id', $row->id)
-                        ->update(['subject' => json_encode($json)]);
-                }
-            });
-
-            DB::table('campaign_actions')
-                ->select('id', 'data')
-                ->where('type', 'bulk_engagement_email')
-                ->whereRaw("json_typeof(data::json -> 'subject') = 'string'")
-                ->chunkById(500, function ($rows) {
-                    foreach ($rows as $row) {
-                        $data = json_decode($row->data, true);
-
-                        $json = [
-                            'channel' => $data['channel'] ?? null,
-                            'subject' => [
-                                'type' => 'doc',
-                                'content' => [[
-                                    'type' => 'paragraph',
-                                    'attrs' => ['class' => null, 'style' => null],
-                                    'content' => [[
-                                        'type' => 'text',
-                                        'text' => $data['subject'] ?? '',
-                                    ]],
-                                ]],
-                            ],
-                            'body' => $data['body'] ?? null,
-                        ];
-
-                        DB::table('campaign_actions')
-                            ->where('id', $row->id)
-                            ->update(['data' => json_encode($json)]);
-                    }
-                });
-
-            RefactorEngagementCampaignSubjectToJsonb::activate();
-        });
-    }
-
-    public function down(): void
-    {
-        DB::transaction(function () {
-            DB::table('engagements')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    /** @var array<string, mixed> $decoded */
-                    $decoded = json_decode($row->subject, true);
-
-                    /** @var array<int, array{type: string, content?: array<int, array{type: string, text: string}>}> $blocks */
-                    $blocks = $decoded['content'] ?? [];
-
-                    /** @var string $text */
-                    $text = collect($blocks)
-                        ->flatMap(fn ($block) => collect($block['content'] ?? []))
-                        ->filter(fn ($node) => $node['type'] === 'text')
-                        ->pluck('text')
-                        ->implode('');
-
-                    DB::table('engagements')
-                        ->where('id', $row->id)
-                        ->update(['subject' => $text]);
-                }
-            });
-
-            DB::table('engagement_batches')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    /** @var array<string, mixed> $decoded */
-                    $decoded = json_decode($row->subject, true);
-
-                    /** @var array<int, array{type: string, content?: array<int, array{type: string, text: string}>}> $blocks */
-                    $blocks = $decoded['content'] ?? [];
-
-                    /** @var string $text */
-                    $text = collect($blocks)
-                        ->flatMap(fn ($block) => collect($block['content'] ?? []))
-                        ->filter(fn ($node) => $node['type'] === 'text')
-                        ->pluck('text')
-                        ->implode('');
-
-                    DB::table('engagement_batches')
-                        ->where('id', $row->id)
-                        ->update(['subject' => $text]);
-                }
-            });
-
-            DB::table('form_email_auto_replies')->select('id', 'subject')->chunkById(500, function ($rows) {
-                foreach ($rows as $row) {
-                    /** @var array<string, mixed> $decoded */
-                    $decoded = json_decode($row->subject, true);
-
-                    /** @var array<int, array{type: string, content?: array<int, array{type: string, text: string}>}> $blocks */
-                    $blocks = $decoded['content'] ?? [];
-
-                    /** @var string $text */
-                    $text = collect($blocks)
-                        ->flatMap(fn ($block) => collect($block['content'] ?? []))
-                        ->filter(fn ($node) => $node['type'] === 'text')
-                        ->pluck('text')
-                        ->implode('');
-
-                    DB::table('form_email_auto_replies')
-                        ->where('id', $row->id)
-                        ->update(['subject' => $text]);
-                }
-            });
-
-            DB::table('campaign_actions')
-                ->select('id', 'data')
-                ->where('type', 'bulk_engagement_email')
-                ->whereRaw("json_typeof(data::json -> 'subject') = 'object'")
-                ->whereRaw("(data::json -> 'subject' ->> 'type') = 'doc'")
-                ->chunkById(500, function ($rows) {
-                    foreach ($rows as $row) {
-                        $data = json_decode($row->data, true);
-
-                        $json = [
-                            'channel' => $data['channel'] ?? null,
-                            'subject' => $data['subject']['content'][0]['content'][0]['text'] ?? '',
-                            'body' => $data['body'] ?? null,
-                        ];
-
-                        DB::table('campaign_actions')
-                            ->where('id', $row->id)
-                            ->update(['data' => json_encode($json)]);
-                    }
-                });
-
-            RefactorEngagementCampaignSubjectToJsonb::deactivate();
-        });
-    }
-};
diff --git a/app-modules/engagement/resources/views/components/engagement-timeline-item.blade.php b/app-modules/engagement/resources/views/components/engagement-timeline-item.blade.php
index 274a340b8b..fd82e8da94 100644
--- a/app-modules/engagement/resources/views/components/engagement-timeline-item.blade.php
+++ b/app-modules/engagement/resources/views/components/engagement-timeline-item.blade.php
@@ -34,7 +34,6 @@
 @php
     use App\Filament\Resources\UserResource;
     use AdvisingApp\Notification\Enums\NotificationChannel;
-    use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 @endphp
 
 <div>
@@ -75,11 +74,9 @@ class="my-4 rounded-lg border-2 border-gray-200 p-2 text-base font-normal text-g
         @if (!blank($record->subject))
             <div class="mb-2 flex flex-col">
                 <p class="text-xs text-gray-400 dark:text-gray-500">Subject:</p>
-                @if (RefactorEngagementCampaignSubjectToJsonb::active())
-                    <p>{{ $record->getSubject() }}</p>
-                @else
-                    <p>{{ $record->subject }}</p>
-                @endif
+
+                <p>{{ $record->getSubject() }}</p>
+
             </div>
         @endif
         <div class="flex flex-col">
diff --git a/app-modules/engagement/src/Actions/GenerateEngagementSubjectContent.php b/app-modules/engagement/src/Actions/GenerateEngagementSubjectContent.php
index efcf370f71..2e632ee3cd 100644
--- a/app-modules/engagement/src/Actions/GenerateEngagementSubjectContent.php
+++ b/app-modules/engagement/src/Actions/GenerateEngagementSubjectContent.php
@@ -43,19 +43,11 @@
 class GenerateEngagementSubjectContent
 {
     /**
-     * @param string|array<string|int, string>|null $content
+     * @param string|array<string|int, string> $content
      * @param array<string, mixed> $mergeData
-     *
-     * @todo RefactorEngagementCampaignSubjectToJsonb: Once migration to JSONB subject is complete and all usages are updated,
-     *       - make $content non-nullable (string|array)
-     *       - remove the blank($content) check
      */
-    public function __invoke(string|array|null $content, array $mergeData, Model $record, string $recordAttribute): HtmlString
+    public function __invoke(string|array $content, array $mergeData, Model $record, string $recordAttribute): HtmlString
     {
-        if (blank($content)) {
-            return new HtmlString('');
-        }
-
         $html = tiptap_converter()
             ->mergeTagsMap($mergeData)
             ->record($record, $recordAttribute)
diff --git a/app-modules/engagement/src/DataTransferObjects/EngagementCreationData.php b/app-modules/engagement/src/DataTransferObjects/EngagementCreationData.php
index aaf764bdc8..b2b993d345 100644
--- a/app-modules/engagement/src/DataTransferObjects/EngagementCreationData.php
+++ b/app-modules/engagement/src/DataTransferObjects/EngagementCreationData.php
@@ -54,10 +54,7 @@ public function __construct(
         public User $user,
         public CanBeNotified | Collection $recipient,
         public NotificationChannel $channel,
-        /**
-         * @todo RefactorEngagementCampaignSubjectToJsonb: Change this to `?array` once full migration to JSONB subject is complete.
-         */
-        public string|array|null $subject = null,
+        public ?array $subject = null,
         public ?array $body = null,
         public array $temporaryBodyImages = [],
         public ?CarbonInterface $scheduledAt = null,
diff --git a/app-modules/engagement/src/Filament/Actions/BulkEngagementAction.php b/app-modules/engagement/src/Filament/Actions/BulkEngagementAction.php
index dcaf2a377f..c1e2f9599f 100644
--- a/app-modules/engagement/src/Filament/Actions/BulkEngagementAction.php
+++ b/app-modules/engagement/src/Filament/Actions/BulkEngagementAction.php
@@ -42,13 +42,11 @@
 use AdvisingApp\Engagement\Models\EmailTemplate;
 use AdvisingApp\Notification\Enums\NotificationChannel;
 use AdvisingApp\Notification\Models\Contracts\CanBeNotified;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use Filament\Forms\Components\Actions;
 use Filament\Forms\Components\Actions\Action;
 use Filament\Forms\Components\Checkbox;
 use Filament\Forms\Components\DateTimePicker;
 use Filament\Forms\Components\Select;
-use Filament\Forms\Components\TextInput;
 use Filament\Forms\Components\Toggle;
 use Filament\Forms\Components\Wizard\Step;
 use Filament\Forms\Form;
@@ -85,13 +83,6 @@ public static function make(string $context)
                 Step::make('Engagement Details')
                     ->description("Add the details that will be sent to the selected {$context}")
                     ->schema([
-                        TextInput::make('subject')
-                            ->autofocus()
-                            ->required()
-                            ->placeholder(__('Subject'))
-                            ->hidden(fn (Get $get): bool => $get('channel') === NotificationChannel::Sms->value)
-                            ->columnSpanFull()
-                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
                         TiptapEditor::make('subject')
                             ->label('Subject')
                             ->mergeTags([
@@ -107,8 +98,7 @@ public static function make(string $context)
                             ->profile('sms')
                             ->required()
                             ->placeholder('Enter the email subject here...')
-                            ->columnSpanFull()
-                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                            ->columnSpanFull(),
                         TiptapEditor::make('body')
                             ->disk('s3-public')
                             ->label('Body')
diff --git a/app-modules/engagement/src/Filament/Actions/RelationManagerSendEngagementAction.php b/app-modules/engagement/src/Filament/Actions/RelationManagerSendEngagementAction.php
index c0095599f5..a2a4cd65d7 100644
--- a/app-modules/engagement/src/Filament/Actions/RelationManagerSendEngagementAction.php
+++ b/app-modules/engagement/src/Filament/Actions/RelationManagerSendEngagementAction.php
@@ -47,7 +47,6 @@
 use AdvisingApp\Prospect\Models\ProspectPhoneNumber;
 use AdvisingApp\StudentDataModel\Models\StudentEmailAddress;
 use AdvisingApp\StudentDataModel\Models\StudentPhoneNumber;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use Filament\Actions\StaticAction;
 use Filament\Forms\Components\Actions;
 use Filament\Forms\Components\Actions\Action as FormComponentAction;
@@ -56,7 +55,6 @@
 use Filament\Forms\Components\Fieldset;
 use Filament\Forms\Components\Grid;
 use Filament\Forms\Components\Select;
-use Filament\Forms\Components\TextInput;
 use Filament\Forms\Components\Toggle;
 use Filament\Forms\Form;
 use Filament\Forms\Get;
@@ -139,13 +137,6 @@ protected function setUp(): void
                     ]),
                 Fieldset::make('Content')
                     ->schema([
-                        TextInput::make('subject')
-                            ->autofocus()
-                            ->required()
-                            ->placeholder(__('Subject'))
-                            ->hidden(fn (Get $get): bool => $get('channel') === NotificationChannel::Sms->value)
-                            ->columnSpanFull()
-                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
                         TiptapEditor::make('subject')
                             ->label('Subject')
                             ->mergeTags([
@@ -166,8 +157,7 @@ protected function setUp(): void
                             ->profile('sms')
                             ->required()
                             ->placeholder('Enter the email subject here...')
-                            ->columnSpanFull()
-                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                            ->columnSpanFull(),
                         TiptapEditor::make('body')
                             ->disk('s3-public')
                             ->label('Body')
@@ -277,14 +267,10 @@ protected function setUp(): void
             ]))
             ->action(function (array $data, Form $form, RelationManager $livewire) {
                 $recipient = $livewire->getOwnerRecord();
-
-                if (RefactorEngagementCampaignSubjectToJsonb::active()) {
-                    $data['subject'] ??= ['type' => 'doc', 'content' => []];
-                    $data['subject']['content'] = [
-                        ...($data['subject']['content'] ?? []),
-                    ];
-                }
-
+                $data['subject'] ??= ['type' => 'doc', 'content' => []];
+                $data['subject']['content'] = [
+                    ...($data['subject']['content'] ?? []),
+                ];
                 $data['body'] ??= ['type' => 'doc', 'content' => []];
                 $data['body']['content'] = [
                     ...($data['body']['content'] ?? []),
diff --git a/app-modules/engagement/src/Filament/Actions/SendEngagementAction.php b/app-modules/engagement/src/Filament/Actions/SendEngagementAction.php
index d39812e04d..f05b08f836 100644
--- a/app-modules/engagement/src/Filament/Actions/SendEngagementAction.php
+++ b/app-modules/engagement/src/Filament/Actions/SendEngagementAction.php
@@ -48,7 +48,6 @@
 use AdvisingApp\StudentDataModel\Models\Student;
 use AdvisingApp\StudentDataModel\Models\StudentEmailAddress;
 use AdvisingApp\StudentDataModel\Models\StudentPhoneNumber;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use Exception;
 use Filament\Actions\Action;
 use Filament\Actions\StaticAction;
@@ -59,7 +58,6 @@
 use Filament\Forms\Components\Fieldset;
 use Filament\Forms\Components\Grid;
 use Filament\Forms\Components\Select;
-use Filament\Forms\Components\TextInput;
 use Filament\Forms\Components\Toggle;
 use Filament\Forms\Form;
 use Filament\Forms\Get;
@@ -166,13 +164,6 @@ protected function setUp(): void
                     ]),
                 Fieldset::make('Content')
                     ->schema([
-                        TextInput::make('subject')
-                            ->autofocus()
-                            ->required()
-                            ->placeholder(__('Subject'))
-                            ->hidden(fn (Get $get): bool => $get('channel') === NotificationChannel::Sms->value)
-                            ->columnSpanFull()
-                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
                         TiptapEditor::make('subject')
                             ->label('Subject')
                             ->mergeTags([
@@ -193,8 +184,7 @@ protected function setUp(): void
                             ->profile('sms')
                             ->required()
                             ->placeholder('Enter the email subject here...')
-                            ->columnSpanFull()
-                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                            ->columnSpanFull(),
                         TiptapEditor::make('body')
                             ->disk('s3-public')
                             ->label('Body')
@@ -305,14 +295,10 @@ protected function setUp(): void
             ->action(function (array $data, Form $form, Page $livewire) {
                 /** @var Student | Prospect $recipient */
                 $recipient = $this->getEducatable();
-
-                if (RefactorEngagementCampaignSubjectToJsonb::active()) {
-                    $data['subject'] ??= ['type' => 'doc', 'content' => []];
-                    $data['subject']['content'] = [
-                        ...($data['subject']['content'] ?? []),
-                    ];
-                }
-
+                $data['subject'] ??= ['type' => 'doc', 'content' => []];
+                $data['subject']['content'] = [
+                    ...($data['subject']['content'] ?? []),
+                ];
                 $data['body'] ??= ['type' => 'doc', 'content' => []];
                 $data['body']['content'] = [
                     ...($data['body']['content'] ?? []),
diff --git a/app-modules/engagement/src/Models/Engagement.php b/app-modules/engagement/src/Models/Engagement.php
index d9222597bd..94935a4dfd 100644
--- a/app-modules/engagement/src/Models/Engagement.php
+++ b/app-modules/engagement/src/Models/Engagement.php
@@ -54,7 +54,6 @@
 use AdvisingApp\Timeline\Models\Contracts\ProvidesATimeline;
 use AdvisingApp\Timeline\Models\Timeline;
 use AdvisingApp\Timeline\Timelines\EngagementTimeline;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Models\BaseModel;
 use App\Models\User;
 use Exception;
@@ -105,6 +104,7 @@ class Engagement extends BaseModel implements Auditable, CanTriggerAutoSubscript
         'scheduled_at' => 'datetime',
         'dispatched_at' => 'datetime',
         'channel' => NotificationChannel::class,
+        'subject' => 'array',
     ];
 
     // TODO Consider changing this relationship if we ever needed to timeline something else where records might be shared across entities
@@ -293,17 +293,6 @@ public function getDeliveryMethod(): NotificationChannel
         return $this->channel;
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
-    protected function casts(): array
-    {
-        return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
-        ];
-    }
-
     protected static function booted(): void
     {
         static::addGlobalScope('licensed', function (Builder $builder) {
diff --git a/app-modules/engagement/src/Models/EngagementBatch.php b/app-modules/engagement/src/Models/EngagementBatch.php
index e52dbdd524..d5372f1a5c 100644
--- a/app-modules/engagement/src/Models/EngagementBatch.php
+++ b/app-modules/engagement/src/Models/EngagementBatch.php
@@ -44,7 +44,6 @@
 use AdvisingApp\Engagement\Observers\EngagementBatchObserver;
 use AdvisingApp\Notification\Enums\NotificationChannel;
 use AdvisingApp\Notification\Models\Contracts\CanBeNotified;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Models\BaseModel;
 use App\Models\User;
 use DOMDocument;
@@ -82,6 +81,7 @@ class EngagementBatch extends BaseModel implements ExecutableFromACampaignAction
         'total_engagements' => 'integer',
         'processed_engagements' => 'integer',
         'successful_engagements' => 'integer',
+        'subject' => 'array',
     ];
 
     /**
@@ -131,15 +131,4 @@ public static function renderWithMergeTags(string $html): string
 
         return $dom->saveHTML();
     }
-
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
-    protected function casts(): array
-    {
-        return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
-        ];
-    }
 }
diff --git a/app-modules/engagement/src/Notifications/EngagementFailedNotification.php b/app-modules/engagement/src/Notifications/EngagementFailedNotification.php
index 394810fa12..05a1d54824 100644
--- a/app-modules/engagement/src/Notifications/EngagementFailedNotification.php
+++ b/app-modules/engagement/src/Notifications/EngagementFailedNotification.php
@@ -39,7 +39,6 @@
 use AdvisingApp\Engagement\Models\Engagement;
 use AdvisingApp\Notification\Models\Contracts\CanBeNotified;
 use AdvisingApp\Notification\Notifications\Messages\MailMessage;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Models\NotificationSetting;
 use App\Models\User;
 use Filament\Notifications\Notification as FilamentNotification;
@@ -69,13 +68,7 @@ public function toMail(object $notifiable): MailMessage
             ->settings($this->resolveNotificationSetting($notifiable))
             ->subject('An engagement failed to deliver')
             ->line("The engagement {$this->engagement->channel->getLabel()} failed to be delivered to {$this->engagement->recipient->display_name}:")
-            ->line(
-                'Subject: ' . (
-                    RefactorEngagementCampaignSubjectToJsonb::active()
-                      ? $this->engagement->getSubject()
-                      : ($this->engagement->subject ?? 'n/a')
-                )
-            )
+            ->line('Subject: ' . $this->engagement->getSubject())
             ->line('Body: ' . $this->engagement->getBody());
     }
 
diff --git a/app-modules/engagement/src/Notifications/EngagementNotification.php b/app-modules/engagement/src/Notifications/EngagementNotification.php
index 309ef36b8c..7fec2b4ad5 100644
--- a/app-modules/engagement/src/Notifications/EngagementNotification.php
+++ b/app-modules/engagement/src/Notifications/EngagementNotification.php
@@ -50,7 +50,6 @@
 use AdvisingApp\Notification\Notifications\Contracts\HasBeforeSendHook;
 use AdvisingApp\Notification\Notifications\Messages\MailMessage;
 use AdvisingApp\Notification\Notifications\Messages\TwilioMessage;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use Illuminate\Bus\Queueable;
 use Illuminate\Contracts\Queue\ShouldQueue;
 use Illuminate\Database\Eloquent\Model;
@@ -82,11 +81,7 @@ public function toMail(object $notifiable): MailMessage
     {
         return MailMessage::make()
             ->to($this->engagement->recipient_route)
-            ->subject(
-                RefactorEngagementCampaignSubjectToJsonb::active()
-                    ? $this->engagement->getSubject()
-                    : $this->engagement->subject
-            )
+            ->subject($this->engagement->getSubject())
             ->greeting("Hello {$this->engagement->recipient->display_name}!")
             ->content($this->engagement->getBody());
     }
diff --git a/app-modules/form/src/Filament/Resources/FormResource/Pages/ManageFormEmailAutoReply.php b/app-modules/form/src/Filament/Resources/FormResource/Pages/ManageFormEmailAutoReply.php
index 3eda653617..63acc309da 100644
--- a/app-modules/form/src/Filament/Resources/FormResource/Pages/ManageFormEmailAutoReply.php
+++ b/app-modules/form/src/Filament/Resources/FormResource/Pages/ManageFormEmailAutoReply.php
@@ -39,14 +39,12 @@
 use AdvisingApp\Engagement\Models\EmailTemplate;
 use AdvisingApp\Form\Filament\Resources\FormResource;
 use AdvisingApp\Form\Models\Form;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Filament\Resources\Pages\EditRecord\Concerns\EditPageRedirection;
 use App\Models\User;
 use Filament\Forms\Components\Actions\Action;
 use Filament\Forms\Components\Checkbox;
 use Filament\Forms\Components\Section;
 use Filament\Forms\Components\Select;
-use Filament\Forms\Components\TextInput;
 use Filament\Forms\Components\Toggle;
 use Filament\Forms\Form as FilamentForm;
 use Filament\Forms\Get;
@@ -90,15 +88,7 @@ public function form(FilamentForm $form): FilamentForm
                         Toggle::make('is_enabled')
                             ->label('Enabled')
                             ->live(),
-                        TextInput::make('subject')
-                            ->required(fn (Get $get) => $get('is_enabled'))
-                            ->placeholder('Subject')
-                            ->columnSpanFull()
-                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
-                        /**
-                         * @todo Remove 'temp_subject' name once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-                         */
-                        TiptapEditor::make(RefactorEngagementCampaignSubjectToJsonb::active() ? 'subject' : 'temp_subject')
+                        TiptapEditor::make('subject')
                             ->mergeTags([
                                 'recipient first name',
                                 'recipient last name',
@@ -111,8 +101,7 @@ public function form(FilamentForm $form): FilamentForm
                             ->helperText('You can insert recipient information by typing {{ and choosing a merge value to insert.')
                             ->columnSpanFull()
                             ->placeholder('Enter the email subject here...')
-                            ->showMergeTagsInBlocksPanel(false)
-                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                            ->showMergeTagsInBlocksPanel(false),
                         TiptapEditor::make('body')
                             ->disk('s3-public')
                             ->mergeTags([
diff --git a/app-modules/form/src/Models/FormEmailAutoReply.php b/app-modules/form/src/Models/FormEmailAutoReply.php
index 88daeea2e1..f88fa00b11 100644
--- a/app-modules/form/src/Models/FormEmailAutoReply.php
+++ b/app-modules/form/src/Models/FormEmailAutoReply.php
@@ -40,7 +40,6 @@
 use AdvisingApp\Engagement\Actions\GenerateEngagementSubjectContent;
 use AdvisingApp\Prospect\Models\Prospect;
 use AdvisingApp\StudentDataModel\Models\Student;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Models\BaseModel;
 use Illuminate\Database\Eloquent\Relations\BelongsTo;
 use Illuminate\Database\Eloquent\SoftDeletes;
@@ -64,6 +63,7 @@ class FormEmailAutoReply extends BaseModel implements HasMedia
     protected $casts = [
         'body' => 'array',
         'is_enabled' => 'boolean',
+        'subject' => 'array',
     ];
 
     /**
@@ -109,15 +109,4 @@ public function getMergeData(Student|Prospect|null $author): array
             'student preferred name' => $author->getAttribute($author->displayPreferredNameKey()),
         ];
     }
-
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
-    protected function casts(): array
-    {
-        return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
-        ];
-    }
 }
diff --git a/app-modules/form/src/Notifications/FormSubmissionAutoReplyNotification.php b/app-modules/form/src/Notifications/FormSubmissionAutoReplyNotification.php
index 0d501bea30..c6a7934b62 100644
--- a/app-modules/form/src/Notifications/FormSubmissionAutoReplyNotification.php
+++ b/app-modules/form/src/Notifications/FormSubmissionAutoReplyNotification.php
@@ -70,7 +70,7 @@ public function toMail(object $notifiable): MailMessage
         $author = $this->submission->author;
 
         return MailMessage::make()
-            ->subject($form->emailAutoReply->subject)
+            ->subject($form->emailAutoReply->getSubject($author))
             ->content($form->emailAutoReply->getBody($author));
     }
 }
diff --git a/app-modules/student-data-model/src/Filament/Resources/EducatableResource/Widgets/EducatableActivityFeedWidget.php b/app-modules/student-data-model/src/Filament/Resources/EducatableResource/Widgets/EducatableActivityFeedWidget.php
index 5beac8e5fe..20db1c4870 100644
--- a/app-modules/student-data-model/src/Filament/Resources/EducatableResource/Widgets/EducatableActivityFeedWidget.php
+++ b/app-modules/student-data-model/src/Filament/Resources/EducatableResource/Widgets/EducatableActivityFeedWidget.php
@@ -47,7 +47,6 @@
 use AdvisingApp\Task\Histories\TaskHistory;
 use AdvisingApp\Timeline\Livewire\Concerns\CanLoadTimelineRecords;
 use AdvisingApp\Timeline\Livewire\Concerns\HasTimelineRecords;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Models\User;
 use Filament\Actions\Concerns\InteractsWithActions;
 use Filament\Actions\Contracts\HasActions;
@@ -108,12 +107,7 @@ public function getTimelineRecordDescription(Model $record): ?string
     {
         return (string) str(match ($record->getMorphClass()) {
             'interaction' => "Subject: {$record->subject}",
-            'engagement' => RefactorEngagementCampaignSubjectToJsonb::active()
-            ? $this->getEngagementDescription($record)
-            : match ($record->getDeliveryMethod()) {
-                NotificationChannel::Sms => "Preview: {$record->getBodyMarkdown()}",
-                default => "Subject: {$record->subject}",
-            },
+            'engagement' => $this->getEngagementDescription($record),
             'engagement_response' => 'Preview: ' . str($record->getBody())->stripTags(),
             'task_history' => "Title: {$record->subject?->title}",
             'alert_history' => "{$record->subject?->severity->getLabel()} severity, " . str($record->subject?->description)->limit(200),
diff --git a/app-modules/student-data-model/src/Filament/Resources/StudentResource/RelationManagers/EngagementsRelationManager.php b/app-modules/student-data-model/src/Filament/Resources/StudentResource/RelationManagers/EngagementsRelationManager.php
index c45fa47915..7d6980f564 100644
--- a/app-modules/student-data-model/src/Filament/Resources/StudentResource/RelationManagers/EngagementsRelationManager.php
+++ b/app-modules/student-data-model/src/Filament/Resources/StudentResource/RelationManagers/EngagementsRelationManager.php
@@ -46,7 +46,6 @@
 use AdvisingApp\Notification\Models\EmailMessageEvent;
 use AdvisingApp\Notification\Models\SmsMessageEvent;
 use AdvisingApp\Timeline\Models\Timeline;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Infolists\Components\EngagementBody;
 use Filament\Infolists\Components\Fieldset as InfolistFieldset;
 use Filament\Infolists\Components\RepeatableEntry;
@@ -92,11 +91,6 @@ public function infolist(Infolist $infolist): Infolist
                                     ->getStateUsing(fn (Timeline $record): string => $record->timelineable->user->name ?? 'N/A'),
                                 InfolistFieldset::make('Content')
                                     ->schema([
-                                        TextEntry::make('subject')
-                                            ->getStateUsing(fn (Timeline $record): string|array|null => $record->timelineable->subject)
-                                            ->hidden(fn ($state): bool => blank($state))
-                                            ->columnSpanFull()
-                                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
                                         TextEntry::make('subject')
                                             ->getStateUsing(function (Timeline $record): ?string {
                                                 $model = $record->timelineable;
@@ -107,8 +101,7 @@ public function infolist(Infolist $infolist): Infolist
 
                                                 return null;
                                             })
-                                            ->columnSpanFull()
-                                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                                            ->columnSpanFull(),
                                         EngagementBody::make('body')
                                             ->getStateUsing(fn (Timeline $record): HtmlString => $record->timelineable->getBody())
                                             ->columnSpanFull(),
diff --git a/app-modules/timeline/src/Timelines/EngagementTimeline.php b/app-modules/timeline/src/Timelines/EngagementTimeline.php
index 7400dd8304..fe8fdd57b9 100644
--- a/app-modules/timeline/src/Timelines/EngagementTimeline.php
+++ b/app-modules/timeline/src/Timelines/EngagementTimeline.php
@@ -39,7 +39,6 @@
 use AdvisingApp\Engagement\Models\Engagement;
 use AdvisingApp\Notification\Enums\NotificationChannel;
 use AdvisingApp\Timeline\Models\CustomTimeline;
-use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 use App\Infolists\Components\EngagementBody;
 use Exception;
 use Filament\Actions\ViewAction;
@@ -86,15 +85,10 @@ public function modalViewAction(): ViewAction
                     ->label('Created By'),
                 Fieldset::make('Content')
                     ->schema([
-                        TextEntry::make('subject')
-                            ->hidden(fn ($state): bool => blank($state))
-                            ->columnSpanFull()
-                            ->visible(! RefactorEngagementCampaignSubjectToJsonb::active()),
                         TextEntry::make('subject')
                             ->hidden(fn ($state): bool => blank($state))
                             ->getStateUsing(fn (Engagement $engagement): HtmlString => $engagement->getSubject())
-                            ->columnSpanFull()
-                            ->visible(RefactorEngagementCampaignSubjectToJsonb::active()),
+                            ->columnSpanFull(),
                         EngagementBody::make('body')
                             ->getStateUsing(fn (Engagement $engagement): HtmlString => $engagement->getBody())
                             ->columnSpanFull(),
diff --git a/app/Features/RefactorEngagementCampaignSubjectToJsonb.php b/app/Features/RefactorEngagementCampaignSubjectToJsonb.php
deleted file mode 100644
index 6dbdfc9103..0000000000
--- a/app/Features/RefactorEngagementCampaignSubjectToJsonb.php
+++ /dev/null
@@ -1,47 +0,0 @@
-<?php
-
-/*
-<COPYRIGHT>
-
-    Copyright © 2016-2025, Canyon GBS LLC. All rights reserved.
-
-    Advising App™ is licensed under the Elastic License 2.0. For more details,
-    see https://github.com/canyongbs/advisingapp/blob/main/LICENSE.
-
-    Notice:
-
-    - You may not provide the software to third parties as a hosted or managed
-      service, where the service provides users with access to any substantial set of
-      the features or functionality of the software.
-    - You may not move, change, disable, or circumvent the license key functionality
-      in the software, and you may not remove or obscure any functionality in the
-      software that is protected by the license key.
-    - You may not alter, remove, or obscure any licensing, copyright, or other notices
-      of the licensor in the software. Any use of the licensor’s trademarks is subject
-      to applicable law.
-    - Canyon GBS LLC respects the intellectual property rights of others and expects the
-      same in return. Canyon GBS™ and Advising App™ are registered trademarks of
-      Canyon GBS LLC, and we are committed to enforcing and protecting our trademarks
-      vigorously.
-    - The software solution, including services, infrastructure, and code, is offered as a
-      Software as a Service (SaaS) by Canyon GBS LLC.
-    - Use of this software implies agreement to the license terms and conditions as stated
-      in the Elastic License 2.0.
-
-    For more information or inquiries please visit our website at
-    https://www.canyongbs.com or contact us via email at legal@canyongbs.com.
-
-</COPYRIGHT>
-*/
-
-namespace App\Features;
-
-use App\Support\AbstractFeatureFlag;
-
-class RefactorEngagementCampaignSubjectToJsonb extends AbstractFeatureFlag
-{
-    public function resolve(mixed $scope): mixed
-    {
-        return false;
-    }
-}
diff --git a/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php b/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php
index 36af2e5f58..6c3152d50b 100644
--- a/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php
+++ b/resources/views/filament/forms/components/campaigns/actions/bulk-engagement.blade.php
@@ -36,7 +36,6 @@
     use AdvisingApp\Notification\Enums\NotificationChannel;
     use AdvisingApp\Engagement\Models\EngagementBatch;
     use Carbon\Carbon;
-    use App\Features\RefactorEngagementCampaignSubjectToJsonb;
 @endphp
 
 <x-filament::fieldset>
@@ -60,13 +59,9 @@
         @if (isset($action['subject']))
             <div class="flex flex-col pt-3">
                 <dt class="mb-1 text-sm text-gray-500 dark:text-gray-400">Subject</dt>
-                @if (RefactorEngagementCampaignSubjectToJsonb::active())
-                    <dd class="text-sm font-semibold">
-                        {!! EngagementBatch::renderWithMergeTags(tiptap_converter()->asHTML($action['subject'])) !!}
-                    </dd>
-                @else
-                    <dd class="text-sm font-semibold">{{ $action['subject'] }}</dd>
-                @endif
+                <dd class="text-sm font-semibold">
+                    {!! EngagementBatch::renderWithMergeTags(tiptap_converter()->asHTML($action['subject'])) !!}
+                </dd>
             </div>
         @endif
         @if ($action['body'])


Reviews:
user: login: Orrison
state: CHANGES_REQUESTED
author_association: COLLABORATOR
submitted_at: 2025-05-13T21:32:53Z
user: login: ketan-canyon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:43:59Z
user: login: ketan-canyon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:44:33Z
user: login: ketan-canyon
state: COMMENTED
author_association: CONTRIBUTOR
submitted_at: 2025-05-15T19:44:38Z
user: login: Orrison
state: COMMENTED
author_association: COLLABORATOR
submitted_at: 2025-05-16T13:20:43Z
user: login: Orrison
state: APPROVED
author_association: COLLABORATOR
submitted_at: 2025-05-16T13:21:16Z

Review comments:
diff_hunk: @@ -293,14 +292,10 @@ public function getDeliveryMethod(): NotificationChannel
         return $this->channel;
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
         ];
     }
path: app-modules/engagement/src/Models/Engagement.php
user: login: Orrison
body: Like the todo said. This can now be moved to the `casts` property and this method removed.
created_at: 2025-05-13T21:30:53Z
author_association: COLLABORATOR
diff_hunk: @@ -132,14 +131,10 @@ public static function renderWithMergeTags(string $html): string
         return $dom->saveHTML();
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
         ];
path: app-modules/engagement/src/Models/EngagementBatch.php
user: login: Orrison
body: Like the todo said. This can now be moved to the `casts` property and this method removed.
created_at: 2025-05-13T21:31:08Z
author_association: COLLABORATOR
diff_hunk: @@ -110,14 +109,10 @@ public function getMergeData(Student|Prospect|null $author): array
         ];
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
path: app-modules/form/src/Models/FormEmailAutoReply.php
user: login: Orrison
body: Like the todo said. This can now be moved to the `casts` property and this method removed.
created_at: 2025-05-13T21:31:56Z
author_association: COLLABORATOR
diff_hunk: @@ -293,14 +292,10 @@ public function getDeliveryMethod(): NotificationChannel
         return $this->channel;
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
         ];
     }
path: app-modules/engagement/src/Models/Engagement.php
user: login: ketan-canyon
body: Done. Really sorry its my mistake, I didn't read properly. Next time it will not happen.
created_at: 2025-05-15T19:43:59Z
author_association: CONTRIBUTOR
diff_hunk: @@ -132,14 +131,10 @@ public static function renderWithMergeTags(string $html): string
         return $dom->saveHTML();
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
         ];
path: app-modules/engagement/src/Models/EngagementBatch.php
user: login: ketan-canyon
body: Done. Really sorry its my mistake, I didn't read properly. Next time it will not happen.
created_at: 2025-05-15T19:44:33Z
author_association: CONTRIBUTOR
diff_hunk: @@ -110,14 +109,10 @@ public function getMergeData(Student|Prospect|null $author): array
         ];
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
path: app-modules/form/src/Models/FormEmailAutoReply.php
user: login: ketan-canyon
body: Done. Really sorry its my mistake, I didn't read properly. Next time it will not happen.
created_at: 2025-05-15T19:44:38Z
author_association: CONTRIBUTOR
diff_hunk: @@ -293,14 +292,10 @@ public function getDeliveryMethod(): NotificationChannel
         return $this->channel;
     }
 
-    /**
-     * @todo Remove this dynamic cast once `RefactorEngagementCampaignSubjectToJsonb` is removed.
-     *       Move 'subject' casting to the static `$casts` array: protected $casts = ['subject' => 'array'];
-     */
     protected function casts(): array
     {
         return [
-            'subject' => ! RefactorEngagementCampaignSubjectToJsonb::active() ? 'string' : 'array',
+            'subject' => 'array',
         ];
     }
path: app-modules/engagement/src/Models/Engagement.php
user: login: Orrison
body: It's alright.
created_at: 2025-05-16T13:20:43Z
author_association: COLLABORATOR

PR comments:
user: login: sonarqubecloud[bot]
created_at: 2025-05-15T19:42:16Z
body: ## [![Quality Gate Passed](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/checks/QualityGateBadge/qg-passed-20px.png 'Quality Gate Passed')](https://sonarcloud.io/dashboard?id=canyongbs_advisingapp&pullRequest=1538) **Quality Gate passed**  
Issues  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 New issues](https://sonarcloud.io/project/issues?id=canyongbs_advisingapp&pullRequest=1538&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/accepted-16px.png '') [0 Accepted issues](https://sonarcloud.io/project/issues?id=canyongbs_advisingapp&pullRequest=1538&issueStatuses=ACCEPTED)

Measures  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0 Security Hotspots](https://sonarcloud.io/project/security_hotspots?id=canyongbs_advisingapp&pullRequest=1538&issueStatuses=OPEN,CONFIRMED&sinceLeakPeriod=true)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [0.0% Coverage on New Code](https://sonarcloud.io/component_measures?id=canyongbs_advisingapp&pullRequest=1538&metric=new_coverage&view=list)  
![](https://sonarsource.github.io/sonarcloud-github-static-resources/v2/common/passed-16px.png '') [5.7% Duplication on New Code](https://sonarcloud.io/component_measures?id=canyongbs_advisingapp&pullRequest=1538&metric=new_duplicated_lines_density&view=list)  
  
[See analysis details on SonarQube Cloud](https://sonarcloud.io/dashboard?id=canyongbs_advisingapp&pullRequest=1538)


author_association: NONE