Skip to content
Cockpit0.29.0
A snapshot: read at commit 707a04c7ce8 on 2026-10-18T00:06:01Z by majordomus 0.29.0. Nothing on this page is read again. See it live

project.a-derive-sees-what-the-commit-will@1

A derive sees what the commit will

What it is

URI
majordomus://rule/project.a-derive-sees-what-the-commit-will@1
Kind
rule
Identity
project.a-derive-sees-what-the-commit-will@1
Path
.ai/repo/rules/project/a-derive-sees-what-the-commit-will.v1.md
Provenance
declared
Media type
text/markdown
As a capability
rule.project.a-derive-sees-what-the-commit-will@1

Front matter

{
  "id": "project.a-derive-sees-what-the-commit-will",
  "version": 1,
  "kind": "rule",
  "title": "A derive sees what the commit will",
  "description": "scripts/derive refuses, before it waits for the lock or builds anything, when the tree holds an untracked file that is not ignored, because a derive projects only what git tracks and the commit that adds the file would be refused.",
  "statement": "Stage every new file before you derive, and ignore or remove what will never be committed. scripts/derive refuses an untracked, non-ignored file before it takes the machine's lock and names each one.",
  "status": "active",
  "class": "blocking",
  "depends_on": [
    "project.derived-files-regenerated@1",
    "project.one-derive-at-a-time@1"
  ],
  "tags": [
    "derived",
    "machine"
  ],
  "x-majordomus": {
    "tests": [
      "test/cases/1020_a_derive_sees_what_the_commit_will.sh"
    ]
  }
}

Content

---
id: project.a-derive-sees-what-the-commit-will
version: 1
kind: rule
title: A derive sees what the commit will
description: scripts/derive refuses, before it waits for the lock or builds anything, when the tree holds an untracked file that is not ignored, because a derive projects only what git tracks and the commit that adds the file would be refused.
statement: Stage every new file before you derive, and ignore or remove what will never be committed. scripts/derive refuses an untracked, non-ignored file before it takes the machine's lock and names each one.
status: active
class: blocking
depends_on: [project.derived-files-regenerated@1, project.one-derive-at-a-time@1]
tags: [derived, machine]

x-majordomus:
  tests: [test/cases/1020_a_derive_sees_what_the_commit_will.sh]
---

# Rationale

A derive projects what git tracks. `majordomus generate` enumerates through the index
(`--discovery vcs`, the layer's contract) and the site generator reads `git ls-files`. That
is right, because the committed artifacts must project the committed tree. But it means a
file the next commit adds is invisible to the derive that precedes the commit. The
projections leave the file out, and the `derived-current` gate refuses the commit after the
derive has cost its minutes.

On 2026-10-09 that cost the 0.19.0 release two extra derives on a machine at load 40. An
episode record and two generated pages were untracked when the derive ran. A
`majordomus generate` repaired the registry and moved an input of the site data, so a full
derive had to run again behind the queue. A memory note that already read "stage new files
before derive" did not prevent it. A note can be skipped; a refusal at the right moment
cannot.

# Required behaviour

- Before it waits for the lock or builds anything, `scripts/derive` lists
  `git ls-files --others --exclude-standard`. If anything is listed, it names every file,
  says to `git add` it or to ignore or remove it, and exits 10.
- What git ignores never refuses: `.gitignore`, `.git/info/exclude` and the global excludes.
  The release job keeps its downloaded artifacts in `dist/`, which `.gitignore` names for this
  reason.
- `MAJORDOMUS_DERIVE_ALLOW_UNTRACKED=1` lets the derive run with a warning that names how many
  files it cannot see. It is for a worker who knows the files will never be committed.
- `scripts/derive --preflight` runs this check alone. `scripts/derive --locked -- <command>`
  projects nothing and owes no preflight.
- The arguments are judged first, so a mistyped flag is a usage error (exit 2) and never a
  refusal of the tree.

# Failure behaviour

The refusal lands before the lock is taken, so a refused derive never holds the queue. It
costs the time `git ls-files` takes.

# Verification

`test/cases/1020_a_derive_sees_what_the_commit_will.sh` runs a copy of `scripts/derive` in a
disposable repository and proves each behaviour:

- an untracked page is named and refused with exit 10, by `--preflight` and by a full derive
  alike;
- a refused derive never builds and never takes the lock;
- the same file, once added, passes;
- files ignored by `.gitignore` and by `.git/info/exclude` never refuse;
- the override goes on with its warning;
- `--locked` owes no preflight.

Two mutations each fail the case: removing the preflight from the derive's path, and dropping
`--exclude-standard`.