@nurix/etna 0.19.73 → 0.19.75

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,5 +1,5 @@
1
1
  {
2
- "fingerprint": "96b7a7dc48a3",
2
+ "fingerprint": "a4e2f407bbc6",
3
3
  "harnesses": {
4
4
  "code": {
5
5
  "plugin": "code",
@@ -21,6 +21,10 @@ run at the start of every dispatch and again before you return:
21
21
  node <skill>/scripts/detector.mjs <session-folder> [--gate=manifest|move] [--json]
22
22
  ```
23
23
 
24
+ A real run always prints per-rule lines and a `clean`/`failing` summary (or a usage/error
25
+ message). **Empty output is not a clean run — it means the detector never executed**; treat it
26
+ as a failed gate and report it, never as a pass.
27
+
24
28
  ## The write protocol you enforce
25
29
 
26
30
  One writer per file. You write `state.md`, `thesis.md`, `forks.md`, `ledger.md`, `fragments/`,
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "harness": "code",
3
3
  "name": "code",
4
- "version": "3.9.4",
4
+ "version": "3.9.6",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
@@ -15,5 +15,5 @@
15
15
  "docs/memory/main.md",
16
16
  ".mcp.json"
17
17
  ],
18
- "digest": "df2fa5a29379"
18
+ "digest": "f1da48ec8895"
19
19
  }
@@ -21,6 +21,7 @@ None by default. Keep a comment only when it captures a non-obvious **WHY** —
21
21
 
22
22
  ### 1.4 workers.dev URLs
23
23
 
24
+ - **Don't expose on `workers.dev` by default** — it is a public, guessable, internet-facing domain (`<worker>.<account>.workers.dev`), indexed and reachable by anyone the moment the Worker deploys. Default to `"workers_dev": false`; when a deploy is requested and no custom domain or route exists yet, warn the user that enabling `workers.dev` makes the Worker public, and enable it only if they confirm they want that.
24
25
  - Set `"workers_dev": false` once a custom domain is wired up, so the custom domain is the only entry point.
25
26
  - Pair with a `routes` entry using `custom_domain: true` so Cloudflare manages the DNS record and cert.
26
27
 
@@ -181,6 +181,8 @@ the move to history requires a recorded passing uselessness test [BS-USELESS-01]
181
181
  - `move` — adds the post-apply gates (manifest applied, uselessness pass recorded, no links into
182
182
  scaffolding); the folder may not move while any fail.
183
183
 
184
- Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. Rules degrade
184
+ Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. A run always
185
+ prints output — per-rule lines plus a `clean`/`failing` summary (or a usage/error message);
186
+ **exit 0 with empty output means the detector never executed** and is a failure, never a pass. Rules degrade
185
187
  gracefully: no git context ⇒ BS-OPS-02 reports `skipped`, never blocks. Rule IDs are stable —
186
188
  consumers (the twin, the evals, the maintenance census) key on them.
@@ -3,7 +3,7 @@
3
3
  // Contract: references/session-schema.md. Rule IDs are stable; consumers key on them.
4
4
  // Usage: node detector.mjs <session-folder> [--gate=session|manifest|move] [--json]
5
5
 
6
- import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
6
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from 'node:fs';
7
7
  import { join, basename, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { execFileSync } from 'node:child_process';
@@ -455,6 +455,8 @@ function main() {
455
455
  process.exit(failed.length === 0 ? 0 : 1);
456
456
  }
457
457
 
458
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
458
+ // realpath both sides: argv[1] keeps the invoking symlink (e.g. a ~/.claude/skills link),
459
+ // while import.meta.url is the resolved file — a plain path compare silently skips main().
460
+ if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
459
461
  main();
460
462
  }
@@ -617,6 +617,8 @@ function main() {
617
617
  process.stdout.write(`${JSON.stringify(output, null, 2)}\n`);
618
618
  }
619
619
 
620
- if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
620
+ // realpath both sides: argv[1] keeps the invoking symlink (e.g. a ~/.claude/skills link),
621
+ // while import.meta.url is the resolved file — a plain path compare silently skips main().
622
+ if (process.argv[1] && fs.realpathSync(process.argv[1]) === fs.realpathSync(fileURLToPath(import.meta.url))) {
621
623
  main();
622
624
  }
@@ -86,9 +86,9 @@
86
86
  // (--space-base, --sidebar-width[-collapsed], --topbar-height, --side-panel-width[-wide],
87
87
  // --content-max-width; unmapped keys emit as --layout-<key>). The dark block carries the 36 dark roles.
88
88
 
89
- import { readFileSync, existsSync, statSync, readdirSync } from "node:fs";
89
+ import { readFileSync, existsSync, statSync, readdirSync, realpathSync } from "node:fs";
90
90
  import { join, resolve, basename, dirname, relative, sep } from "node:path";
91
- import { pathToFileURL } from "node:url";
91
+ import { fileURLToPath } from "node:url";
92
92
  import process from "node:process";
93
93
 
94
94
  // ---------------------------------------------------------------------------
@@ -1195,4 +1195,6 @@ function main() {
1195
1195
  process.exit(anyFail ? 1 : 0);
1196
1196
  }
1197
1197
 
1198
- if (process.argv[1] && import.meta.url === pathToFileURL(resolve(process.argv[1])).href) main();
1198
+ // realpath both sides: argv[1] keeps the invoking symlink (e.g. a ~/.claude/skills link),
1199
+ // while import.meta.url is the resolved file — a plain path compare silently skips main().
1200
+ if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) main();
@@ -21,6 +21,10 @@ run at the start of every dispatch and again before you return:
21
21
  node <skill>/scripts/detector.mjs <session-folder> [--gate=manifest|move] [--json]
22
22
  ```
23
23
 
24
+ A real run always prints per-rule lines and a `clean`/`failing` summary (or a usage/error
25
+ message). **Empty output is not a clean run — it means the detector never executed**; treat it
26
+ as a failed gate and report it, never as a pass.
27
+
24
28
  ## The write protocol you enforce
25
29
 
26
30
  One writer per file. You write `state.md`, `thesis.md`, `forks.md`, `ledger.md`, `fragments/`,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "harness": "delivery",
3
3
  "name": "delivery",
4
- "version": "0.2.11",
4
+ "version": "0.2.12",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
10
  "seeds": [],
11
- "digest": "ea0919582c83"
11
+ "digest": "599762913a4e"
12
12
  }
@@ -181,6 +181,8 @@ the move to history requires a recorded passing uselessness test [BS-USELESS-01]
181
181
  - `move` — adds the post-apply gates (manifest applied, uselessness pass recorded, no links into
182
182
  scaffolding); the folder may not move while any fail.
183
183
 
184
- Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. Rules degrade
184
+ Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. A run always
185
+ prints output — per-rule lines plus a `clean`/`failing` summary (or a usage/error message);
186
+ **exit 0 with empty output means the detector never executed** and is a failure, never a pass. Rules degrade
185
187
  gracefully: no git context ⇒ BS-OPS-02 reports `skipped`, never blocks. Rule IDs are stable —
186
188
  consumers (the twin, the evals, the maintenance census) key on them.
@@ -3,7 +3,7 @@
3
3
  // Contract: references/session-schema.md. Rule IDs are stable; consumers key on them.
4
4
  // Usage: node detector.mjs <session-folder> [--gate=session|manifest|move] [--json]
5
5
 
6
- import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
6
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from 'node:fs';
7
7
  import { join, basename, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { execFileSync } from 'node:child_process';
@@ -455,6 +455,8 @@ function main() {
455
455
  process.exit(failed.length === 0 ? 0 : 1);
456
456
  }
457
457
 
458
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
458
+ // realpath both sides: argv[1] keeps the invoking symlink (e.g. a ~/.claude/skills link),
459
+ // while import.meta.url is the resolved file — a plain path compare silently skips main().
460
+ if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
459
461
  main();
460
462
  }
@@ -21,6 +21,10 @@ run at the start of every dispatch and again before you return:
21
21
  node <skill>/scripts/detector.mjs <session-folder> [--gate=manifest|move] [--json]
22
22
  ```
23
23
 
24
+ A real run always prints per-rule lines and a `clean`/`failing` summary (or a usage/error
25
+ message). **Empty output is not a clean run — it means the detector never executed**; treat it
26
+ as a failed gate and report it, never as a pass.
27
+
24
28
  ## The write protocol you enforce
25
29
 
26
30
  One writer per file. You write `state.md`, `thesis.md`, `forks.md`, `ledger.md`, `fragments/`,
@@ -1,12 +1,12 @@
1
1
  {
2
2
  "harness": "etna",
3
3
  "name": "etna",
4
- "version": "1.4.10",
4
+ "version": "1.4.11",
5
5
  "files": [
6
6
  ".prettierignore",
7
7
  ".prettierrc.json",
8
8
  ".vscode/settings.json"
9
9
  ],
10
10
  "seeds": [],
11
- "digest": "a3ff16dda3c2"
11
+ "digest": "84ce5acb1a74"
12
12
  }
@@ -181,6 +181,8 @@ the move to history requires a recorded passing uselessness test [BS-USELESS-01]
181
181
  - `move` — adds the post-apply gates (manifest applied, uselessness pass recorded, no links into
182
182
  scaffolding); the folder may not move while any fail.
183
183
 
184
- Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. Rules degrade
184
+ Exit code 0 = clean, 1 = violations (listed with rule IDs), 2 = unusable input. A run always
185
+ prints output — per-rule lines plus a `clean`/`failing` summary (or a usage/error message);
186
+ **exit 0 with empty output means the detector never executed** and is a failure, never a pass. Rules degrade
185
187
  gracefully: no git context ⇒ BS-OPS-02 reports `skipped`, never blocks. Rule IDs are stable —
186
188
  consumers (the twin, the evals, the maintenance census) key on them.
@@ -3,7 +3,7 @@
3
3
  // Contract: references/session-schema.md. Rule IDs are stable; consumers key on them.
4
4
  // Usage: node detector.mjs <session-folder> [--gate=session|manifest|move] [--json]
5
5
 
6
- import { readFileSync, existsSync, readdirSync, statSync } from 'node:fs';
6
+ import { readFileSync, existsSync, readdirSync, statSync, realpathSync } from 'node:fs';
7
7
  import { join, basename, resolve } from 'node:path';
8
8
  import { fileURLToPath } from 'node:url';
9
9
  import { execFileSync } from 'node:child_process';
@@ -455,6 +455,8 @@ function main() {
455
455
  process.exit(failed.length === 0 ? 0 : 1);
456
456
  }
457
457
 
458
- if (process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
458
+ // realpath both sides: argv[1] keeps the invoking symlink (e.g. a ~/.claude/skills link),
459
+ // while import.meta.url is the resolved file — a plain path compare silently skips main().
460
+ if (process.argv[1] && realpathSync(process.argv[1]) === realpathSync(fileURLToPath(import.meta.url))) {
459
461
  main();
460
462
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@nurix/etna",
3
- "version": "0.19.73",
3
+ "version": "0.19.75",
4
4
  "description": "Bootstrap a project with a Nurix harness — copies the harness's CLAUDE.md, skills, agents, rules, and hooks directly into the project from a bundled payload (no marketplace fetch, no network).",
5
5
  "type": "module",
6
6
  "bin": {
@@ -42,5 +42,5 @@
42
42
  "agent",
43
43
  "plugin"
44
44
  ],
45
- "etnaFingerprint": "96b7a7dc48a3"
45
+ "etnaFingerprint": "a4e2f407bbc6"
46
46
  }