# `Lei.CommitSubstance`
[🔗](https://github.com/kitplummer/lowendinsight/blob/main/lib/lei/commit_substance.ex#L1)

Decides whether a commit carries information about a project's maintenance (#244).

Commit currency measures time since the last commit of any kind, which makes
it resettable by things that say nothing about whether anyone is paying
attention: a README typo, a license year bump, a merged Dependabot PR. A
repository with no human involvement for a year can report `low` on the
strength of automation alone -- and because that is true at any threshold,
tightening the levels would not reach it.

The same distinction already exists on the contributor axis.
`contributor_count` counts everyone who ever pushed; `functional_contributors`
counts those carrying a real share of the work. Contributors got their filter.
This is the one currency never had.

A commit is not substantive when either holds:

  * its author classifies as a bot, which `Lei.AgenticDetector` already
    recognises -- Dependabot, Renovate, github-actions, release-please
  * every path it touches is documentation or repository metadata

Note that AI co-authorship does **not** make a commit non-substantive. An
agent-assisted commit still represents a person deciding the project needed
changing; `agentic_classification` is the metric that speaks to that, and
conflating the two would answer a different question than this one asks.

# `documentation?`

```elixir
@spec documentation?(String.t()) :: boolean()
```

Whether a repository path is documentation or metadata rather than the work.

# `parse_log`

```elixir
@spec parse_log(String.t()) :: [map()]
```

Parses `git log --name-only` output written with the record format this
module expects, newest commit first.

Records are separated by \x1e and header fields by \x1f, because both are
bytes git will not emit inside a path, an address or a date. Splitting on a
printable delimiter would break on the first filename containing it.

`warning:` lines are dropped for the same reason `GitModule.git_log_split/2`
drops them: git writes them onto the same stream as the output.

# `substantive?`

```elixir
@spec substantive?(map()) :: boolean()
```

A single commit, as `%{author_name:, author_email:, files:}`.

An empty `files` list is deliberately non-substantive: a commit that changed
no path -- an empty commit, or a merge recorded with no diff of its own --
tells us nothing about whether the code is being maintained. `Enum.all?/2`
would return `true` for it either way, so this is stated rather than left to
fall out of a vacuous truth.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
