@arjunkhera/atlas 0.3.51 → 0.3.52

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "atlas",
3
- "version": "0.3.51",
3
+ "version": "0.3.52",
4
4
  "description": "Atlas: the delivery lifecycle, its crews and the Atlas tools, for any repository.",
5
5
  "author": {
6
6
  "name": "Arjun Khera"
package/door/cli.mjs CHANGED
@@ -140,6 +140,7 @@ const HELP = `atlas — the door into a repo's Atlas files
140
140
  On a GitHub plan with no rulesets, apply changes nothing and reports advisory mode.
141
141
  atlas gate ready --repo <owner/name> --pr <n> may this pull request merge: every required check green on its head
142
142
  atlas gate audit --repo <owner/name> [--since <date>] each merge since the date (default: one day) that came before its checks were green
143
+ atlas gate audit --repo <owner/name> --commit <sha> one commit on the main branch: red when no pull request merged it, or one merged it before its checks were green
143
144
 
144
145
  atlas judge seal <tasks.md> the fresh-agent test: check the judge's six candidates, print the seal and the three that run
145
146
  atlas judge brief <tasks.md> <id> print the exact words for one run: the task, then the report form
@@ -180,7 +181,7 @@ export const FLAGS = Object.freeze({
180
181
  view: { product: 'value', name: 'value', out: 'value' },
181
182
  tests: { root: 'optional', halves: 'value', evidence: 'value', 'guards-from': 'value', tests: 'value', 'dry-run': 'switch', run: 'value', own: 'value', base: 'value', head: 'value', list: 'value', side: 'value', old: 'value', new: 'value', covers: 'switch', findings: 'value', repo: 'value', max: 'value', bot: 'value', since: 'value', out: 'value', text: 'switch', json: 'switch', scenario: 'value', language: 'value' },
182
183
  upkeep: { root: 'optional', event: 'value', json: 'switch' },
183
- gate: { repo: 'value', root: 'optional', 'dry-run': 'switch', off: 'switch', on: 'switch', pr: 'value', since: 'value' },
184
+ gate: { repo: 'value', root: 'optional', 'dry-run': 'switch', off: 'switch', on: 'switch', pr: 'value', since: 'value', commit: 'value' },
184
185
  judge: { tasks: 'value', verdict: 'value', seal: 'value', runs: 'value', out: 'value', repo: 'value', budget: 'value', model: 'value' },
185
186
  install: { local: 'switch', from: 'value', 'skip-global': 'switch', yes: 'switch', cloud: 'switch' },
186
187
  upgrade: { version: 'optional', yes: 'switch' },
@@ -417,7 +418,7 @@ function upkeepVerb(chosen) {
417
418
  }
418
419
 
419
420
  function gateVerb(chosen) {
420
- return gateCommand({ what: chosen._[1], repo: chosen.repo, root: rootOf(chosen), dryRun: Boolean(chosen['dry-run']), off: Boolean(chosen.off), on: Boolean(chosen.on), pr: chosen.pr, since: chosen.since }, line);
421
+ return gateCommand({ what: chosen._[1], repo: chosen.repo, root: rootOf(chosen), dryRun: Boolean(chosen['dry-run']), off: Boolean(chosen.off), on: Boolean(chosen.on), pr: chosen.pr, since: chosen.since, commit: chosen.commit }, line);
421
422
  }
422
423
 
423
424
  function judgeVerb(chosen) {
package/door/lib/gate.mjs CHANGED
@@ -6,8 +6,8 @@
6
6
  // 2. "atlas release tags" on v* tags: no update, no delete.
7
7
  // The bypass list stays empty. --off and --on are the break glass.
8
8
  //
9
- // A private repo on a free GitHub plan gets no rulesets (owner, 24 October
10
- // 2026: "ignore pro and assume going with that check in place"). There the
9
+ // A private repo on a free GitHub plan gets no rulesets. The owner chose on 24
10
+ // October 2026 to stay on that plan (decision dd90d8e8). There the
11
11
  // gate is advisory: apply changes nothing and says so, `atlas gate ready`
12
12
  // says if one pull request may merge, and `atlas gate audit` names each merge
13
13
  // that came before its checks were green (design docs/design-docs/gate-advisory/).
@@ -211,7 +211,8 @@ export function readyCommand({ repo, pr }, line, gh = ghApi) {
211
211
 
212
212
  // `atlas gate audit --since <date>`: each pull request merged since the date
213
213
  // whose required checks were not all green at the moment of the merge.
214
- export function auditCommand({ repo, since }, line, gh = ghApi) {
214
+ export function auditCommand({ repo, since, commit }, line, gh = ghApi) {
215
+ if (commit !== undefined) return commitAudit({ repo, commit }, line, gh);
215
216
  const from = since && since !== true ? since : new Date(Date.now() - 864e5).toISOString().slice(0, 10);
216
217
  if (Number.isNaN(Date.parse(from))) throw new GateError(`--since must be a date, not "${from}".`);
217
218
  const closed = gh('GET', `repos/${repo}/pulls?state=closed&sort=updated&direction=desc&per_page=100`) ?? [];
@@ -239,12 +240,46 @@ export function auditCommand({ repo, since }, line, gh = ghApi) {
239
240
  return red ? 1 : 0;
240
241
  }
241
242
 
243
+ // `atlas gate audit --commit <sha>`: one commit that reached the main branch.
244
+ // The push job runs it on each push. A commit is good when a pull request
245
+ // merged it after that pull request's required checks were green. A commit
246
+ // that no pull request merged was pushed past the gate, so it is red.
247
+ function commitAudit({ repo, commit }, line, gh) {
248
+ if (!commit || commit === true || !/^[0-9a-f]{7,40}$/i.test(String(commit))) throw new GateError('--commit needs a commit SHA.');
249
+ // GitHub expands a short SHA, and names the first parent: the main branch
250
+ // just before this commit, whose atlas.yaml held the gate at that moment.
251
+ const found = gh('GET', `repos/${repo}/commits/${commit}`);
252
+ const sha = found?.sha;
253
+ if (!sha) throw new GateError(`there is no commit ${commit} in ${repo}.`);
254
+ const pull = (gh('GET', `repos/${repo}/commits/${sha}/pulls`) ?? []).find((each) => each.merged_at && each.merge_commit_sha === sha);
255
+ if (!pull) {
256
+ line(`- ✗ ${sha.slice(0, 7)} reached the main branch with no pull request: a push past the gate.`);
257
+ line('RED PUSH: look at it now; the main branch may hold an untested change.');
258
+ return 1;
259
+ }
260
+ const parent = found.parents?.[0]?.sha ?? pull.base.sha;
261
+ let checks;
262
+ try { checks = checksAt(gh, repo, parent); } catch (error) {
263
+ if (!(error instanceof GateError)) throw error;
264
+ line(`- atlas gate audit: ${sha.slice(0, 7)} is the merge of #${pull.number}; atlas.yaml before it listed no required_checks, so nothing to audit.`);
265
+ return 0;
266
+ }
267
+ const bad = checkStates(checks, checkRuns(gh, repo, pull.head.sha), pull.merged_at).filter((row) => row.state !== 'green');
268
+ if (bad.length) {
269
+ line(`- ✗ #${pull.number} merged ${pull.merged_at} before its checks were green: ${bad.map((row) => `${row.name} ${row.state}`).join(', ')}`);
270
+ line('RED MERGES: 1. Look at it now; the main branch may hold an untested change.');
271
+ return 1;
272
+ }
273
+ line(`- atlas gate audit: ${sha.slice(0, 7)} is the merge of #${pull.number}, after its ${checks.length} required check(s) were green.`);
274
+ return 0;
275
+ }
276
+
242
277
  // `atlas gate apply|ready|audit`. Returns the exit code.
