@nathapp/nax 0.78.0 → 0.79.1

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.
@@ -31,11 +31,11 @@
31
31
  * the `acceptance` node last passed, and the repo-root `test` command does
32
32
  * not cover per-feature acceptance tests — so without this a fix could break
33
33
  * the contract the first gate proved and still ship.
34
- * - `commit_gate` re-enters `review_quality` when its fix touched non-test code
35
- * — the gate loop was previously the one editing loop whose output only ever
36
- * faced mechanical checks. A test-only fix skips the re-review by explicit
37
- * cost tradeoff; see `gateCommitRoute` for why that is a known hole. The skip
38
- * records `review-skipped`, so it is visible in the audit.
34
+ * - `commit_gate` re-enters `review_quality` whenever its fix committed the
35
+ * gate loop was otherwise the one editing loop whose output faced only
36
+ * mechanical checks, which a fix that degrades the tests it repairs will
37
+ * satisfy. A test-only fix used to skip the re-review as a cost tradeoff;
38
+ * #1510 closed that hole. See `gateCommitRoute`.
39
39
  * - `load_ctx` and `open_pr` both route to `escalate` rather than throwing on
40
40
  * their own failures. acpx has no error edge, so a throw ends the run with no
41
41
  * result file for the plugin to read or notify from — the one outcome that
