---
apiVersion: data-spec/v1
id: task-updated-by-field-layout-20260401
description: "Add Task Updated by metadata and surface it on the default Task detail layout"
issue: "#332"
created: 2026-04-01
scope: production-only
status: pending
tables: [Task, _System_Field, _System_PageLayout]
depends_on: []
---

# Task Updated By Field And Layout

## Summary

Issue #332 still reproduces a missing `Updated by` field on Task in staging. Live
`Task` describe and UI layout metadata already contain `Progress__c`,
`Next_Milestone__c`, and `Risk_and_Next_Step__c`, but there is no `Updated by`
field to render or persist. Because `Task` is a discovered object and is not
managed in `system_tables.json`, this change belongs in production data, not
bootstrap code.

## Changes

### 1. Add the physical `Updated_by__c` column to `Task`

```sql
ALTER TABLE Task
ADD COLUMN IF NOT EXISTS Updated_by__c VARCHAR(255) NULL;
```

**Idempotency**: `ADD COLUMN IF NOT EXISTS` is a no-op when the column already
exists.

### 2. Register `Task.Updated_by__c` in `_System_Field` as a lookup to `User`

```sql
UPDATE _System_Field f
JOIN _System_Object o ON o.Id = f.ObjectId
SET f.Label = 'Updated by',
    f.PhysicalName = 'Updated_by__c',
    f.Type = 'reference',
    f.ReferenceTo = JSON_ARRAY('User'),
    f.DeleteRule = 'set null',
    f.RelationshipName = 'Updated_by__r',
    f.Required = 0,
    f.IsUnique = 0,
    f.IsSystem = 0,
    f.IsNameField = 0,
    f.LastModifiedDate = CURRENT_TIMESTAMP
WHERE o.ApiName = 'Task'
  AND f.ApiName = 'Updated_by__c';

INSERT INTO _System_Field (
    Id,
    ObjectId,
    ApiName,
    Label,
    PhysicalName,
    Type,
    Required,
    IsUnique,
    IsSystem,
    IsNameField,
    ReferenceTo,
    DeleteRule,
    RelationshipName,
    CreatedDate,
    LastModifiedDate
)
SELECT
    CONCAT(o.Id, ':Updated_by__c'),
    o.Id,
    'Updated_by__c',
    'Updated by',
    'Updated_by__c',
    'reference',
    0,
    0,
    0,
    0,
    JSON_ARRAY('User'),
    'set null',
    'Updated_by__r',
    CURRENT_TIMESTAMP,
    CURRENT_TIMESTAMP
FROM _System_Object o
WHERE o.ApiName = 'Task'
  AND NOT EXISTS (
      SELECT 1
      FROM _System_Field f
      WHERE f.ObjectId = o.Id
        AND f.ApiName = 'Updated_by__c'
  );
```

**Idempotency**: The `UPDATE` normalizes any existing row to the desired final
state. The `INSERT ... NOT EXISTS` only creates the field row once.

### 3. Surface `Updated_by__c` on the default Task detail layout

```sql
-- Update the default Task detail layout stored in _System_PageLayout so that
-- the "System Information" section visibly includes Updated_by__c.
--
-- Logical target:
--   ObjectApiName = 'Task'
--   LayoutType = 'Detail'
--   default layout row (the same row currently exposed as `default_task`)
--
-- Logical JSON change:
--   append Updated_by__c to the "System Information" section's fields/layoutRows
--   without duplicating it if already present.
--
-- Equivalent backend-safe apply procedure:
--   1. Read the current Task detail layout JSON Config from _System_PageLayout.
--   2. Locate the "System Information" section.
--   3. If Updated_by__c is absent, append it as a field component in that section.
--   4. Write the updated Config back to the same row.
--
-- If this environment regenerates Task layouts from live field metadata rather
-- than a persisted _System_PageLayout row, re-save the Task layout through the
-- existing UI API layout save path after Change #2 so the generated detail
-- layout picks up Updated_by__c.
```

**Idempotency**: The layout update is append-if-missing. Re-running leaves the
same final layout JSON.

## Rollback

```sql
-- Remove Task.Updated_by__c metadata registration
DELETE f
FROM _System_Field f
JOIN _System_Object o ON o.Id = f.ObjectId
WHERE o.ApiName = 'Task'
  AND f.ApiName = 'Updated_by__c';

-- Remove Updated_by__c from the default Task detail layout Config if it was added
-- (reverse the logical append described above).
```

Rollback note: this spec intentionally does not drop the physical
`Task.Updated_by__c` column. Column drops are destructive and are disallowed by
the data-spec protocol. After rollback, the column becomes inert because the
field metadata and layout exposure are removed.
