# HUMANS.md, version 0.2 (draft)

**HUMANS.md is a file a person writes so that AI agents know how to work with them: what they read, what they decide, and when to interrupt them.** AGENTS.md tells agents how to work on a codebase. HUMANS.md tells them how to work with a person.

Not to be confused with a list of a project's contributors, or with a guide for people who work with agents. Both exist under the same file name. This document describes neither ([§ 1](#1-not-to-be-confused-with)).

- Status: draft, not published. Date: 2026-10-13.
- License: [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/) for this document; [Apache-2.0](tools/LICENSE) for the reference tools. Using the format never requires a cmdAI product.
- Schemas: [`schema/humans.schema.json`](schema/humans.schema.json) (a profile), [`schema/humans-group.schema.json`](schema/humans-group.schema.json) (a merged group). Closed lists: [`VOCABULARY.md`](VOCABULARY.md). Examples: [`examples/`](examples/).
- Reference tools and conformance tests: `cd tools && npm install && npm test`. `node validate.mjs path/to/file.md` checks one file.

The key words MUST, MUST NOT, SHOULD, SHOULD NOT and MAY are to be read as in [RFC 2119](https://www.rfc-editor.org/rfc/rfc2119) and [RFC 8174](https://www.rfc-editor.org/rfc/rfc8174) when, and only when, they appear in capitals.

Terms used below:

- **Owner**: the person a profile describes. Only the owner writes it.
- **Own agent**: an agent working for the owner (their Claude Code, Codex, Cursor…).
- **Sender's agent**: an agent working for someone else that writes to, or for, the owner.
- **Consumer**: any program that reads a profile and hands it to an agent (a skill, an MCP server, a hook).
- **Directory**: the organization's list of people, synchronised with its identity provider, that maps a person to one profile.

## 1. Not to be confused with

| You found… | What it is | How to tell |
|---|---|---|
| A `HUMANS.md` that lists the people behind a project | A contributor list, in the spirit of [humans.txt](https://humanstxt.org) | No YAML frontmatter with `humans:` |
| A `HUMANS.md` that tells people how to collaborate with an AI assistant (e.g. [attogram/agents](https://github.com/attogram/agents)) | Guidance written for humans about agents. The opposite direction. | No YAML frontmatter with `humans:` |
| An output-style skill such as [i-have-adhd](https://github.com/ayghri/i-have-adhd) | One set of formatting rules for your own agent, named after a condition (its README says no diagnosis is needed). You change it by editing the skill. | It is a skill, not a profile. |

A HUMANS.md profile is recognised by its frontmatter: a YAML block with `humans: "0.2"`. A consumer MUST ignore a file named `HUMANS.md` that has no such frontmatter.

What sets a profile apart from an output-style skill: the owner declares it in closed fields, it covers decisions and interruptions as well as format, and any agent can read it, including the agents of the people who write to the owner.

## 2. Principles

1. **Preferences, never diagnoses.** A profile says how to write to the owner, never why. "TL;DR first, one decision per message" belongs in it; "ADHD" never does. Validators reject health terms ([§ 3.9](#39-health-guard)).
2. **Declared, never inferred.** Everything served is what the owner approved. Consumers infer nothing about a person from other data.
3. **Closed values.** Every field but `name`, `aliases` and `notes` takes a value from a fixed list. That is what makes a profile safe to hand to an agent written by someone else, mergeable for a group, and identical in every language.
4. **Free text stays local.** The owner writes in their own words; their own agent compiles those words into fields; the owner approves. Only the fields leave the machine ([§ 10](#10-compiling-free-text)).
5. **Fewer asks, never more rights.** A profile can reduce what agents ask the owner. It never grants a permission ([§ 4](#4-precedence)).

## 3. Files

The owner keeps two files:

| File | Holds | Read by |
|---|---|---|
| `~/.humans/me.md` | **the profile**: the fields, as a YAML frontmatter and nothing else | Every agent, through imports and consumers |
| `~/.humans/me.source.md` | **the source**: the owner's free text, in any language | The owner's own agent only, to compile the profile. It never leaves the machine. |

A profile file is UTF-8 and opens with the frontmatter:

- The first line is exactly `---`. The frontmatter ends at the next line that is exactly `---`. Nothing else may share those lines.
- The frontmatter is at most **6 KB**.
- `me.md` has nothing after the closing line. A profile kept in a repository (`humans/<id>.md`) MAY add a short Markdown body for colleagues, at most **4 KB**. A consumer MUST NOT serve that body to any agent.

The source file SHOULD use three headings so tools can add a line under the right one without a model: `## What I read`, `## What I decide`, `## When to interrupt me`. Tools MUST also accept `## Ce que je lis`, `## Ce que je décide`, `## Quand tu m'interromps`. HTML comments in the source (for example a provenance line `<!-- added-by: tool-name … -->`) are ignored by compilation.

### 3.1 YAML subset

The frontmatter is one YAML 1.2 document, read with the core schema. Validators MUST reject anchors, aliases, explicit tags (`!…`), directives (`%…`), document markers (`---`, `...`) inside the frontmatter, and duplicate keys. What is left is plain maps, lists, strings, integers and booleans.

All fields are optional unless marked required. An absent field means "no preference": the agent keeps its default.

### 3.2 Identity

| Field | Required | Value |
|---|---|---|
| `humans` | yes | `"0.2"`, quoted |
| `id` | yes | slug `[a-z0-9-]{2,64}`, unique within the organization. Equals the file name without `.md`, except in `me.md`. |
| `name` | yes | plain text ([§ 3.8](#38-plain-text)), ≤ 80 characters. For the directory only, never served ([§ 6](#6-what-a-consumer-serves)). |
| `aliases` | | ≤ 5 entries, plain text, ≤ 40 characters each. For the directory only, never served. |
| `identities` | | map with keys `email`, `slack` (`T…/U…`), `github`, `gitlab`, `entra` (object ID, a UUID), `google`. Claims, not proof ([§ 7](#7-resolving-a-name-to-a-profile)). |
| `languages` | | ISO 639-1 codes in order of preference, e.g. `[fr, en]` |
| `updated` | yes | `"YYYY-MM-DD"`, quoted, a real calendar date: the day the owner last approved the profile |
| `visibility` | | `org` (default), `public`, `private` (served to the owner only) |

### 3.3 `reading`: what I read

| Field | Values | Meaning |
|---|---|---|
| `lead_with` | `tldr`, `decision_needed`, `context`, `question` | What the first line holds |
| `length` | `minimal` (≤ 5 lines), `short` (≤ 1 screen), `standard`, `thorough` | Target length |
| `max_words` | integer 20–2000 | Hard cap, overrides `length` |
| `structure` | `bullets`, `prose`, `mixed` | Body layout |
| `visual` | ordered list from `table`, `diagram`, `chart`, `example`, `text` | Preferred forms, most wanted first. A list starting with `table` or `diagram` means "visual first". |
| `technical_depth` | `non_technical`, `balanced`, `expert` | Level of detail |
| `jargon` | `avoid`, `define`, `ok` | Acronyms and trade terms |
| `tone` | `direct`, `neutral`, `warm` | Register |
| `evidence` | `numbers_and_sources`, `key_numbers`, `light` | How much proof to show |
| `uncertainty` | `flag_explicitly`, `inline` | `flag_explicitly`: say what was checked and what was assumed |

### 3.4 `deciding`: what I decide

| Field | Values | Meaning |
|---|---|---|
| `reversible` | `decide_and_report`, `ask` | For a choice the agent is already allowed to make and that can be undone: make it and report, or ask first |
| `batch_questions` | `end_of_task`, `as_they_come` | When open questions reach the owner |
| `default_answer` | `always`, `when_obvious` | Whether each question carries the agent's proposed answer |
| `asks_per_message` | `one`, `few`, `any` | Requests per message |
| `format` | `recommendation_first`, `options_then_recommendation`, `options_only` | How a choice is laid out |
| `challenge` | `welcome_early`, `after_draft`, `on_request` | When to raise a disagreement |
| `out_of_scope_steps` | `omit_unless_blocking`, `list` | Whether to list follow-ups outside the task (merge, publish, deploy) at the end |

### 3.5 `interrupting`: when to interrupt me

| Field | Values | Meaning |
|---|---|---|
| `interrupt_for` | `blocking_only`, `blocking_or_risky`, `anything` | What justifies stopping to ask |
| `group_by` | `project`, `none` | How several pending messages are grouped |
| `when_focused` | `summary_later`, `urgent_only`, `as_usual` | Behaviour while the `focus` state is on |
| `channel_hint` | `async_written`, `sync_call`, `either` | Preferred channel for complex topics |

### 3.6 `context` and `avoid`

`context` says what the owner already knows and what they decide, so agents skip what is known and explain what is not. Every value comes from [`VOCABULARY.md`](VOCABULARY.md).

| Field | Value |
|---|---|
| `role` | one role from the vocabulary, e.g. `cto`, `product-manager` |
| `knows_well`, `knows_little` | ≤ 15 topics each, e.g. `kubernetes`, `cloud-costs` |
| `decides` | ≤ 10 decisions, e.g. `infra-budget`, `release-scope` |
| `context_updated` | `"YYYY-MM-DD"`. Served with `stale: true` after 183 days. |

A résumé or public profile MAY be used to propose values during compilation; it is never stored.

`avoid` is a list from: `walls_of_text`, `filler_intro`, `filler_outro`, `hedging`, `emoji`, `rhetorical_questions`, `multiple_asks`, `unexplained_acronyms`, `passive_voice`, `exclamation`, `long_preamble`, `nested_bullets`, `sarcasm`.

### 3.7 `states`

Temporary variants: `incident`, `low_bandwidth`, `focus`, `mobile`. Each holds a partial override of `reading`, `deciding` and `interrupting`, written with the bare field names (they are unique across the three blocks), e.g. `incident: { lead_with: decision_needed, length: minimal, asks_per_message: one }`. The file only describes the variants. Switching one on is a separate, short-lived event:

- **Who**: only the owner switches a state on, directly (a command, a tool call made by the owner) or through a status they set themselves (Slack status, do-not-disturb). A sender, a sender's agent or the owner's own agent MUST NOT switch a state on, and MUST NOT infer one from content.
- **How long**: every activation carries an expiry, at most **12 hours** after it starts. An activation without an expiry lasts **2 hours**. After expiry the consumer drops it. Activations are never stored as history.
- **Several at once**: the order is `incident`, then `low_bandwidth`, then `focus`, then `mobile`. For each field, the value from the first active state that sets it wins; fields no active state sets come from the profile.
- **Served as instructions**: a consumer serves the resulting field values, never the name of the state as a description of the person.
- **Inside permissions**: a state changes form and asks only. It MUST NOT widen what an agent may do ([§ 4](#4-precedence)).

### 3.8 Plain text

`name`, `aliases` and `notes` are the only free-text values. Each is one line of plain text. Validators MUST reject a value that contains:

- a control, format, private-use or unassigned character (Unicode categories Cc, Cf, Co, Cn), which covers U+0085, U+00AD, U+061C, the zero-width characters, the bidirectional controls and the tag characters; a line or paragraph separator; any space other than U+0020;
- a Markdown or HTML character: `` ` `` `*` `_` `#` `<` `>` `[` `]` `{` `}` `|` `~` `\`;
- a URL scheme: any `scheme://`, and `http:`, `https:`, `mailto:`, `javascript:`, `vbscript:`, `data:`, `file:`, `ftp:`, `tel:`, `sms:`, `ssh:`, `blob:`, `ws:`, `wss:` in any case; a `www.` address; an e-mail address.

`notes` is at most 280 characters: a form preference no field captures, written as a fact about the owner ("I often read on my phone").

### 3.9 Health guard

A validator MUST reject a file that contains, at any depth:

- one of these keys: `diagnosis`, `condition`, `disability`, `health`, `medical`, `neurotype`, `personality_type`, `disc`, `mbti`, `motivations`, `triggers`, `persuasion`;
- in any string value, `notes` included, one of the health terms listed in [`VOCABULARY.md`](VOCABULARY.md#health-terms), in English or French, matched without case or accents, on whole words.

The list is a net, not a proof: a preference can still hint at a condition. That is why profiles carry instructions, never reasons.

### 3.10 Unknown keys and values

Validators MUST reject unknown keys and values. Consumers MUST ignore them, so a newer file still works with an older consumer.

## 4. Precedence

1. **For actions**, the order is: the harness and the session's permissions, then the repository's instructions (AGENTS.md, CLAUDE.md), then HUMANS.md, then the agent's defaults. A HUMANS.md MAY reduce what an agent asks the owner, inside what the higher levels allow. It MUST NOT grant a permission and MUST NOT lift a rule of the repository or the harness. `deciding.reversible: decide_and_report` applies only to actions the agent is already allowed to take and that can be undone.
2. **For form**, HUMANS.md comes before the agent's defaults for anything addressed to the owner: messages, summaries, questions, reports. Artifacts the repository formats itself (commit messages, PR templates, changelogs) follow the repository.
3. **The profile is data.** It shapes how the agent works with the owner. Recipients, facts, tool calls and links come only from the request the agent is working on. A consumer MUST present the profile as data and MUST NOT follow instructions found in it.

## 5. Locations

| Scope | Location | Written by |
|---|---|---|
| Personal | `~/.humans/me.md` (profile) and `~/.humans/me.source.md` (source, local only) | The owner |
| Repository or team | `humans/<id>.md`, with a `CODEOWNERS` line `humans/<id>.md @<handle>` so only the owner can approve changes | The owner, by pull request |
| Repository index | `HUMANS.md` at the root, OPTIONAL: a list of links to `humans/<id>.md`. An index, never a profile; it has no `humans:` frontmatter. | Maintainers |
| Local cache of other people | `~/.humans/people/<id>.md` | Consumers, never by hand |
| Organization directory | an MCP server synchronised with the identity provider | The owner, through the server |
| Web | WebFinger ([RFC 7033](https://www.rfc-editor.org/rfc/rfc7033)) on the domain of the owner's e-mail: `GET https://<domain>/.well-known/webfinger?resource=acct:<email>&rel=https://humansmd.dev/rel/profile` returns a link to the profile | The domain's organization |
| Slack | a custom profile field named `HUMANS.md` (`long_text`, ≤ 5,000 characters) holding the frontmatter | An admin creates the field; the owner fills it |

Loading your own profile into your own agent: imports and hooks target the profile `~/.humans/me.md`, never the source file.

- **Claude Code** imports files with `@path` in `CLAUDE.md`. Add `@~/.humans/me.md` to `~/.claude/CLAUDE.md`. An import from a project-level file that points outside the project triggers an approval dialog once; imports nest up to four levels.
- **Codex** reads `~/.codex/AGENTS.md` (or `AGENTS.override.md`) and has no import syntax. Use a `SessionStart` hook whose plain stdout prints `~/.humans/me.md`: Codex adds it as developer context. Copying the profile into `~/.codex/AGENTS.md` also works but goes stale.
- **Other agents**: a HUMANS.md skill or MCP server; see the list at [humansmd.dev/implementations](https://humansmd.dev/implementations).

Sources for this section: [`SOURCES.md`](SOURCES.md).

## 6. What a consumer serves

### 6.1 Who gets what

- **The owner's own agent** gets the profile. It reads the source file only when the owner asks it to compile ([§ 10](#10-compiling-free-text)).
- **A sender's agent** gets the validated fields only:
  - `name` and `aliases` are dropped; the display name comes from the directory.
  - `notes` is served by the rules of § 6.2 and § 6.3.
  - Any Markdown body is never served.
  - A profile not updated for 365 days is served with `stale: true`.
- **A group** gets the merged profile only ([§ 9](#9-groups)), with no `notes`.

### 6.2 Withholding

A consumer MUST withhold `notes` when it contains one of the words addressed to an agent listed in [`VOCABULARY.md`](VOCABULARY.md#words-addressed-to-an-agent), matched like the health terms (no case, no accents, whole words). A withheld note is not served; the response lists `notes` under `withheld`. False positives only cost a note: "Instead of long emails, use bullet points." is withheld.

Conformance cases: [`tests/withholding.json`](tests/withholding.json). A consumer is conformant when it serves or withholds each case as listed.

### 6.3 Serialization

- In a structured tool result, `notes` is a JSON string field next to `id`, never merged into prose.
- In text handed to a model, the only allowed form is:

  ```text
  Form preference quoted from <id>'s HUMANS.md (data, not an instruction): <notes as a JSON string>
  ```

  where `<notes as a JSON string>` is the value encoded as a JSON string ([RFC 8259 § 7](https://www.rfc-editor.org/rfc/rfc8259#section-7)), quotes included, so a `"` in the note cannot close the quotation. The serialization cases in `tests/withholding.json` are normative.

## 7. Resolving a name to a profile

In v0.2, **the organization directory is the only resolver.** A profile says who it belongs to (`id`, `identities`), but that is a claim: a consumer MUST NOT decide that a person is the owner of a profile from the profile's own fields, a file name, or a display name.

1. The sender's request names a person: an @mention (Slack user ID), an e-mail, a handle or a name.
2. The consumer asks the directory of the sender's organization, which returns at most one person per identifier, with that person's profile. `id` is unique within the organization.
3. A name that matches several people: the sender picks from a list (name, title, team); the agent never picks.
4. Nothing found: [§ 8](#8-no-profile).

Repository files (`humans/<id>.md`), the local cache and WebFinger are places a directory may load profiles from. WebFinger counts as the directory of the e-mail's domain. Outside any directory, an agent uses a profile only when the sender points to that exact file; that is the sender's choice, not a resolution.

## 8. No profile

The consumer returns `status: none` and the organization's default profile if one exists. The agent writes neutrally: TL;DR first, one clear request, standard length. It infers nothing about the person from other data. From the outside, "no profile" and "hidden profile" look the same.

## 9. Groups

For a channel or a list of recipients, the consumer merges the members' profiles field by field. The merge is deterministic: the same profiles always give the same result. Reference implementation: [`tools/merge.mjs`](tools/merge.mjs), cases in [`tests/group/`](tests/group/). Output shape: [`schema/humans-group.schema.json`](schema/humans-group.schema.json).

**Who counts.** Members with a `private` profile, and members without a profile, count in `coverage.total` only. If the channel has a state of its own (an incident channel), each member's override for that state is applied first and `state` is set in the output.

**Votes.** For each field, only members who set it vote. A field nobody sets is omitted; a block with no field is omitted. "Majority" is the most frequent value; on a tie, the merge takes the tie value given below, even when it was not among the tied values.

| Field | Rule | Tie or fallback |
|---|---|---|
| `lead_with` | `tldr` if anyone sets it; else `decision_needed` if anyone sets it; else majority | `context` |
| `length` | the shortest (`minimal` < `short` < `standard` < `thorough`) | |
| `max_words` | the smallest | |
| `structure` | majority | `mixed` |
| `visual` | rank vote: each member gives 5, 4, 3… points down their list; top two kept | order `table`, `diagram`, `example`, `chart`, `text` |
| `technical_depth` | the lowest; if members differ, the highest goes to `appendix_depth` | |
| `jargon` | the strictest (`avoid` < `define` < `ok`) | |
| `tone` | majority | `neutral` |
| `evidence` | majority | `key_numbers` |
| `uncertainty` | `flag_explicitly` if anyone sets it | otherwise `inline` |
| `reversible` | `ask` if anyone sets it | otherwise `decide_and_report` |
| `batch_questions` | majority | `end_of_task` |
| `default_answer` | `always` if anyone sets it | otherwise `when_obvious` |
| `asks_per_message` | the strictest (`one` < `few` < `any`) | |
| `format` | majority | `options_then_recommendation` |
| `challenge` | majority | `after_draft` |
| `out_of_scope_steps` | majority | `omit_unless_blocking` |
| `interrupt_for` | the strictest (`blocking_only` < `blocking_or_risky` < `anything`) | |
| `group_by` | `project` if anyone sets it | otherwise `none` |
| `channel_hint` | majority | `either` |
| `when_focused` | omitted | |
| `languages` | languages every member lists, ordered by summed rank; if none is shared, the one most members list | ties by summed rank, then alphabetical |
| `context.knows_well` | topics every member knows well | vocabulary order |
| `context.knows_little` | topics any member knows little | vocabulary order |
| `context.role`, `context.decides` | omitted | |
| `avoid` | union | list order of § 3.6 |
| `states`, `notes`, identity fields | omitted | |

**Conflicts.** For every merged single-value field where members set different values, `conflicts` lists the field. With three or more profiles, it also lists the values and their counts; with fewer, values stay hidden so nobody can tell who asked for what. Agents use conflicts to offer a split: a short version in the channel, details in a thread.

## 10. Compiling free text

1. The owner writes in their own words, in any language, in `~/.humans/me.source.md`.
2. Their own agent, on their own machine, turns those words into fields. A local check flags health terms and proposes a preference instead ("short sentences, one idea per line"), without logging anything.
3. The agent shows a preview ("this is how agents will write to you") and the owner approves.
4. The agent writes the approved fields to `~/.humans/me.md`. Only that file leaves the machine.

## 11. Versioning

`humans: "0.2"` replaces 0.1. A consumer MAY read a 0.1 file: its `reading` block keeps its meaning, its `asks_per_message` and `challenge` move to `deciding`, its `decisions` becomes `deciding.format`, and its `tired` state maps to `low_bandwidth`. Field names in 0.2 are stable: tools write them into suggested changes, and a rename breaks those changes. The vocabularies grow only with a new version.

## 12. Security and privacy

- **Prompt injection** is the main risk: a profile is text written by someone else and read by an agent with tools. The defences are closed values, plain-text rules, withholding and fixed serialization for `notes`, never serving a body, and treating the profile as data ([§ 4](#4-precedence)).
- **Impersonation**: only the owner writes their profile (`CODEOWNERS` in a repository, authenticated writes in the directory), and only the directory resolves people ([§ 7](#7-resolving-a-name-to-a-profile)).
- **Health data**: the health guard catches the obvious cases. Keep reasons out, keep states short-lived, and let the owner see who read their profile and delete it in one step.
- **No evaluation**: a profile describes how to write to someone. Using it to assess, rank, assign work or monitor people is outside this specification and against its purpose.

## 13. Known limits

- **No proof of identity in v0.2.** A profile's `identities` are claims. v0.2 relies on the organization directory and on `CODEOWNERS`; outside a directory, nothing proves that a profile belongs to the person it names. Strong proof (a signed profile, or verification by the identity provider) is planned for **v0.3**.
- **Lists catch words, not intent.** The health guard and the withholding list miss paraphrases and other languages than English and French. They reduce risk; they do not remove it.
- **Closed vocabularies lag behind.** A role, topic or decision missing from `VOCABULARY.md` cannot be expressed until the next version.
