Activities / ACTIVITIES.md

Declaring activities: ACTIVITIES.md

A repository says which activities on the activity registry its code performs or enables in one file at its root. The registry is the list; this file is a project's own pick from it, with the reason for each.

The file

ACTIVITIES.md sits at a repository's root, beside SECURITY.md and AGENTS.md. Its YAML front matter is the record a validator and a lint read; the Markdown under it is for a person. The record lists every package the repository ships as a component, and for each one the activities its code performs or enables, the ones its maintainers rule out, and the counterparties it cannot run without.

--- version: 1 registry: https://lexlint.io/activities/v1 date: 2026-10-16 components: - purl: pkg:npm/@pennant/outreach shape: library aggregate: complete activities: - id: automated_outreach mentioned: performs enabled: by_default where: [src/send.ts, src/campaign.ts] note: Sends email and text messages the caller queues. - id: generates_content mentioned: enables enabled: requires_configuration where: [src/draft.ts] note: Drafts a message with a model when one is configured. not: - id: tracks_devices why: Sets no cookie and sends no telemetry. talks_to: - host: api.sms-gateway.example party: Example SMS gateway required: true why: Every text message leaves through it. --- # What this library does, by feature **Sending.** send() and campaign() deliver email and text messages to people the caller names. Outreach law applies in most jurisdictions.

The fields that carry the meaning

  • mentioned is performs when the shipped software does it when run as documented, and enables when a product that calls it does it. A library that sends messages performs outreach; a model client that a product uses to write text enables content generation.
  • enabled says what it takes: by_default, requires_configuration, requires_dependency or requires_environment.
  • not rules out an activity a reader might infer, with the reason. aggregate: complete with no activities is a declared clean record, which is different from no file at all.
  • talks_to names a counterparty. When the software cannot do its documented job without it (required: true), that party's jurisdiction joins the run of every product that uses the component, so the finding can say why that jurisdiction is there. A default the deployer can change is asked about, never added silently.
  • A role is never declared here. Who the operator is belongs to the product, in its own lexlint.yml: a payments library does not make a product a bank.

Each note, and each paragraph of the body, says what the feature does and the kind of legal responsibility it tends to attract, in a sentence: "this sends messages to people; outreach law applies in most jurisdictions". Never a list of statutes, which goes stale in a month, and never "compliant", "certified" or "safe": the validator refuses all three.

Assigned first, declared later

A project's activities are recorded in two phases. LexLint starts by assigning them itself: for each project it covers, it records in a central registry the activities the project's code performs or enables, read from the project's own code and documentation. That needs nothing from the maintainers, so it is where every project starts.

When a project publishes its own ACTIVITIES.md, the file overrides anything LexLint recorded for it, in full: what the maintainers declare, rule out and name as a counterparty is the record, even where LexLint read it differently. LexLint then archives its own record for the project, dated and marked as replaced by the file, and never uses it again. The people who wrote the code know what it does, and the file is how they say so.

The front matter's JSON Schema is at https://lexlint.io/schema/activities.schema.json, and the LexLint plugin ships the same file. The same record rides in any CycloneDX bill of materials through the lexlint property namespace: ACTIVITIES.md in CycloneDX.