@@ -103,14 +103,23 @@ export const _openPrDeps = {
103
103
  *
104
104
  * - `unchanged` — nothing committed; no new diff, so nothing to review.
105
105
  * - `tests-only` — every touched path matched the repo's test-file patterns.
106
- * Skipped by explicit choice: the re-review is the flow's most expensive node
107
- * and a gate fix is usually a mechanical test repair. **This is a real hole.**
108
- * The defect that motivated the re-entry (rs-stock `b6fb66dd`) was itself
109
- * test-only — 8 copy-pasted stubs across 3 test files — so this route would
110
- * not have caught it. Widen it here if test-quality regressions start
111
- * shipping — the audit's `review-skipped` rounds are the evidence.
112
106
  * - `changed` — production code was touched, or the paths could not be
113
107
  * classified at all. "Cannot classify" reviews rather than skips.
108
+ *
109
+ * `tests-only` and `changed` both re-enter `review_quality` (#1510). They used
110
+ * to diverge: `tests-only` skipped the re-review as a cost tradeoff, on the
111
+ * reasoning that a gate fix is usually a mechanical test repair. That was a
112
+ * real hole — the defect that motivated the re-entry was itself test-only, 8
113
+ * copy-pasted stubs across 3 test files, so the skip would not have caught the
114
+ * very thing it was built for. A gate fix can turn a red suite green by
115
+ * degrading the tests it repairs, and `quality_gates` is satisfied by exactly
116
+ * that; nothing else read that diff. The audit settled the cost side: across
117
+ * every finish recorded, exactly one `gate` round has ever fired, so the skip
118
+ * was saving a review that almost never runs.
119
+ *
120
+ * The classification is kept even though both routes now review. It is what a
121
+ * cheaper test-quality-scoped reviewer would key off if gate rounds ever become
122
+ * frequent enough for the full re-review to hurt.
114
123
  */
115
124
  async function gateCommitRoute(
116
125
  i: FinishInput,
@@ -509,7 +518,7 @@ export default defineFlow({
509
518
  from: "commit_gate",
510
519
  switch: {
511
520
  on: "$.route",
512
- cases: { changed: "review_quality", "tests-only": "quality_gates", unchanged: "quality_gates" },
521
+ cases: { changed: "review_quality", "tests-only": "review_quality", unchanged: "quality_gates" },
513
522
  },
514
523
  },
515
524
  // The narrative runs only once the PR exists. acpx has no error edge, so an
@@ -16,16 +16,15 @@ const REVIEWED_PHASES: FinishPhase[] = ["spec", "quality"];
16
16
  /**
17
17
  * What produced this round, given the successor the commit routed to.
18
18
  *
19
- * The `route` argument is why this is computed after the commit rather than
20
- * alongside it: `tests-only` is only known once the committed paths have been
21
- * classified, and it is the difference between "no reviewer exists for this
22
- * phase" and "a reviewer exists, was owed a look, and was skipped".
19
+ * `route` no longer changes the answer, and that is the point: since #1510
20
+ * every committed gate fix re-enters `review_quality`, so no route can skip an
21
+ * owed re-review. The parameter stays because the round is still keyed on the
22
+ * successor conceptually, and a future route that *does* bypass a reviewer
23
+ * would need to be reflected here rather than silently inheriting
24
+ * `no-reviewer`.
23
25
  */
24
- export function commitRoundOutcome(phase: FinishPhase, route: string): FinishRoundOutcome {
26
+ export function commitRoundOutcome(phase: FinishPhase, _route: string): FinishRoundOutcome {
25
27
  if (REVIEWED_PHASES.includes(phase)) return "fixed";
26
- // Only `gate` can skip an owed re-review; `acceptance` has no reviewer to
27
- // skip, so its `tests-only`-shaped routes (it has none today) stay honest.
28
- if (phase === "gate" && route === "tests-only") return "review-skipped";
29
28
  return "no-reviewer";
30
29
  }
31
30
 
@@ -58,6 +57,7 @@ export function buildCommitRound(i: CommitRoundInput): FinishRound {
58
57
  committed: i.committed,
59
58
  outcome: commitRoundOutcome(i.phase, i.route),
60
59
  findings: i.findings,
60
+ route: i.route,
61
61
  ...(i.failing ? { failing: i.failing } : {}),
62
62
  ...(i.committed && i.shaAfter ? { sha: i.shaAfter } : {}),
63
63
  };
@@ -80,16 +80,17 @@ export type FinishRoundOutcome =
80
80
  */
81
81
  | "no-reviewer"
82
82
  /**
83
- * A re-review was owed and deliberately skipped — today only a `gate` fix
84
- * that touched test files exclusively (`gateCommitRoute` → `tests-only`).
83
+ * A re-review was owed and deliberately skipped.
85
84
  *
86
- * Distinct from `no-reviewer`, which the same node writes when the fix *is*
87
- * routed on to `review_quality`. Without the distinction both wrote
88
- * `no-reviewer`, so the audit could not tell a gate fix that was re-reviewed
89
- * from one whose re-review was skipped by policy — the #1507 failure mode
90
- * surviving on the one path where the omission is intentional, and the path
91
- * where a reader most needs to know. Recording it is also what makes "how
92
- * often does this fire?" answerable before anyone decides to close the hole.
85
+ * **No longer emitted.** It described the `gate` `tests-only` route, which
86
+ * skipped `review_quality` as a cost tradeoff; #1510 closed that hole, so
87
+ * every committed gate fix is now re-reviewed and nothing writes this.
88
+ *
89
+ * Retained because the audit trail is read, not just written: a project that
90
+ * ran an earlier nax can hold rounds carrying this outcome, and dropping it
91
+ * from the union would make those unrenderable. Do not reuse the name for a
92
+ * new meaning — a reader hitting it in an old artifact must still be told
93
+ * what it meant when it was written.
93
94
  */
94
95
  | "review-skipped";
95
96
 
@@ -106,6 +107,19 @@ export interface FinishRound {
106
107
  findings: Finding[];
107
108
  /** Gate commands that were red this round (gate phase). */
108
109
  failing?: string[];
110
+ /**
111
+ * The successor this round's commit routed to — `changed` / `tests-only` /
112
+ * `unchanged` for `gate`, `changed` / `unchanged` elsewhere.
113
+ *
114
+ * Recorded because `outcome` stopped carrying it. Until #1510 a tests-only
115
+ * gate fix was the only round writing `review-skipped`, so the outcome
116
+ * doubled as the classification; now every committed gate fix is reviewed
117
+ * and writes `no-reviewer`, which would leave "what did this fix touch?"
118
+ * unanswerable from the trail. That question is the input to deciding
119
+ * whether the re-review ever needs a cheaper, test-scoped form, so it has to
120
+ * survive the round it was computed in.
121
+ */
122
+ route?: string;
109
123
  /**
110
124
  * `HEAD` SHA after this round's commit (set only when `committed`); absent
111
125
  * on no-op rounds so a reader can distinguish "no commit" from "record lost".
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nathapp/nax",
3
- "version": "0.78.0",
3
+ "version": "0.79.1",
4
4
  "description": "AI Coding Agent Orchestrator — loops until done",
5
5
  "type": "module",
6
6
  "bin": {
@@ -47,9 +47,14 @@
47
47
  "check:dispatch-context": "bash scripts/check-dispatch-context.sh",
48
48
  "check:naxconfig-cast": "bash scripts/check-no-silent-naxconfig-cast.sh",
49
49
  "check:runtime-cleanup": "bash scripts/check-runtime-cleanup.sh",
50
+ "check:scripts": "bash scripts/check-scripts.sh",
50
51
  "check:adapter-no-config-import": "bash scripts/check-adapter-no-config-import.sh",
52
+ "check:test-typecheck": "bun run scripts/check-test-typecheck.ts",
53
+ "check:test-typecheck:update": "bun run scripts/check-test-typecheck.ts --update-baseline",
54
+ "check:test-as-unknown-as": "bun run scripts/check-test-as-unknown-as.ts",
55
+ "check:test-as-unknown-as:update": "bun run scripts/check-test-as-unknown-as.ts --update-baseline",
51
56
  "check:gate-reachability": "bun run scripts/check-gate-reachability.ts",
52
- "check:all": "bun run lint && bun run check:test-mocks && bun run check:process-cwd && bun run check:no-adapter-wrap && bun run check:dispatch-context && bun run check:naxconfig-cast && bun run check:runtime-cleanup && bun run check:adapter-no-config-import && bun run check:gate-reachability",
57
+ "check:all": "bun run lint && bun run check:test-mocks && bun run check:process-cwd && bun run check:no-adapter-wrap && bun run check:scripts && bun run check:dispatch-context && bun run check:naxconfig-cast && bun run check:runtime-cleanup && bun run check:adapter-no-config-import && bun run check:test-typecheck && bun run check:test-as-unknown-as && bun run check:gate-reachability",
53
58
  "prepublishOnly": "bun run build",
54
59
  "test:full": "FULL=1 NAX_PRECHECK=1 bun test test/ --timeout=60000"
55
60
  },