๐Ÿ“ฆ EqualifyEverything / equalify-iris

๐Ÿ“„ CONTRIBUTING.md ยท 165 lines
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165# Contributing to Equalify Iris

Thanks for helping make document remediation more accessible! Iris is Open Source under
**AGPL-3.0** (see [LICENSE](LICENSE)) and maintained by [Equalify Inc.](https://equalify.app/),
the **University of Illinois Chicago**, and **California State University**.

By participating you agree to our [Code of Conduct](CODE_OF_CONDUCT.md).

## Ways to contribute

- **Report an accessibility barrier** โ€” in the app/demo or in the HTML Iris produces. Use the
  **Accessibility issue** template. These are our highest priority.
- **Report a bug / request a feature** โ€” use the matching issue template.
- **Improve a prompt** โ€” the agent files are in [`agents/`](agents/), and `agents/page.md`
  renders every page, so a PR that sharpens it changes the product. (The Reader and Copy Editor
  prompts are code, in `src/pipeline/review.ts`.) Iris also opens `New agent suggestion: <type>`
  issues when it meets content a specialist would handle better, and you're welcome to open one
  yourself โ€” but note that `agents/` is not a directory of content types: a file added for a type
  the whole-page pass already covers is never loaded โ€” the
  [design notes](docs/design-notes.md#the-pipelines-shape) say why, under "One agent per page, not
  one per content type". `chartDataAgent.md` is the shape that earns its place.
- **Code** โ€” bug fixes and improvements via pull request.

A well-written issue may get a pull request without you doing anything else. A scheduled workflow
ranks the open issues Sunโ€“Wed and opens one PR for the most pressing one it can finish well
([details](docs/ci.md#scheduled-issue-triage)) โ€” accessibility barriers rank first, and small
user-visible fixes reported against the demo rank well because they review cleanly. It never
touches an issue labelled `no-auto-pr`, never files a second PR for an issue it has already tried,
and stops entirely when nothing is eligible. If you'd rather own the fix yourself, say so on the
issue and add that label.

**You get the credit for it.** A PR from that workflow names you in its body and carries a
`Co-authored-by` trailer for your account on the commit, so the merged commit is attributed to you
as well as to the bot that typed it โ€” the report is the contribution. The trailer uses your
GitHub `users.noreply` address, never your real email.

**It also stays off any issue that already has an open PR โ€” including yours.** Open a PR for an
issue and the workflow leaves it alone; if every open issue has one, it opens nothing at all. Two
things claim an issue: **`Closes #<n>` in your PR body** (which also closes the issue on merge, so
this is the one to use) or an `issue-<n>` in your branch name. Merely mentioning `#<n>` in prose
does *not* claim it โ€” too many PRs reference issues they aren't fixing โ€” so if you are working on
something, say `Closes #<n>` and the robot will stay out of your way.

## Development

Requires **Node 24+** (runs TypeScript directly; uses built-in `node:sqlite`), **git**, and โ€”
for PDF uploads โ€” **poppler-utils** (`brew install poppler` / `apt-get install poppler-utils`).

```bash
npm install
cp .env.example .env            # a GitHub PAT (required) + a model provider key
cp config.example.yaml config.yaml
npm start                       # http://localhost:8080  (app at /, API under /v1)
```

Before opening a PR:

```bash
npm run typecheck               # tsc --noEmit
npm test                        # the unit suite (node --test; run it through npm, see below)
./test/e2e.sh                   # full API lifecycle against mock GitHub + mock model (needs jq)
```

Run the unit suite through `npm`, not as a bare `node --test`. The `npm test` script
registers a second reporter (`test/spec-with-signals.mjs`) that prints `signal` and
`exitCode` when a test file's *process* dies. Node's default reporter shows that as `โœ–
some.test.ts` and `'test failed'` โ€” identical to a failed assertion, with nothing on
stderr โ€” and the tests after the death simply never run, so the pass count reads clean
while being short. See #405.

The demo page must stay accessible โ€” it's audited with the project's own axe-core lint and
should report **0 violations**.

## Documentation

**Docs here are written in concise plain language. That is a requirement, not a preference.** It
covers `README.md`, everything under `docs/`, the comments in `config.example.yaml`, and the agent
prompts in `agents/` โ€” every one of them is read by someone deciding whether to trust Iris with a
document, and the prompts are read by a model as well.

What it asks for:

- **One idea per sentence.** If a sentence needs a second read, split it.
- **The claim first, the caveat after.** Never the reverse.
- **A number instead of an adjective.** "About 11ยข a page" beats "cost-effective". Anything you
  assert about behaviour should be checkable against code, a test, or a named benchmark round.
- **No jargon without a gloss on first use**, and no new term where a plain one exists.
- **No repetition.** A paragraph that restates the one above it gets deleted, not softened.
- **One job per document, and no restating another one.** A claim lives in the document whose job it
  is, and the others link to it โ€” that is what keeps this set DRY and cheap to correct, since a claim
  written twice is a claim that goes stale in one place.
  [README ยง Working on Iris](README.md#working-on-iris--including-if-you-are-an-ai-agent) says which
  document holds what.
- **Shorter over completer.** A page nobody finishes documents nothing.

Two things this is not. It is not a ban on detail โ€” an exact number, a file path, or a caveat that
saves a reader an hour all belong in. And it is not about formatting: there is no linter here, and
nothing checks heading style, line length or word choice.

**Who checks it:** the automated review, then a maintainer. On a PR touching those files the
reviewer quotes a sentence that breaks one of the rules above and names the rule, as a
[non-blocking note](#what-the-automated-review-will-say) โ€” at most three, the worst ones. It is the
only prose it comments on, and a note is not a merge gate. It does not judge whether a document
earns its length: that is the last rule above, and it stays with the maintainer.

A PR that only makes an existing doc plainer is welcome, with no code change attached.

## Pull requests

- Branch from `main`, keep PRs focused, and describe the change + how you tested it.
- **`main` is protected: every change lands as a merged pull request.** Direct pushes are
  refused for everyone โ€” maintainers included โ€” and force-pushes and branch deletion are
  blocked. **@bbertucc** merges; a review from them is requested automatically
  ([CODEOWNERS](.github/CODEOWNERS)).
- **No formal approval is required to merge**, so don't wait for a green review checkmark that
  never comes. The gate is a maintainer reading the PR, not a count of approvals โ€” GitHub won't
  let anyone approve their own work, and requiring approvals would block the maintainer's
  changes rather than yours.
- Match the surrounding code style (the codebase favors small, well-commented modules).
- New runtime dependencies should be justified โ€” Iris aims to stay portable and lightweight.
- AGPL-3.0: contributions are licensed under the same terms.

### What the automated review will say

Your PR gets a review from Claude in CI before a maintainer reads it
([details](docs/ci.md#automated-code-review)). Useful things to know:

- **It runs the checks itself** and quotes their real output, so a failing `typecheck` or `e2e`
  comes back as a blocking finding with the relevant lines.
- **It will not nit-pick style, formatting or naming.** There's no linter or formatter in this
  repo on purpose. It's also told not to suggest alternatives when your approach is correct, and
  not to raise pre-existing issues your PR doesn't touch. If it does one of those anyway, that's a
  bug in the prompt โ€” say so on the PR.
- **One exception: docs prose.** On a PR touching the files the [Documentation](#documentation)
  section covers, it checks prose against those rules โ€” quoting the sentence, naming the rule, three
  at most, always as a non-blocking note. The list of files lives in that section, not in this
  bullet, so this bullet cannot fall out of date with it. Not in scope even there: heading style,
  line length, Markdown layout, or wording it would have chosen differently.
- **`### Non-blocking notes` means "merge-ready".** A finding only blocks if something reaches it
  on input the code accepts today; real-but-unreachable findings are notes on an *approval*. You
  don't have to resolve them to merge, and you don't have to argue your way out of them.
- **Its verdict is advisory, and its check is not a merge gate.** A human merges. The check is
  deliberately not required on `main`, because it could not be a trustworthy gate: a PR touching
  only ignored paths never triggers it at all, and a fork PR's skipped job counts as satisfied
  with nothing having reviewed the code. If you think a blocking finding is wrong, reply on the
  PR โ€” the reviewer sees its earlier reviews on re-runs, but it is the maintainer you're actually
  talking to.
- **Fork PRs aren't reviewed automatically** (a fork PR gets no CI secrets). A maintainer
  dispatches the review manually; nothing is needed from you.
- **A PR touching `.github/workflows/**` gets a CI-security review first.** The workflows are part
  of the app, so expect questions about `permissions:`, secrets reaching PR-authored code, and
  `${{ }}` in `run:` blocks before anything about accessibility. Changing `code-review.yml` itself
  also works now, with one visible difference: the review posts as **github-actions[bot]** rather
  than claude[bot], and the PR gets a comment explaining why and asking for a human read as well.
- **`iris-auto/*` PRs aren't reviewed automatically either.** Those come from the scheduled triage
  workflow, and GitHub doesn't start workflow runs for events raised by `GITHUB_TOKEN`. Each one
  carries a comment saying so with the dispatch command. Nothing is needed from contributors.

## Architecture (orientation)

`src/pipeline` (extraction โ†’ assembly โ†’ review), `src/providers` (LLM provider abstraction),
`src/routes` (the `/v1` API), `src/auth` (the deployment's one GitHub identity, and the API gate),
`agents/` (the agent prompt files).
See [README.md](README.md) and [docs/API.md](docs/API.md).