243
- export function gateCommand({ what, repo, root, dryRun = false, off = false, on = false, pr, since }, line, gh = ghApi) {
278
+ export function gateCommand({ what, repo, root, dryRun = false, off = false, on = false, pr, since, commit }, line, gh = ghApi) {
244
279
  if (!['apply', 'ready', 'audit'].includes(what)) throw new GateError(`there is no gate verb "${what ?? ''}". Use apply, ready or audit.`);
245
280
  if (!repo || repo === true || !/^[A-Za-z0-9_.-]+\/[A-Za-z0-9_.-]+$/.test(repo)) throw new GateError(`atlas gate ${what} needs --repo <owner/name>.`);
246
281
  if (what === 'ready') return readyCommand({ repo, pr }, line, gh);
247
- if (what === 'audit') return auditCommand({ repo, since }, line, gh);
282
+ if (what === 'audit') return auditCommand({ repo, since, commit }, line, gh);
248
283
  if (off && on) throw new GateError('pick --off or --on, not both.');
249
284
  const mode = off ? 'off' : on ? 'on' : 'apply';
250
285
  const facts = mode === 'apply' ? gateFacts(root) : null;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@arjunkhera/atlas",
3
- "version": "0.3.51",
3
+ "version": "0.3.52",
4
4
  "description": "Atlas: the delivery lifecycle, its crews and the Atlas tools, as a Claude Code plugin for any repository.",
5
5
  "type": "module",
6
6
  "license": "UNLICENSED",
@@ -105,7 +105,7 @@ set. A test keeps the two lists equal.
105
105
  | `atlas design` | `folder` to learn where designs live; `check` before you show a design; `build` to make its page |
106
106
  | `atlas view` | `build --product <id>` makes the project view page of one product, when the owner asks for it or after an Atlas release changes it. Publish it with only the grant the command prints |
107
107
  | `atlas upkeep` | `check` before you ask for a merge: the docs index, dead doc links, and the tracker item a pull request names. The daily upkeep run calls it on the main branch |
108
- | `atlas gate` | `apply` once per repo, by the owner on his Mac, after the repo's `required_checks` exist. `--dry-run` first. `--off` and `--on` are the break glass. On a plan with no rulesets it reports advisory mode. `ready` before each merge. `audit` in the daily upkeep run |
108
+ | `atlas gate` | `apply` once per repo, by the owner on his Mac, after the repo's `required_checks` exist. `--dry-run` first. `--off` and `--on` are the break glass. On a plan with no rulesets it reports advisory mode. `ready` before each merge. `audit` in the daily upkeep run, and with `--commit` in the push job of a repo that has one |
109
109
  | `atlas judge` | In the fresh-agent test only, as the `atlas:fresh-agent-test` skill says. `seal` after the pick, `brief` to read a sealed ask, `run` for each sealed task, `check` after the grade |
110
110
 
111
111
  A person merges every file that `atlas tooling` writes.