# Seed Archived status — 2026-05-11

## TL;DR

Migration `2026_05_11_065554_seed_archived_status_for_old_tests.php` renames
`statuses.id=5` from `Deprecated` → `Archived` with description
`"Old user test, rolled back when starting a new diagnostic"`.

180 existing `tests` rows already FK-reference id=5 (the entire point — they
inherit the new label). Forward-only: `down()` is a no-op because the
referenced row cannot be deleted while FK dependents exist.

End-to-end status: **committed → pushed → deployed to prod → verified.**
Prod health:200.

## Migration file

- Path: `database/migrations/2026_05_11_065554_seed_archived_status_for_old_tests.php`
- Filename timestamp: `2026_05_11_065554` (UTC HHMMSS at creation)
- `php -l`: **No syntax errors detected.**

### `up()`
```php
public function up(): void
{
    DB::table('statuses')->updateOrInsert(
        ['id' => 5],
        [
            'status'      => 'Archived',
            'description' => 'Old user test, rolled back when starting a new diagnostic',
            'created_at'  => now(),
            'updated_at'  => now(),
        ]
    );
}
```

### `down()`
```php
public function down(): void
{
    // Forward-only: id=5 is FK-referenced by tests (and 16 other
    // tables nominally). Renaming Deprecated -> Archived cannot be
    // safely reversed without destroying FK-dependent rows.
}
```

## Local pre-state (step 2)

```json
[
  {"id":1,"status":"Only Me","description":"Unpublished only creator can see it","created_at":"2018-05-04 07:15:37","updated_at":"2018-05-04 07:15:37"},
  {"id":2,"status":"Restricted","description":"Restricted by community","created_at":"2018-05-04 07:15:37","updated_at":"2018-05-04 07:15:37"},
  {"id":3,"status":"Public","description":"Everyone can see","created_at":"2018-05-04 07:15:37","updated_at":"2018-05-04 07:15:37"},
  {"id":4,"status":"Draft","description":"Draft and not to be published","created_at":"2018-05-04 07:15:37","updated_at":"2018-05-04 07:15:37"},
  {"id":5,"status":"Deprecated","description":"Outdated and not to be published","created_at":"2018-05-04 07:15:37","updated_at":"2018-05-04 07:15:37"}
]
```

## Local migrate output (step 5)

```
INFO  Running migrations.
  2026_05_11_065554_seed_archived_status_for_old_tests ........ 4.01ms DONE
```

## Step 6 — local row verification

```
php artisan tinker --execute='echo (DB::table("statuses")->where("id",5)->value("status")) ?: "MISSING";'
→ Archived
```

## Step 7 — idempotency (rollback + re-migrate)

```
$ php artisan migrate:rollback --step=1
INFO  Rolling back migrations.
  2026_05_11_065554_seed_archived_status_for_old_tests ........ 1.99ms DONE

# verified after rollback — data intact (down is no-op), only migrations record removed:
echo (DB::table("statuses")->where("id",5)->value("status")) ?: "MISSING"; → Archived
echo (DB::table("statuses")->where("id",5)->value("description")) ?: "MISSING";
  → Old user test, rolled back when starting a new diagnostic

$ php artisan migrate
INFO  Running migrations.
  2026_05_11_065554_seed_archived_status_for_old_tests ........ 2.90ms DONE

# post-state:
echo (DB::table("statuses")->where("id",5)->value("status")) ?: "MISSING"; → Archived
```

No errors on either step. Forward run is idempotent (`updateOrInsert` is a no-op
when target state already matches). Rollback is a clean no-op.

### Note on the initial idempotency attempt
The first version of `down()` did a raw `DELETE WHERE id=5`. It failed:

```
SQLSTATE[23000]: Integrity constraint violation: 1451 Cannot delete or update
a parent row: a foreign key constraint fails (`api`.`tests`,
CONSTRAINT `tests_status_id_foreign` FOREIGN KEY (`status_id`) REFERENCES
`statuses` (`id`))
```

