# `semantic.ai_field_unmarked`

Code stores a value from a field marked as personal data, or `to_models: false`, in a field that doesn't carry the same marks.

Area 21, [AI](/docs/ai.md); not declinable; the next step is usually the agent's.

## Why Jadpo refuses it

A field's marks say what it holds: its personal-data category, from
`personal:` or its type, and `to_models: false` when no model may be
sent it. Jadpo checks every prompt against those marks. Once a marked
value is copied into a field without them, whatever reads that field
later, a prompt among them, would send it unchecked.

So a field that stores a marked value keeps its marks. That holds for a
value decided by a marked field too, such as one chosen under an `if` or
a `match` on it.

## What it looks like

The clinic copies a patient's notes, marked `health`, into a remark
field that has no mark.




domains/clinic/entities/patient.jadpo:

```jadpo
use clinic/types { ClinicalNote }

/// A patient of the clinic.
entity Patient {
    id: Uuid
    notes: ClinicalNote {
        personal: PersonalData.health
    }
    handover_remark: ClinicalNote?

    policy: {
        read: [Access.authenticated]
        update: [Access.authenticated]
    }

    person: true
}
```

domains/clinic/actions/remark_on_notes.jadpo:

```jadpo
use clinic/entities { Patient, PatientNotFound }

/// Copies a patient's notes into the remark the next clinician reads.
action remark_on_notes(patient_id: Patient.id) fails PatientNotFound -> Patient {
    var patient: Patient = attempt Patient.get(patient_id)
    return Patient.update(patient) {
        set: {
            handover_remark: patient.notes
        }
    }
}
```

```text
error: `Patient.notes` is stored in `Patient.handover_remark`, which isn't marked `health`
 --> domains/clinic/actions/remark_on_notes.jadpo:8:30
  |
8 |             handover_remark: patient.notes
  |                              ^^^^^^^^^^^^^ holds `Patient.notes`
  reason: `Patient.notes` is marked `personal: PersonalData.health`, a sensitive category. Whatever reads `Patient.handover_remark` later, a prompt among them, sends what it holds, so a field that stores a marked value keeps its marks: each category it is, in `personal:` or its type, and `to_models: false`.
  next: Give `handover_remark` `personal: PersonalData.health`, or leave `Patient.notes` out of it (agent)
  rule: semantic.ai_field_unmarked
  help: https://jadpo.dev/docs/errors/semantic.ai_field_unmarked

check failed: 1 error, 5 files
```

`(agent)` at the end of the `next:` line means your coding agent can make
the fix itself.

A model's answer carries what its prompt was built from, so a field
read off the answer counts as those fields. Here the summary of a
patient's notes is `health` too, and storing it in the unmarked remark
is refused in the same way.









domains/clinic/actions/keep_handover.jadpo:

```jadpo
use clinic/entities { Patient, PatientNotFound }
use clinic/types { ClinicalNote, HandoverSummary }
use clinic/failures { SummaryUnavailable }
use clinic/prompts { SummariseNotes }

/// Keeps a summary of a patient's notes as the next clinician's remark.
action keep_handover(patient_id: Patient.id) fails PatientNotFound, SummaryUnavailable -> Patient {
    var patient: Patient = attempt Patient.get(patient_id)
    var handover: HandoverSummary = attempt ai.generate(
        SummariseNotes {
            notes: patient.notes
        },
        as: HandoverSummary
    ) {
        refused: SummaryUnavailable
        malformed: SummaryUnavailable
        timed_out: SummaryUnavailable
        over_budget: SummaryUnavailable
        too_long: SummaryUnavailable
    }
    return Patient.update(patient) {
        set: {
            handover_remark: ClinicalNote(handover.summary)
        }
    }
}
```

```text
error: `Patient.notes` is stored in `Patient.handover_remark`, which isn't marked `health`
 --> domains/clinic/actions/keep_handover.jadpo:23:30
   |
23 |             handover_remark: ClinicalNote(handover.summary)
   |                              ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ holds `Patient.notes`
  reason: `Patient.notes` is marked `personal: PersonalData.health`, a sensitive category. Whatever reads `Patient.handover_remark` later, a prompt among them, sends what it holds, so a field that stores a marked value keeps its marks: each category it is, in `personal:` or its type, and `to_models: false`.
  next: Give `handover_remark` `personal: PersonalData.health`, or leave `Patient.notes` out of it (agent)
  rule: semantic.ai_field_unmarked
  help: https://jadpo.dev/docs/errors/semantic.ai_field_unmarked

check failed: 1 error, 8 files
```

## How to fix it

**If the field should hold the value**, give it the same marks. In
`domains/clinic/entities/patient.jadpo`:

```jadpo
    handover_remark: ClinicalNote? {
        personal: PersonalData.health
    }
```






`handover_remark` is now marked `health`, as the notes are, so a prompt
that reads it later is checked against
`ai: { personal_data: { allowed: } }` as the notes would be.

If the field shouldn't hold personal data, leave `Patient.notes`, and
anything worked out from it, out of what you store there.

## Related

- [Personal data sent to a model](/docs/safety-and-privacy.md#personal-data-sent-to-a-model)
- [Following personal data](/docs/following-personal-data.md)
- [Marking personal fields](/docs/marking-personal-fields.md)
- [`semantic.ai_personal_data_not_allowed`](/docs/errors/semantic.ai_personal_data_not_allowed.md)
