instar 1.3.1148 → 1.3.1149

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.
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
5
- "packageVersion": "1.3.1148",
5
+ "packageVersion": "1.3.1149",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "c4fb233de8c00b924323e7e9b167a5832162a35740c6445ccb7200c863ee9686",
2
+ "sha256": "1ed8f5bd6959a002ef362c9be5f88a382b0956d4992b16879350851ce3711ac9",
3
3
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
4
- "packageVersion": "1.3.1148"
4
+ "packageVersion": "1.3.1149"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
3
3
  "articleCount": 88,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1148"
5
+ "packageVersion": "1.3.1149"
6
6
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "instar",
3
- "version": "1.3.1148",
3
+ "version": "1.3.1149",
4
4
  "description": "Coherence infrastructure for self-evolving AI agents — on the Claude Code or Codex subscription you already have.",
5
5
  "type": "module",
6
6
  "main": "dist/index.js",
@@ -72,6 +72,49 @@ const FUNNEL_ALLOWLIST = new Set([
72
72
  // in the spec's Layer-1 "misses" row.)
73
73
  const HANDROLLED_GATE =
74
74
  /\?\?\s*(?:!{1,2}\s*|Boolean\s*\(\s*)?[A-Za-z_$][\w$.?]*(?:\.developmentAgent\b|\[\s*['"]developmentAgent['"]\s*\])/;
75
+
76
+ // ── 2026-08-14: the gate value does not have to be spelled at the `??`. ──
77
+ // The matcher above requires `.developmentAgent` LITERALLY after the `??`, so
78
+ // lifting it into a local const first walks past it while resolving the gate by
79
+ // hand exactly as before:
80
+ // const da = config.developmentAgent;
81
+ // return enabled ?? !!da; // exit 0 against the old matcher
82
+ // The header's declared limit ("cannot catch arbitrary aliases/wrapper helpers")
83
+ // was honest; this closes the LOCAL-CONST half of it, which is the shape a
84
+ // rename-defeat audit actually reproduced.
85
+ //
86
+ // Deliberately NOT closed, so it stays stated rather than implied: wrapper
87
+ // helpers (`isDevAgent(config)`), cross-module aliases, and any value that needs
88
+ // dataflow to resolve. Guessing at those would over-match, and this lint fails
89
+ // builds — flagging correct code is the more expensive error here.
90
+ //
91
+ // Scope-bounded on purpose: an alias binds only within its own file, so the map
92
+ // is rebuilt per file and never leaks between them.
93
+ const ALIAS_DECL =
94
+ /\b(?:const|let|var)\s+([A-Za-z_$][\w$]*)\s*(?::\s*[^=]+?)?=\s*[A-Za-z_$][\w$.?]*(?:\.developmentAgent\b|\[\s*['"]developmentAgent['"]\s*\])/;
95
+
96
+ /**
97
+ * Local identifiers bound to `<expr>.developmentAgent` in THIS file.
98
+ * `config.developmentAgentName` does not qualify — the `\b` keeps the boundary
99
+ * through the alias exactly as it holds at the direct callsite.
100
+ */
101
+ function collectDevAgentAliases(lines) {
102
+ const names = new Set();
103
+ for (const line of lines) {
104
+ const code = codeOnly(line);
105
+ if (code === null) continue;
106
+ const m = ALIAS_DECL.exec(code);
107
+ if (m) names.add(m[1]);
108
+ }
109
+ return names;
110
+ }
111
+
112
+ /** `?? [!! | Boolean(] <alias>` — the aliased spelling of the same hand-rolled gate. */
113
+ function aliasGateMatcher(names) {
114
+ if (names.size === 0) return null;
115
+ const alts = [...names].join('|');
116
+ return new RegExp(`\\?\\?\\s*(?:!{1,2}\\s*|Boolean\\s*\\(\\s*)?(?:${alts})\\b`);
117
+ }
75
118
  // A comment referencing the gate convention (for assertion B).
76
119
  const GATE_MARKER = /developmentAgent/i;
77
120
  const GATE_MARKER_QUALIFIER = /\b(dark|gate)\b/i;
@@ -171,10 +214,11 @@ for (const file of resolveTargets()) {
171
214
 
172
215
  // ── Assertion A: funnel ──
173
216
  if (!FUNNEL_ALLOWLIST.has(rel)) {
217
+ const aliasGate = aliasGateMatcher(collectDevAgentAliases(lines));
174
218
  lines.forEach((line, i) => {
175
219
  const code = codeOnly(line);
176
220
  if (code === null) return;
177
- if (HANDROLLED_GATE.test(code)) {
221
+ if (HANDROLLED_GATE.test(code) || (aliasGate !== null && aliasGate.test(code))) {
178
222
  violations.push({
179
223
  file: rel, line: i + 1, kind: 'A: hand-rolled gate',
180
224
  text: line.trim(),
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "$schema": "./builtin-manifest.schema.json",
3
3
  "schemaVersion": 1,
4
- "generatedAt": "2026-08-14T23:20:47.847Z",
5
- "instarVersion": "1.3.1148",
4
+ "generatedAt": "2026-08-15T00:08:31.593Z",
5
+ "instarVersion": "1.3.1149",
6
6
  "entryCount": 202,
7
7
  "entries": {
8
8
  "hook:session-start": {
@@ -2,7 +2,7 @@
2
2
  "schemaVersion": 1,
3
3
  "generatedFrom": "source-tree",
4
4
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
5
- "packageVersion": "1.3.1148",
5
+ "packageVersion": "1.3.1149",
6
6
  "guards": [
7
7
  {
8
8
  "ref": "docs/audits/phase-b/f10-triage.md",
@@ -1,5 +1,5 @@
1
1
  {
2
- "sha256": "c4fb233de8c00b924323e7e9b167a5832162a35740c6445ccb7200c863ee9686",
2
+ "sha256": "1ed8f5bd6959a002ef362c9be5f88a382b0956d4992b16879350851ce3711ac9",
3
3
  "registrySha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
4
- "packageVersion": "1.3.1148"
4
+ "packageVersion": "1.3.1149"
5
5
  }
@@ -2,5 +2,5 @@
2
2
  "sha256": "81b53363a440e832672618965540b3e507ae0d93adcc67ec2b93daf7933b3ab4",
3
3
  "articleCount": 88,
4
4
  "generatedFrom": "docs/STANDARDS-REGISTRY.md",
5
- "packageVersion": "1.3.1148"
5
+ "packageVersion": "1.3.1149"
6
6
  }
@@ -0,0 +1,48 @@
1
+ # Upgrade Guide — vNEXT
2
+
3
+ <!-- assembled-by: assemble-next-md -->
4
+ <!-- bump: patch -->
5
+
6
+ ## What Changed
7
+
8
+ `scripts/lint-dev-agent-dark-gate.js` assertion A bans hand-rolled dev-agent gate resolution outside
9
+ `resolveDevAgentGate`. The matcher required `.developmentAgent` **literally after the `??`**, so lifting the
10
+ value into a local const first walked past it while resolving the gate by hand exactly as before:
11
+
12
+ ```ts
13
+ const da = config.developmentAgent;
14
+ return enabled ?? !!da; // exit 0 against the shipped lint
15
+ ```
16
+
17
+ instar-codey reproduced this while auditing rename-defeatable checks and pre-scoped the remedy ("low FP for
18
+ simple local alias folding around `developmentAgent` feeding `??`"). That is the scope implemented: local
19
+ `const`/`let`/`var` aliases, collected per file, matched at `?? [!!|Boolean(] <alias>`.
20
+
21
+ The lint's header already declared this limit ("cannot catch arbitrary aliases/wrapper helpers"). It is
22
+ closed for the shape that actually occurs; the declaration was honest, not wrong.
23
+
24
+ Four controls keep it from failing correct builds, each with a test: only a `developmentAgent` binding
25
+ counts; matching is whole-word so `developmentAgentName` is untouched; an alias never used at a `??` is
26
+ legal (reading the flag is fine — hand-rolling the fallback is what is banned); and a commented-out
27
+ declaration binds nothing.
28
+
29
+ Still not caught, stated in the source: wrapper helpers, cross-module aliases, and anything needing
30
+ dataflow. Guessing at those would over-match a build-failing lint.
31
+
32
+ ## What to Tell Your User
33
+
34
+ None — internal change (no user-facing surface).
35
+
36
+ ## Summary of New Capabilities
37
+
38
+ None — internal change (no user-facing surface).
39
+
40
+ ## Evidence
41
+
42
+ - `tests/unit/lint-dev-agent-dark-gate.test.ts` — **31/31 green** (24 existing + 7 added).
43
+ - Negative control: tests written BEFORE the fix, run against the shipped lint — **3 of 31 fail** (const
44
+ alias, bracket-access alias, `Boolean(alias)`). The other 28 pass both ways.
45
+ - Reproduced by hand first with a positive control: direct form exits 1, aliased form exits 0.
46
+ - Real-tree verdict: `node scripts/lint-dev-agent-dark-gate.js` → exit 0, no new flags.
47
+ - Full `npm run lint` chain green.
48
+ - Side-effects: `upgrades/side-effects/dev-agent-gate-alias.md` · ELI16: `docs/specs/dev-agent-gate-alias.eli16.md`.
@@ -0,0 +1,104 @@
1
+ # Side-Effects Review — dev-agent gate check follows a local alias
2
+
3
+ **Version / slug:** `dev-agent-gate-alias`
4
+ **Date:** `2026-08-14`
5
+ **Author:** `echo`
6
+ **Second-pass reviewer:** `not required — Tier 1 (CI-only lint script; no runtime path). The rule is unchanged; the check now recognises one more spelling of the thing it already forbids.`
7
+
8
+ ## Summary of the change
9
+
10
+ `scripts/lint-dev-agent-dark-gate.js` assertion A bans hand-rolled dev-agent gate resolution — anything
11
+ resolving `enabled ?? !!<x>.developmentAgent` outside `resolveDevAgentGate`. The matcher required
12
+ `.developmentAgent` (or `['developmentAgent']`) **literally after the `??`**, so lifting the value into a
13
+ local const first walked past it while resolving the gate by hand exactly as before:
14
+
15
+ ```ts
16
+ const da = config.developmentAgent;
17
+ return enabled ?? !!da; // exit 0 against the shipped lint
18
+ ```
19
+
20
+ instar-codey reproduced this while auditing rename-defeatable checks and scoped the remedy: *"low for
21
+ simple local alias folding around `developmentAgent` feeding `??`."* That is the scope implemented — local
22
+ `const`/`let`/`var` aliases only, rebuilt per file.
23
+
24
+ The lint's header **already declared this limit** ("cannot catch arbitrary aliases/wrapper helpers"). Like
25
+ the journal-actuation ban earlier today, it is closed because the declared gap is cheap to close for the
26
+ shape that actually occurs, not because the declaration was dishonest.
27
+
28
+ ## Decision-point inventory
29
+
30
+ - `collectDevAgentAliases(lines)` — ADD — per-file identifiers bound to `<expr>.developmentAgent`.
31
+ - `aliasGateMatcher(names)` — ADD — `?? [!!|Boolean(] <alias>`; returns null when there are no aliases, so
32
+ a file without one is byte-identical to before.
33
+ - Assertion A predicate — WIDEN — `HANDROLLED_GATE || aliasGate`.
34
+ - Assertions B and C, the funnel allowlist, the comment-stripping (`codeOnly`), and the marker logic are
35
+ untouched.
36
+ - No runtime block/allow decision added or modified. CI-time only.
37
+
38
+ ## 1. Over-block
39
+
40
+ The failure that matters: this lint fails builds, so flagging correct code costs more than missing a case.
41
+ Four controls, each with a test:
42
+
43
+ - **An alias of something else** (`config.somethingElse`) is not flagged — only `developmentAgent` binds.
44
+ - **A look-alike name** (`config.developmentAgentName`) is not flagged: the `\b` holds through the alias
45
+ exactly as it holds at the direct callsite.
46
+ - **An alias never used at a `??`** is not flagged. *Reading* the flag is legal; resolving the GATE by hand
47
+ is what is banned, and that distinction is preserved.
48
+ - **A comment describing the aliased pattern** is not flagged — `codeOnly` already strips comments and the
49
+ alias collector runs on the same stripped lines, so a commented-out declaration binds nothing.
50
+
51
+ Scope is per-file by construction: aliases cannot leak between files.
52
+
53
+ Verified against the real tree: exit 0 — the widened check introduces **no new flags on existing code**.
54
+
55
+ ## 2. Under-block
56
+
57
+ Stated in the source rather than implied:
58
+
59
+ - **Wrapper helpers** (`isDevAgent(config)`) — still invisible.
60
+ - **Cross-module aliases** — an alias exported from another file is not followed.
61
+ - **Anything needing dataflow** to resolve.
62
+
63
+ Guessing at those would over-match. The header's original claim is narrowed, not erased: it now cannot
64
+ catch *arbitrary* aliases, having gained the local-const case.
65
+
66
+ ## 3. Level-of-abstraction fit
67
+
68
+ Same layer as the existing check — line-oriented regex over comment-stripped source, no AST, no type
69
+ information, no new dependency. The alias map is the minimum needed to answer "what value is at this
70
+ `??`?" without climbing to a parser.
71
+
72
+ ## 4. Signal vs authority compliance
73
+
74
+ Unchanged. A CI guard, not a runtime authority. It pushes callers toward `resolveDevAgentGate`; the funnel
75
+ allowlist still exempts the funnel itself.
76
+
77
+ ## 5. Interactions
78
+
79
+ - `npm run lint` chain — position unchanged; full chain green.
80
+ - Assertions B/C unaffected; their env-fixture tests pass untouched.
81
+ - No source module, route, config key, or state file touched.
82
+
83
+ ## 6. External surfaces
84
+
85
+ None. Developer tooling, not an agent capability; the Agent Awareness Standard does not apply.
86
+
87
+ ## 7. Rollback cost
88
+
89
+ `git revert` of one script plus the appended tests. No migration, no state, no deployed artifact.
90
+
91
+ ## Conclusion
92
+
93
+ Ship. One evasion closed at the scope a peer's audit recommended, four anti-over-block controls added, real
94
+ tree verified clean.
95
+
96
+ ## Evidence pointers
97
+
98
+ - `tests/unit/lint-dev-agent-dark-gate.test.ts` — **31/31 green** (24 existing + 7 added).
99
+ - Negative control: tests written BEFORE the fix and run against the shipped lint — **3 of 31 fail** (const
100
+ alias, bracket-access alias, `Boolean(alias)`); the four new controls and all 24 existing tests pass both
101
+ ways, which is what makes them controls.
102
+ - Reproduced by hand first, with a positive control: the direct form exits 1, the aliased form exits 0.
103
+ - Real-tree verdict: `node scripts/lint-dev-agent-dark-gate.js` → exit 0.
104
+ - Full `npm run lint` chain green.