180 rows in `tests` already FK-reference id=5 (sample: id=1,2,5 — user_id=2,
created 2018-05). Only `tests` (among 17 FK-bearing tables) actually has rows
pointing at id=5. We swapped `down()` to a no-op to reflect that this is a
forward-only rename, not a reversible seed.

## Commit

- SHA: `b3b665cb010303805482e84eb151af6d206a2bbd`
- Subject: `feat(data): seed Archived status (id=5) for old-test rollback`
- Branch: `master`
- Pushed: `ac3b65e..b3b665c  master -> master`

## Step 9 — prod deploy SSH output (verbatim)

```
$ ssh root@152.42.223.228 "cd /var/www/html/mathapi && git pull && php artisan migrate --force && php artisan config:clear && php artisan config:cache && chown -R www-data:www-data storage bootstrap/cache"

From github.com:2ppaamm/capstoneapi
   ac3b65e..b3b665c  master     -> origin/master
Updating ac3b65e..b3b665c
Fast-forward
 ...1_065554_seed_archived_status_for_old_tests.php | 26 ++++++++++++++++++++++
 1 file changed, 26 insertions(+)
 create mode 100644 database/migrations/2026_05_11_065554_seed_archived_status_for_old_tests.php

   INFO  Running migrations.
  2026_05_11_065554_seed_archived_status_for_old_tests ........... 4.68ms DONE

   INFO  Configuration cache cleared successfully.

   INFO  Configuration cached successfully.
```

Exit code: 0. (chown produced no output, which is the success case.)

## Step 10 — prod row verification

```
$ ssh root@152.42.223.228 'cd /var/www/html/mathapi && set -a && source .env && set +a && mysql -u "$DB_USERNAME" -p"$DB_PASSWORD" "$DB_DATABASE" -e "SELECT * FROM statuses WHERE id=5;"'

mysql: [Warning] Using a password on the command line interface can be insecure.
id   status     description                                                  created_at           updated_at
5    Archived   Old user test, rolled back when starting a new diagnostic   2026-05-11 07:37:56  2026-05-11 07:37:56
```

Exactly 1 row, status=Archived. ✓

## Step 11 — prod `migrate:status` tail

```
2026_05_08_174413_add_phase1b_supporting_indexes .................. [33] Ran
2026_05_09_150000_add_maxile_lookback_window_to_configs_table ..... [34] Ran
2026_05_10_120000_add_boundary_irt_columns_to_diagnostic_field_progress  [35] Ran
2026_05_11_065554_seed_archived_status_for_old_tests .............. [36] Ran
```

New migration recorded as batch 36, status **Ran**. ✓

## Step 12 — prod health

```
$ curl -s -o /dev/null -w 'health:%{http_code}\n' https://mathapi.allgifted.com/api/health
health:200
```

## Side effects / things to be aware of

1. **`created_at` on id=5 was overwritten.** `updateOrInsert` applies all
   `$values` on the update path, so id=5's original 2018-05-04 created_at was
   replaced with 2026-05-11 07:37:56. The row is otherwise identical to spec.
   Only relevant if any code keys on the historical creation date (unlikely).
2. **180 prod `tests` rows now read as `Archived`** (their status_id=5
   reference is unchanged; only the label moved). This was the intent.
3. **Forward-only.** Subsequent `php artisan migrate:rollback` will run a
   no-op for this migration — the label change can't be safely reverted while
   the 180 tests rows exist. To revert, you'd have to either update the row
   manually or reassign those tests to a different status_id first.
4. **One-time prod-write authorization was consumed for this deploy.**
   Read-only-prod resumes for subsequent sessions per CLAUDE.md.

## Deviations from the locked plan

- `down()` is a **no-op** instead of `DELETE WHERE id=5`. The locked
  destructive down() hit a hard FK violation on Step 7 idempotency rollback;
  Pam confirmed the no-op replacement after seeing the FK count (180 tests
  rows). Without this change, Step 7 stop condition would have aborted the
  deploy.
- Aside from that, the migration content, filename, commit message, and
  deploy commands match spec exactly.
