instar 1.3.766 → 1.3.768
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.
- package/dist/core/PostUpdateMigrator.d.ts.map +1 -1
- package/dist/core/PostUpdateMigrator.js +17 -2
- package/dist/core/PostUpdateMigrator.js.map +1 -1
- package/dist/core/crossModelReviewer.d.ts +40 -0
- package/dist/core/crossModelReviewer.d.ts.map +1 -1
- package/dist/core/crossModelReviewer.js +46 -1
- package/dist/core/crossModelReviewer.js.map +1 -1
- package/dist/scaffold/templates.js +1 -1
- package/dist/server/routes.d.ts.map +1 -1
- package/dist/server/routes.js +19 -4
- package/dist/server/routes.js.map +1 -1
- package/package.json +1 -1
- package/skills/spec-converge/scripts/cross-model-review.mjs +6 -1
- package/src/data/builtin-manifest.json +63 -63
- package/src/scaffold/templates.ts +1 -1
- package/upgrades/1.3.767.md +57 -0
- package/upgrades/1.3.768.md +38 -0
- package/upgrades/side-effects/outbound-advisory-offswitch-config-shape.eli16.md +41 -0
- package/upgrades/side-effects/outbound-advisory-offswitch-config-shape.md +117 -0
- package/upgrades/side-effects/reviewer-door-rewiring-inc2.md +110 -0
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Upgrade Guide — vNEXT
|
|
2
|
+
|
|
3
|
+
<!-- assembled-by: assemble-next-md -->
|
|
4
|
+
<!-- bump: patch -->
|
|
5
|
+
|
|
6
|
+
## What Changed
|
|
7
|
+
|
|
8
|
+
Fixed the outbound-advisory off-switch, which was silently broken on every real install. The preflight
|
|
9
|
+
route read `messaging.outboundAdvisory.enabled`, but on a real install `messaging` is a JSON **array**
|
|
10
|
+
of adapter configs, so that path resolves `undefined` → the `true` default → the documented off-switch
|
|
11
|
+
(`messaging.outboundAdvisory.enabled: false`) had **no effect** (an operator could not disable the
|
|
12
|
+
advisory). This is the un-DISABLABLE sibling of the PR #1379 un-ENABLABLE bug. The off-switch and tune
|
|
13
|
+
knobs now read from a reachable **top-level `outboundAdvisory`** block (the legacy nested key is
|
|
14
|
+
honored as a back-compat fallback). Existing agents' docs are corrected via a CLAUDE.md migration.
|
|
15
|
+
|
|
16
|
+
## What to Tell Your User
|
|
17
|
+
|
|
18
|
+
Nothing proactive — this is an internal fix. If a user ever asks why turning off the automated-message
|
|
19
|
+
advisory did nothing, the answer is that the off-switch was written in a spot the program could not
|
|
20
|
+
read; it now lives at a reachable top-level setting and genuinely works. The advisory is inform-only
|
|
21
|
+
and unchanged otherwise; this only makes its off button work.
|
|
22
|
+
|
|
23
|
+
## Summary of New Capabilities
|
|
24
|
+
|
|
25
|
+
- The outbound advisory is now genuinely **disablable** via top-level `outboundAdvisory.enabled: false`.
|
|
26
|
+
- Its tuning knobs (escalation thresholds, the time-claim dev-gate) read from the same reachable
|
|
27
|
+
top-level block.
|
|
28
|
+
- Legacy object-shaped `messaging.outboundAdvisory` config keeps working (fallback).
|
|
29
|
+
- Existing agents' CLAUDE.md off-switch line is auto-corrected to the reachable key on update.
|
|
30
|
+
|
|
31
|
+
## Evidence
|
|
32
|
+
|
|
33
|
+
- `tests/unit/outbound-advisory-config-shape.test.ts` — real LiveConfig + a real array-shaped config:
|
|
34
|
+
the top-level off-switch disables the advisory (impossible before the fix), default-on when unset,
|
|
35
|
+
object-shape back-compat.
|
|
36
|
+
- Existing `outbound-advisory-routes` / `outbound-advisory` / `telegram-reply-advisory-script` tests
|
|
37
|
+
(58) stay green; `tsc` clean; `lint-no-unreachable-messaging-gate` clean.
|
|
38
|
+
- Sibling audit that surfaced it: `docs/investigations/messaging-config-unreachable-audit-2026-07-04.md`.
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
# ELI16 — the "off" button for the auto-message advisory didn't do anything
|
|
2
|
+
|
|
3
|
+
## The story
|
|
4
|
+
|
|
5
|
+
Instar has an "outbound advisory" — when a background job tries to send you a message, it first checks
|
|
6
|
+
the text for raw file paths, dev jargon, or localhost links and, if it finds any, holds the message so
|
|
7
|
+
the agent can fix it. It's on by default, and the docs told operators they could turn it off by
|
|
8
|
+
setting a switch: `messaging.outboundAdvisory.enabled: false`.
|
|
9
|
+
|
|
10
|
+
The problem: that switch never worked on a real setup. The config's "messaging" section is a **list**
|
|
11
|
+
of chat platforms, not a labeled box — so a switch addressed as if it were a box *inside* that list
|
|
12
|
+
can't be read. The program looked, found nothing, and used the default ("on"). So an operator who
|
|
13
|
+
tried to turn the advisory off would find it stubbornly still on, with no error explaining why.
|
|
14
|
+
|
|
15
|
+
This is the same shape of bug we fixed earlier for the promise follow-through feature (PR #1379). That
|
|
16
|
+
one was worse — a feature you could never turn *on*. This one is milder — a feature you can't turn
|
|
17
|
+
*off* — because it defaults to on, so it still works; you just can't disable it.
|
|
18
|
+
|
|
19
|
+
## What this change does
|
|
20
|
+
|
|
21
|
+
It moves the switch to a spot the program can actually read: a **top-level `outboundAdvisory`**
|
|
22
|
+
setting, right next to the other real settings instead of buried in the platform list. Setting
|
|
23
|
+
`outboundAdvisory.enabled: false` there now genuinely turns the advisory off. The old (broken) nested
|
|
24
|
+
key is still honored where it *can* be read (for setups that use a box-shaped config), so nothing that
|
|
25
|
+
worked before breaks. The docs are corrected to point at the new switch — both for new agents and, via
|
|
26
|
+
a small automatic update, for agents that already have the old wording in their notes.
|
|
27
|
+
|
|
28
|
+
## Why it's safe
|
|
29
|
+
|
|
30
|
+
- The advisory is inform-only — it never blocks your messages — so there's no risk of it wrongly
|
|
31
|
+
rejecting anything. The only change is that its off-button now works.
|
|
32
|
+
- Old configs keep working (the old key is a fallback), so it's backwards compatible.
|
|
33
|
+
- It's a plain code + docs change; if it were ever wrong, the fix is a simple revert with no leftover
|
|
34
|
+
data to clean up.
|
|
35
|
+
|
|
36
|
+
## How we know it works
|
|
37
|
+
|
|
38
|
+
A new test sets up the config the **real** way — "messaging" as a list — flips the new top-level
|
|
39
|
+
off-switch, and confirms the advisory reports itself disabled (which was impossible before the fix). It
|
|
40
|
+
also checks the default-on behavior when the switch isn't set, and that the old box-shaped config still
|
|
41
|
+
works. The 58 existing advisory tests all still pass.
|
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
# Side-Effects Review — outbound-advisory off-switch reachable on array-shaped messaging
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `outbound-advisory-offswitch-config-shape`
|
|
4
|
+
**Date:** `2026-07-04`
|
|
5
|
+
**Author:** `Echo`
|
|
6
|
+
**Second-pass reviewer:** `not required (Tier-1)`
|
|
7
|
+
|
|
8
|
+
## Summary of the change
|
|
9
|
+
|
|
10
|
+
The outbound-advisory preflight route read its off-switch (and tuning knobs) at
|
|
11
|
+
`messaging.outboundAdvisory.*`. On a real install `messaging` is a JSON **array** of adapter configs,
|
|
12
|
+
so that dot-path resolves `undefined` → the read's default. For the `enabled` read (default `true`)
|
|
13
|
+
that meant the **documented off-switch (`messaging.outboundAdvisory.enabled: false`) had no effect —
|
|
14
|
+
an operator could not disable the advisory** (the un-DISABLABLE sub-class of the PR #1379 bug). Fix:
|
|
15
|
+
read from the reachable **top-level `outboundAdvisory`** block (canonical), honoring the legacy
|
|
16
|
+
`messaging.outboundAdvisory` as a back-compat fallback. Applied to all four reads
|
|
17
|
+
(`enabled`, `ignoreEscalationThreshold`, `ignoreEscalationSlugThreshold`, `timeClaim.enabled` dev-gate),
|
|
18
|
+
the documented off-switch key in both templates, and an existing-agent CLAUDE.md doc migration.
|
|
19
|
+
Files: `src/server/routes.ts`, `src/core/PostUpdateMigrator.ts`, `src/scaffold/templates.ts`, + test.
|
|
20
|
+
|
|
21
|
+
## Decision-point inventory
|
|
22
|
+
|
|
23
|
+
- `POST /messaging/preflight` `enabled` gate (`routes.ts`) — **modify** — top-level-first resolution.
|
|
24
|
+
- `OutboundAdvisoryAudit` threshold reads (`routes.ts`) — **modify** — top-level-first.
|
|
25
|
+
- `timeClaim.enabled` dev-gate value (`routes.ts`) — **pass-through** — sourced top-level-first;
|
|
26
|
+
`undefined → live-on-dev` semantics unchanged.
|
|
27
|
+
- CLAUDE template off-switch text (both `PostUpdateMigrator` content + `scaffold/templates`) —
|
|
28
|
+
**modify** — documents the reachable top-level key.
|
|
29
|
+
- `PostUpdateMigrator` TIME_CLAIM anchor marker — **modify** — matched on the stable `- Off-switch: \``
|
|
30
|
+
prefix so it still anchors regardless of which key the line carries.
|
|
31
|
+
- New `migrateClaudeMd` swap — **add** — updates an existing agent's stale nested-key off-switch line.
|
|
32
|
+
|
|
33
|
+
## 1. Over-block / 2. Under-block
|
|
34
|
+
|
|
35
|
+
No block/allow surface change of consequence: the advisory is inform-only and never blocks a message.
|
|
36
|
+
The only behavior change is that the **off-switch now actually works** — an operator who sets
|
|
37
|
+
`outboundAdvisory.enabled: false` disables the advisory (previously impossible). Over-block N/A;
|
|
38
|
+
under-block N/A.
|
|
39
|
+
|
|
40
|
+
## 3. Level-of-abstraction fit
|
|
41
|
+
|
|
42
|
+
Right layer — same top-level-first config-read pattern as PR #1379, applied at the read sites. No new
|
|
43
|
+
authority, no new machinery.
|
|
44
|
+
|
|
45
|
+
## 4. Signal vs authority compliance
|
|
46
|
+
|
|
47
|
+
**Required reference:** [docs/signal-vs-authority.md](../../docs/signal-vs-authority.md)
|
|
48
|
+
|
|
49
|
+
- [x] No — no runtime block/allow surface changes; the advisory remains inform-only. This restores a
|
|
50
|
+
broken operator control (an off-switch), it does not add gating authority.
|
|
51
|
+
|
|
52
|
+
## 5. Interactions
|
|
53
|
+
|
|
54
|
+
- **Shadowing:** none — top-level-first read is a superset of prior behavior; on object-messaging the
|
|
55
|
+
legacy fallback resolves identically.
|
|
56
|
+
- **Double-fire / races / feedback loops:** none.
|
|
57
|
+
- **Migration marker:** the TIME_CLAIM anchor now matches the stable `- Off-switch: \`` prefix, so it
|
|
58
|
+
still finds the off-switch line whether CLAUDE.md carries the old nested or the new top-level key —
|
|
59
|
+
verified the substring is present in both.
|
|
60
|
+
|
|
61
|
+
## 6. External surfaces
|
|
62
|
+
|
|
63
|
+
- **Install base / agents:** existing agents get the corrected off-switch DOC via the new
|
|
64
|
+
`migrateClaudeMd` swap (content-sniff, idempotent) on their next update; new agents via the
|
|
65
|
+
templates. The CODE fix ships with the server. No config migration needed to keep working (top-level
|
|
66
|
+
is additive; legacy still honored).
|
|
67
|
+
- **Operator surface:** restores a documented operator control (the off-switch). No new UI/route.
|
|
68
|
+
- **Persistent state / external systems:** none.
|
|
69
|
+
|
|
70
|
+
## 6b. Operator-surface quality
|
|
71
|
+
|
|
72
|
+
No dashboard/approval/form surface — not applicable (the off-switch is a config edit, now at a
|
|
73
|
+
reachable key, documented for the operator).
|
|
74
|
+
|
|
75
|
+
## 7. Multi-machine posture (Cross-Machine Coherence)
|
|
76
|
+
|
|
77
|
+
**machine-local BY DESIGN** — config (`.instar/config.json`) is per-machine; the advisory runs
|
|
78
|
+
per-machine on the sending machine. This change only alters where in the local config the flag is
|
|
79
|
+
read. No cross-machine state, notice, durable state, or URL.
|
|
80
|
+
|
|
81
|
+
## 8. Rollback cost
|
|
82
|
+
|
|
83
|
+
Pure code + doc change — revert the reads + template text + the one migration block. No persistent
|
|
84
|
+
state; the top-level key is additive and the legacy fallback preserves prior behavior. Zero-cost
|
|
85
|
+
back-out.
|
|
86
|
+
|
|
87
|
+
## Conclusion
|
|
88
|
+
|
|
89
|
+
Restores the outbound-advisory off-switch that was silently broken on every real (array-`messaging`)
|
|
90
|
+
install — the un-DISABLABLE sibling of the PR #1379 un-ENABLABLE bug, found by the sibling audit
|
|
91
|
+
(`docs/investigations/messaging-config-unreachable-audit-2026-07-04.md`). Code + docs + existing-agent
|
|
92
|
+
migration + a real-LiveConfig array-shape test. Clear to ship.
|
|
93
|
+
|
|
94
|
+
## Second-pass review (if required)
|
|
95
|
+
|
|
96
|
+
**Reviewer:** not required (Tier-1)
|
|
97
|
+
|
|
98
|
+
## Evidence pointers
|
|
99
|
+
|
|
100
|
+
- `tests/unit/outbound-advisory-config-shape.test.ts` — real LiveConfig + real array-shaped config:
|
|
101
|
+
top-level off-switch disables (was impossible before the fix), default-on when unset, object-shape
|
|
102
|
+
back-compat.
|
|
103
|
+
- Existing `outbound-advisory-routes` / `outbound-advisory` / `telegram-reply-advisory-script` tests
|
|
104
|
+
(58) stay green; `tsc` clean; `lint-no-unreachable-messaging-gate` clean.
|
|
105
|
+
|
|
106
|
+
## Class-Closure Declaration (display-only mirror)
|
|
107
|
+
|
|
108
|
+
- **`defectClass`** — `config-unreachable-on-shape` (the PR #1379 class; this is its un-DISABLABLE
|
|
109
|
+
default-ON sub-class).
|
|
110
|
+
- **`closure`** — `gap` — the #1381 lint catches the default-OFF (un-enablable) sub-class; the
|
|
111
|
+
default-ON un-disablable sub-class (`.get('messaging.*.enabled', true)` with a documented off-switch)
|
|
112
|
+
is not reliably lint-detectable without false positives (documented in the #1381 side-effects
|
|
113
|
+
follow-ups). This fix ships the direct regression test + moves the config to the reachable
|
|
114
|
+
convention; the class-level guard for the un-disablable sub-class remains a tracked gap.
|
|
115
|
+
- **`guardEvidence`** — n/a for `closure: gap`.
|
|
116
|
+
- **`gap`** — tracked in `docs/investigations/messaging-config-unreachable-audit-2026-07-04.md`
|
|
117
|
+
(follow-up 2) + the #1381 side-effects artifact.
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
# Side-Effects Review — Reviewer-Door Rewiring inc2 (per-family review timeout knob)
|
|
2
|
+
|
|
3
|
+
**Version / slug:** `reviewer-door-rewiring-inc2`
|
|
4
|
+
**Date:** `2026-07-04`
|
|
5
|
+
**Author:** `echo (build hand)`
|
|
6
|
+
**Tier:** `1`
|
|
7
|
+
**Second-pass reviewer:** `not required (Tier-1: no default value changed, absent-config byte-identical, config-reversible, no durable state / external side-effects; PR is the review surface)`
|
|
8
|
+
|
|
9
|
+
## Summary of the change
|
|
10
|
+
|
|
11
|
+
inc2 of REVIEWER-DOOR-REWIRING adds the per-family reviewer-call timeout knob
|
|
12
|
+
(`specConverge.reviewers.timeoutMs`, §3.2 / §7 / D6). It resolves a per-family
|
|
13
|
+
timeout from the config knob and threads it through all three reviewer families'
|
|
14
|
+
invocations (codex-cli, gemini-cli, claude-code). The knob accepts EITHER a single
|
|
15
|
+
number (applies to all families) OR a `{ default, byFramework }` map (per-family
|
|
16
|
+
override with a default fallback). Absent config resolves to today's exact 120s
|
|
17
|
+
default (`REVIEW_TIMEOUT_MS`) for every family — fleet behavior byte-identical.
|
|
18
|
+
Every resolved value is clamped to [30s, 900s]. inc2 ONLY adds the knob: it changes
|
|
19
|
+
no family's default timeout value (the measure-first gemini 600s raise is inc3's
|
|
20
|
+
dogfood decision, not built here).
|
|
21
|
+
|
|
22
|
+
Files touched:
|
|
23
|
+
|
|
24
|
+
- `src/core/crossModelReviewer.ts` — new `resolveReviewerTimeoutMs(config, frameworkId)`
|
|
25
|
+
resolver + `REVIEWER_TIMEOUT_MIN_MS` / `REVIEWER_TIMEOUT_MAX_MS` clamp constants + the
|
|
26
|
+
`timeoutMs` field on the `ReviewerConfig.specConverge.reviewers` type; `runCrossModelReview`
|
|
27
|
+
resolves the per-family timeout from the knob (`args.timeoutMs ?? resolveReviewerTimeoutMs(...)`).
|
|
28
|
+
- `skills/spec-converge/scripts/cross-model-review.mjs` — the `--family` path resolves its
|
|
29
|
+
per-family timeout from the knob when no explicit `--timeout-ms` is passed.
|
|
30
|
+
- `tests/unit/crossModelReviewer-per-family-timeout.test.ts` — new unit + wiring test file (16 tests).
|
|
31
|
+
|
|
32
|
+
## Decision-point inventory
|
|
33
|
+
|
|
34
|
+
- `specConverge.reviewers.timeoutMs` config field — **add** — a per-family knob (number OR map). Absent-safe.
|
|
35
|
+
- `resolveReviewerTimeoutMs` — **add** — pure resolver (single-number / byFramework / default / absent) + clamp.
|
|
36
|
+
- `REVIEWER_TIMEOUT_MIN_MS` / `REVIEWER_TIMEOUT_MAX_MS` — **add** — exported clamp bounds (30s / 900s).
|
|
37
|
+
- `runCrossModelReview` timeout resolution — **modify** — resolves per detected `framework.id` (was a bare `?? REVIEW_TIMEOUT_MS`); an explicit caller value still wins.
|
|
38
|
+
- `cross-model-review.mjs --family` timeout resolution — **modify** — resolves per `familyEntry.id`; explicit `--timeout-ms` still wins.
|
|
39
|
+
- No block/allow gate, no HTTP route, no scheduler job, no watcher is introduced or modified.
|
|
40
|
+
- No default timeout VALUE is changed.
|
|
41
|
+
|
|
42
|
+
---
|
|
43
|
+
|
|
44
|
+
## 1. Over-block
|
|
45
|
+
|
|
46
|
+
**What legitimate inputs does this change reject that it shouldn't?**
|
|
47
|
+
|
|
48
|
+
Nothing is rejected. The knob is purely additive and read per-invocation. A garbage
|
|
49
|
+
value (NaN, a string, null, Infinity, a non-finite `default`/`byFramework`) falls back
|
|
50
|
+
to the 120s default rather than throwing or producing a NaN timeout — the resolver is
|
|
51
|
+
pure and never throws. A below-floor or above-ceiling value is clamped, not refused, so a
|
|
52
|
+
misconfiguration still yields a working (bounded) timeout rather than a failed review.
|
|
53
|
+
|
|
54
|
+
## 2. Under-block
|
|
55
|
+
|
|
56
|
+
**What bad inputs does this change now allow through that it shouldn't?**
|
|
57
|
+
|
|
58
|
+
The clamp bounds this precisely: a value below 30s cannot starve a reviewer into an
|
|
59
|
+
instant timeout, and a value above 900s cannot create a 10-round × huge-timeout tail.
|
|
60
|
+
The knob only sets a *time budget*; it grants no authority, changes no egress
|
|
61
|
+
destination, and cannot alter the cross-model flag (that is `crossFamily` semantics from
|
|
62
|
+
inc1, untouched here). An explicit `--timeout-ms` dev override still wins over the knob,
|
|
63
|
+
preserving existing test/dev affordances.
|
|
64
|
+
|
|
65
|
+
## 3. Blast radius
|
|
66
|
+
|
|
67
|
+
**If this change is wrong, what breaks and how widely?**
|
|
68
|
+
|
|
69
|
+
Bounded to the spec-converge external-reviewer call timeouts on a development agent. On
|
|
70
|
+
the fleet the knob is absent, so behavior is byte-identical to today (120s per family) —
|
|
71
|
+
a bug in the resolver could only surface where an operator has deliberately set the knob.
|
|
72
|
+
Worst case of a resolver bug is a wrong (but clamped, 30–900s) timeout on a reviewer call,
|
|
73
|
+
which degrades loudly as a timeout and never blocks convergence (Signal vs. Authority,
|
|
74
|
+
§6). No durable state, no external side-effect, no data migration.
|
|
75
|
+
|
|
76
|
+
## 4. Reversibility
|
|
77
|
+
|
|
78
|
+
**How is this rolled back?**
|
|
79
|
+
|
|
80
|
+
Delete the `specConverge.reviewers.timeoutMs` config value → the resolver returns the 120s
|
|
81
|
+
default for every family. Or revert this PR (constants + resolver + two call-site edits +
|
|
82
|
+
one test file). No persistence, no interface consumed downstream (the field is
|
|
83
|
+
disclosure-free config read per invocation).
|
|
84
|
+
|
|
85
|
+
## 5. Migration parity
|
|
86
|
+
|
|
87
|
+
New config field `specConverge.reviewers.timeoutMs` — absent-safe by construction (no
|
|
88
|
+
`migrateConfig` entry needed for fleet correctness; fleet-absence = today's behavior).
|
|
89
|
+
`.instar/config.json` is machine-local (no config replication in instar), so setting the
|
|
90
|
+
knob is a per-machine edit. No settings.json hooks, no CLAUDE.md template section (this is
|
|
91
|
+
instar-developing-agent tooling, not an end-user capability).
|
|
92
|
+
|
|
93
|
+
## 6. Framework generality
|
|
94
|
+
|
|
95
|
+
The knob is threaded through the framework-agnostic reviewer registry: the same resolver
|
|
96
|
+
is applied per `framework.id` for codex-cli, gemini-cli, and claude-code alike (and any
|
|
97
|
+
future family added to `SUPPORTED_REVIEWER_FRAMEWORKS`). It routes through the reviewer
|
|
98
|
+
abstraction rather than assuming any single framework; per-framework values are the whole
|
|
99
|
+
point (gemini's reasoning-burn needs more headroom than codex's ~18.5s latency).
|
|
100
|
+
|
|
101
|
+
## 7. Testing
|
|
102
|
+
|
|
103
|
+
Unit + wiring (16 tests, `tests/unit/crossModelReviewer-per-family-timeout.test.ts`):
|
|
104
|
+
single-number applies to all families; `byFramework` overrides per family with default
|
|
105
|
+
fallback; **absent config → exactly 120s for every family (byte-identical)**; clamp
|
|
106
|
+
(10s→30s, 2000s→900s, in-range passthrough, clamp within map/default); each family's
|
|
107
|
+
invocation actually receives its resolved timeout through the driver (codex + gemini via
|
|
108
|
+
forced detection, claude via the family entry) — per-family, not uniform; and an explicit
|
|
109
|
+
caller timeout wins over the knob. Existing crossModelReviewer unit + integration suites
|
|
110
|
+
re-run green (105 unit + 10 integration).
|