@nurix/etna 0.20.36 → 0.20.37
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/data/catalog/skills/nustack/SKILL.md +2 -2
- package/data/catalog/skills/nustack/references/cli-design.md +35 -35
- package/data/catalog/skills/nustack/references/monorepo-master-list.md +6 -6
- package/data/catalog/skills/nustack/references/monorepo-setup.md +13 -13
- package/data/catalog/skills/nustack/references/publishing.md +40 -39
- package/data/harness-index.json +1 -1
- package/data/harnesses/code/harness.json +1 -1
- package/data/harnesses/code/skills/boundary/SKILL.md +33 -22
- package/data/harnesses/code/skills/boundary/references/index.md +2 -4
- package/data/harnesses/code/skills/boundary/references/mechanisms.md +3 -10
- package/data/harnesses/code/skills/boundary/references/packs/frontend.md +17 -17
- package/data/harnesses/code/skills/boundary/references/packs/generic-tail.md +1 -1
- package/data/harnesses/code/skills/brainstorm/README.md +11 -13
- package/data/harnesses/code/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/code/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/code/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/code/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/code/skills/design/SKILL.md +31 -35
- package/data/harnesses/code/skills/design/data/iconography.md +2 -2
- package/data/harnesses/code/skills/design/references/compose.md +14 -16
- package/data/harnesses/code/skills/design/references/foundations.md +5 -5
- package/data/harnesses/code/skills/design/references/intake.md +6 -6
- package/data/harnesses/code/skills/design/references/object-pass.md +1 -1
- package/data/harnesses/code/skills/design/references/ship-gate.md +5 -5
- package/data/harnesses/code/skills/design/references/ux-patterns/Readme.md +4 -4
- package/data/harnesses/code/skills/design/references/ux-patterns/ai-layer.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/container-choice.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/dashboards.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/modes.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/object-model.md +2 -2
- package/data/harnesses/code/skills/design/references/ux-patterns/promotion-demotion.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/settings.md +1 -1
- package/data/harnesses/code/skills/design/references/ux-patterns/standard-pages.md +1 -1
- package/data/harnesses/code/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/code/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/code/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/code/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/code/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/code/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/code/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/code/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/code/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/code/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/code/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/code/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/code/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/code/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/code/skills/issue-bridge/SKILL.md +51 -16
- package/data/harnesses/code/skills/issue-bridge/references/trackers/github.md +5 -5
- package/data/harnesses/code/skills/issue-bridge/references/trackers/jira.md +11 -8
- package/data/harnesses/code/skills/issue-bridge/references/trackers/linear.md +16 -11
- package/data/harnesses/code/skills/nustack-graph/SKILL.md +38 -29
- package/data/harnesses/code/skills/polygraph/references/dispatch.md +31 -18
- package/data/harnesses/code/skills/polygraph/references/rows.md +11 -6
- package/data/harnesses/code/skills/schema-design/SKILL.md +39 -41
- package/data/harnesses/code/skills/test/SKILL.md +38 -42
- package/data/harnesses/code/skills/testing/SKILL.md +83 -73
- package/data/harnesses/code/skills/workflow-mapper/SKILL.md +156 -193
- package/data/harnesses/code/skills/workflow-mapper/references/patterns.md +15 -15
- package/data/harnesses/delivery/harness.json +1 -1
- package/data/harnesses/delivery/skills/brainstorm/README.md +11 -13
- package/data/harnesses/delivery/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/delivery/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/delivery/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/delivery/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/delivery/skills/cost-report/SKILL.md +10 -10
- package/data/harnesses/delivery/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/delivery/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/delivery/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/delivery/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/delivery/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/delivery/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/delivery/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/delivery/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/delivery/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/delivery/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/delivery/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/delivery/skills/escalate/SKILL.md +11 -11
- package/data/harnesses/delivery/skills/prompt-eval/SKILL.md +8 -8
- package/data/harnesses/delivery/skills/prompt-library/SKILL.md +30 -9
- package/data/harnesses/delivery/skills/route/SKILL.md +8 -8
- package/data/harnesses/etna/harness.json +1 -1
- package/data/harnesses/etna/skills/boundary/SKILL.md +33 -22
- package/data/harnesses/etna/skills/boundary/references/index.md +2 -4
- package/data/harnesses/etna/skills/boundary/references/mechanisms.md +3 -10
- package/data/harnesses/etna/skills/boundary/references/packs/frontend.md +17 -17
- package/data/harnesses/etna/skills/boundary/references/packs/generic-tail.md +1 -1
- package/data/harnesses/etna/skills/brainstorm/README.md +11 -13
- package/data/harnesses/etna/skills/brainstorm/SKILL.md +150 -132
- package/data/harnesses/etna/skills/brainstorm/references/doctrine.md +48 -51
- package/data/harnesses/etna/skills/brainstorm/references/harvest.md +51 -56
- package/data/harnesses/etna/skills/brainstorm/references/session-schema.md +48 -42
- package/data/harnesses/etna/skills/documentation/SKILL.md +5 -5
- package/data/harnesses/etna/skills/documentation/references/examples.md +22 -22
- package/data/harnesses/etna/skills/documentation/references/frontmatter.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/instruction-style.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/source-comments.md +3 -3
- package/data/harnesses/etna/skills/documentation/references/style-guide.md +5 -5
- package/data/harnesses/etna/skills/documentation/references/untemplated.md +8 -8
- package/data/harnesses/etna/skills/documentation/scripts/doc-lint.mjs +3 -2
- package/data/harnesses/etna/skills/documentation/templates/Readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/acceptance-criteria.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/api-reference.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/architecture-decision.md +3 -7
- package/data/harnesses/etna/skills/documentation/templates/behavioral-checklist.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/changelog-entry.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/changelog-index.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/docs-readme.md +3 -3
- package/data/harnesses/etna/skills/documentation/templates/doctrine.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/domain-model.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/environment-registry.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/feature.md +3 -3
- package/data/harnesses/etna/skills/documentation/templates/handover.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/implementation-map.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/migration-runbook.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/non-goals.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/operations-procedure.md +2 -2
- package/data/harnesses/etna/skills/documentation/templates/package.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/protected-files.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/root-readme.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/security-overview.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/system-overview.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/system.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/testing-strategy.md +1 -1
- package/data/harnesses/etna/skills/documentation/templates/ux-behavior.md +3 -5
- package/data/harnesses/etna/skills/workflow-mapper/SKILL.md +156 -193
- package/data/harnesses/etna/skills/workflow-mapper/references/patterns.md +15 -15
- package/data/harnesses/hr/harness.json +1 -1
- package/data/harnesses/hr/skills/evaluator/SKILL.md +71 -73
- package/data/harnesses/hr/skills/evaluator/criteria/code-quality.md +28 -47
- package/data/harnesses/hr/skills/evaluator/criteria/deployment-readiness.md +33 -43
- package/data/harnesses/hr/skills/evaluator/criteria/maintainability.md +34 -46
- package/data/harnesses/hr/skills/evaluator/criteria/mechanical-ambition.md +13 -13
- package/data/harnesses/hr/skills/evaluator/criteria/problem-substantiveness.md +7 -7
- package/data/harnesses/hr/skills/evaluator/criteria/testing.md +36 -41
- package/data/harnesses/hr/skills/evaluator/criteria/ux-mechanics.md +25 -11
- package/data/harnesses/hr/skills/evaluator/criteria/value-articulation.md +7 -7
- package/data/harnesses/hr/skills/evaluator/preflight.md +27 -46
- package/data/harnesses/hr/skills/interview-eval/SKILL.md +37 -42
- package/data/harnesses/hr/skills/interview-eval/rubric.md +21 -23
- package/package.json +2 -2
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: nustack
|
|
3
|
-
description: "
|
|
3
|
+
description: "Use when scaffolding a pnpm workspace, publishing an @nurix/* package to npm, building a distributable @nurix CLI (npx self-update, managed blocks), or debugging an npm publish."
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# NuStack
|
|
7
7
|
|
|
8
|
-
The `@nurix/*` packaging and tooling skill.
|
|
8
|
+
The `@nurix/*` packaging and tooling skill. Workspace doctrine that loads every session — package naming, monorepo boundaries, consuming — is the [`pnpm.md`](../../rules/pnpm.md) rule; this skill holds the on-demand material, one area per reference. Read the reference whose trigger matches the task before acting.
|
|
9
9
|
|
|
10
10
|
| Reference | Load when |
|
|
11
11
|
| --- | --- |
|
|
@@ -2,30 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
Load when: building or changing a distributable `@nurix/*` command-line tool.
|
|
4
4
|
|
|
5
|
-
Conventions for
|
|
5
|
+
Conventions for a distributable `@nurix/*` command-line tool. The two reference CLIs are **`@nurix/etna`** (`packages/cli/` in the etna repo — the harness installer, plain ESM JS) and **`@nurix/nustack`** (`~/dev/apollo/packages/cli/` — the service-bootstrap CLI, TypeScript → `dist/`). Where they diverge, both variants are valid.
|
|
6
6
|
|
|
7
7
|
## 1. Packaging
|
|
8
8
|
|
|
9
|
-
The base `package.json` fields — scope, `publishConfig.access: "restricted"`, `files`, build hooks — are [`publishing.md`](publishing.md) §2. CLI
|
|
9
|
+
The base `package.json` fields — scope, `publishConfig.access: "restricted"`, `files`, build hooks — are [`publishing.md`](publishing.md) §2. A CLI adds:
|
|
10
10
|
|
|
11
11
|
- **ESM only** — `"type": "module"`.
|
|
12
|
-
- **`bin` maps the command to its entry** — `{ "etna": "bin/etna.js" }` / `{ "nustack": "dist/index.js" }
|
|
13
|
-
- **`files` ships only the runnable artifact** — `["bin/", "
|
|
14
|
-
- **`engines.node` ≥ 22** for new
|
|
15
|
-
- **One version source** — read the running version from `package.json` at startup;
|
|
16
|
-
- **Minimal deps
|
|
12
|
+
- **`bin` maps the command to its entry** — `{ "etna": "bin/etna.js" }` / `{ "nustack": "dist/index.js" }`; the entry carries a `#!/usr/bin/env node` shebang.
|
|
13
|
+
- **`files` ships only the runnable artifact** — `["bin/", "lib/", "data/"]` (etna) or `["dist"]` (nustack). **Never** `src/`, tests, or the repo.
|
|
14
|
+
- **`engines.node` ≥ 22** for a new CLI, the workspace Node baseline.
|
|
15
|
+
- **One version source** — read the running version from `package.json` at startup; never hardcode it elsewhere.
|
|
16
|
+
- **Minimal deps** — essentially one runtime dep (`@clack/prompts`), because the dependency surface installs on every `npx` run; nustack adds `commander` and `ajv`.
|
|
17
17
|
|
|
18
18
|
## 2. Distribution: `npx`-first, install into the project, never globally
|
|
19
19
|
|
|
20
|
-
- The canonical entry is **`npx @nurix/<tool>`** — no global install
|
|
21
|
-
- **The CLI writes
|
|
22
|
-
- **Idempotent refresh.** Re-running
|
|
20
|
+
- The canonical entry is **`npx @nurix/<tool>`** — no global install, no marketplace, no plugin enablement. The tool does its job in the current directory.
|
|
21
|
+
- **The CLI writes only inside the folder it was invoked in.** `npx @nurix/etna` in a project touches only that project tree — **never** `$HOME`, `~/.claude`, or any shared or user-level location, even where a tool conventionally installs globally (`~/.claude/agents/`): a write outside the target folder clobbers state the user never pointed the CLI at.
|
|
22
|
+
- **Idempotent refresh.** Re-running re-pulls the latest and force-overwrites the files the CLI owns, leaving everything the user authored untouched — a re-run is **always** safe.
|
|
23
23
|
|
|
24
24
|
## 3. Always run the latest — self-update by re-exec
|
|
25
25
|
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
**The mechanism** (etna's `selfUpdateOrReexec()`, run before anything else): on startup, ask the registry for the latest version; if this copy is behind, re-exec the latest and hand off.
|
|
26
|
+
A bare `npx @nurix/<tool>` keeps running an outdated unpacked copy long after a newer version publishes — npm's packument cache resolves an old `latest`, and npx reuses the matching install — so the CLI updates itself rather than relying on users to type `@latest`. Before anything else, ask the registry for the latest version; if this copy is behind, re-exec the latest and hand off (etna's `selfUpdateOrReexec()`):
|
|
29
27
|
|
|
30
28
|
```js
|
|
31
29
|
function selfUpdateOrReexec() {
|
|
@@ -44,43 +42,45 @@ function selfUpdateOrReexec() {
|
|
|
44
42
|
|
|
45
43
|
Required properties:
|
|
46
44
|
|
|
47
|
-
- **`--prefer-online`** on both the `npm view` and the `npx` re-exec — defeats the metadata cache so npx fetches the fresh tarball.
|
|
48
|
-
- **Loop guard** — set `<TOOL>_SELFUPDATED=1` in the child's env so the re-exec'd process
|
|
49
|
-
- **Opt-out** — `<TOOL>_NO_SELFUPDATE=1` skips the
|
|
50
|
-
- **Degrade gracefully** — *any* failure (offline, unauthed, `npm`/`npx` missing, timeout) falls through to running the current copy
|
|
51
|
-
- **Reuse existing npm auth** —
|
|
45
|
+
- **`--prefer-online`** on both the `npm view` and the `npx` re-exec — it defeats the metadata cache, so npx fetches the fresh tarball.
|
|
46
|
+
- **Loop guard** — set `<TOOL>_SELFUPDATED=1` in the child's env so the re-exec'd process never updates again.
|
|
47
|
+
- **Opt-out** — `<TOOL>_NO_SELFUPDATE=1` skips the check: CI, offline, an intentionally pinned build, and testing a local build.
|
|
48
|
+
- **Degrade gracefully** — *any* failure (offline, unauthed, `npm`/`npx` missing, timeout) falls through to running the current copy; the update is a convenience, **never** a gate.
|
|
49
|
+
- **Reuse existing npm auth** — `npm view` rides the caller's credentials, the same auth that installed the restricted package; **never** prompt for login.
|
|
52
50
|
|
|
53
51
|
## 4. Editing the user's files: own a delimited block, idempotently
|
|
54
52
|
|
|
55
|
-
When the CLI
|
|
53
|
+
When the CLI modifies a file it doesn't own (`.env`, `settings.json`, `.gitignore`), it **owns only a delimited region** and rewrites that region in place — **never** the whole file.
|
|
56
54
|
|
|
57
55
|
- nustack wraps its `.env` writes in a managed block:
|
|
58
|
-
`# >>> nustack:managed — do not edit by hand; the nustack CLI rewrites this block >>>` … `# <<< nustack:managed <<<`. Reads
|
|
59
|
-
- etna merges its hooks into `.claude/settings.json` and migrates
|
|
60
|
-
- Rewrites are **idempotent** (re-
|
|
61
|
-
-
|
|
56
|
+
`# >>> nustack:managed — do not edit by hand; the nustack CLI rewrites this block >>>` … `# <<< nustack:managed <<<`. Reads, merges, and removals happen *inside* the markers; everything outside belongs to the user.
|
|
57
|
+
- etna merges its hooks into `.claude/settings.json` and migrates or retires only its own entries.
|
|
58
|
+
- Rewrites are **idempotent** (a re-run converges to the same block) and **reversible** (logout or uninstall removes exactly the managed region).
|
|
59
|
+
- A block holding secrets **must** sit in a file `.gitignore` covers.
|
|
62
60
|
|
|
63
61
|
## 5. Interactive when it can, flag-driven when it can't
|
|
64
62
|
|
|
65
|
-
- Use **`@clack/prompts`** for the interactive
|
|
66
|
-
-
|
|
67
|
-
- A bare invocation in a TTY may present a menu; the same invocation headless must error, not hang.
|
|
63
|
+
- Use **`@clack/prompts`** for the interactive (TTY) path: intro/outro, spinners, notes.
|
|
64
|
+
- **Outside a TTY**, require explicit flags and fail loudly with the correct syntax rather than guessing — etna requires `--name=<harness>`, and an unsupported flag or argument form errors with the current syntax instead of silently misfiring.
|
|
65
|
+
- A bare invocation in a TTY may present a menu; the same invocation headless **must** error, not hang.
|
|
68
66
|
|
|
69
67
|
## 6. Detect the package manager from the lockfile
|
|
70
68
|
|
|
71
|
-
When the CLI installs packages or runs scripts
|
|
69
|
+
When the CLI installs packages or runs scripts for the user, detect the manager from the lockfile — `pnpm-lock.yaml` → pnpm, `yarn.lock` → yarn, else npm (nustack's `detectPackageManager()`); **never** hardcode one.
|
|
72
70
|
|
|
73
71
|
## 7. Publishing is CI-owned
|
|
74
72
|
|
|
75
|
-
Publish from CI on push to `dev`/`main`, path-filtered to the CLI package — never `npm publish` by hand for a routine change. The pipeline mechanics — workflow template, tags, skip directives, `registry-url`, monorepo `working-directory` — are [`publishing.md`](publishing.md) §3–5. CLI
|
|
73
|
+
Publish from CI on push to `dev`/`main`, path-filtered to the CLI package — **never** `npm publish` by hand for a routine change. The pipeline mechanics — workflow template, tags, skip directives, `registry-url`, monorepo `working-directory` — are [`publishing.md`](publishing.md) §3–5. A CLI adds:
|
|
74
|
+
|
|
75
|
+
| Version model | How it bumps |
|
|
76
|
+
| --- | --- |
|
|
77
|
+
| **Fingerprint auto-bump** (etna) | The build stamps a content hash (`fingerprint`) of the shipped payload; CI bumps the **patch** when it changes, publishes, and tags. **Never** hand-bump for a content change; hand-edit the version only for a deliberate minor/major |
|
|
78
|
+
| **Manual bump input** (nustack) | A `workflow_dispatch` `bump` input (`patch`/`minor`/`major`) drives `npm version` |
|
|
76
79
|
|
|
77
|
-
-
|
|
78
|
-
- **Fingerprint auto-bump (etna):** the build stamps a content hash (`fingerprint`) of the shipped payload; CI bumps the **patch** automatically when the fingerprint changed, publishes, and tags. Never hand-bump for a content change — let the fingerprint drive it; hand-edit the version only for a deliberate minor/major.
|
|
79
|
-
- **Manual bump input (nustack):** a `workflow_dispatch` `bump` input (`patch`/`minor`/`major`) drives `npm version`.
|
|
80
|
-
- **`npm version --no-git-tag-version`** (file-only), then commit + tag separately — `npm version` skips the git step when run in a subdirectory of the repo root.
|
|
80
|
+
- **`npm version --no-git-tag-version`** (file-only), then commit and tag separately — `npm version` silently skips its git step in a subdirectory of the repo root.
|
|
81
81
|
|
|
82
82
|
## 8. Robustness defaults
|
|
83
83
|
|
|
84
|
-
- **Never hard-block on an advisory
|
|
85
|
-
- **Restricted-scope auth is ambient** — the read token in `~/.npmrc` (or `$NODE_AUTH_TOKEN` in CI) authenticates installs and `npm view`.
|
|
86
|
-
- **Keep the hot path fast** —
|
|
84
|
+
- **Never hard-block on an advisory or network check.** Version currency, registry lookups, telemetry — each degrades to a one-line notice and continues.
|
|
85
|
+
- **Restricted-scope auth is ambient** — the read token in `~/.npmrc` (or `$NODE_AUTH_TOKEN` in CI) authenticates installs and `npm view`. Attempt the operation; **never** prompt for `npm login` or reach for a substitute package.
|
|
86
|
+
- **Keep the hot path fast** — startup runs on every `npx`; do no heavy work beyond the self-update check.
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
Load when: auditing a workspace's health, reviewing a monorepo PR, scaffolding review gates, or deciding what to enforce next in an existing workspace.
|
|
4
4
|
|
|
5
|
-
The definitive requirements for a `@nurix/*` monorepo, in three tiers: **required** (every workspace, day one), **trigger-bound** (required the moment its trigger exists), and **deferred** (
|
|
5
|
+
The definitive requirements for a `@nurix/*` monorepo, in three tiers: **required** (every workspace, day one), **trigger-bound** (required the moment its trigger exists), and **deferred** (not required before scale). Assembly mechanics are [`monorepo-setup.md`](monorepo-setup.md); the publishing pipeline is [`publishing.md`](publishing.md); naming doctrine is [`pnpm.md`](../../../rules/pnpm.md) §1. Every line holds or it doesn't, and the closing table names how each is enforced.
|
|
6
6
|
|
|
7
7
|
## Required — every workspace, day one
|
|
8
8
|
|
|
@@ -17,22 +17,22 @@ The definitive requirements for a `@nurix/*` monorepo, in three tiers: **require
|
|
|
17
17
|
9. **Source-first internals, one-way flow** — internal packages export `./src/index.ts` through typed subpaths, no build step; apps depend on packages, never the reverse; anything not exported stays unreachable.
|
|
18
18
|
10. **Strict hoisting** — no `shamefully-hoist`, no `node-linker=hoisted`; an undeclared import must fail immediately.
|
|
19
19
|
11. **CI that gates** — a workflow installs with `--frozen-lockfile` and gates merges on the root fan-out. No CI is a compliance failure; a CI that mutates manifests before building is worse than none.
|
|
20
|
-
12. **A real suite behind `pnpm test`** — the canonical-command
|
|
20
|
+
12. **A real suite behind `pnpm test`** — the `testing` rule's canonical-command convention applies to every workspace; root-only toolchain configs (eslint + prettier) are wired to `lint`/`format` scripts that actually run. A fan-out script with zero targets is decoration, not compliance.
|
|
21
21
|
|
|
22
22
|
## Required the moment the trigger exists
|
|
23
23
|
|
|
24
|
-
- **Native or postinstall deps in the lockfile** → `onlyBuiltDependencies` in `pnpm-workspace.yaml` (never the root `package.json` `pnpm` field), kept current as native deps come and go — pnpm 10 silently skips unlisted builds and the failure surfaces at runtime.
|
|
25
|
-
- **A vulnerable transitive** → one `overrides` block in the workspace file; never duplicated into the root manifest.
|
|
24
|
+
- **Native or postinstall deps in the lockfile** → `onlyBuiltDependencies` in `pnpm-workspace.yaml` (**never** the root `package.json` `pnpm` field), kept current as native deps come and go — pnpm 10 silently skips unlisted builds and the failure surfaces at runtime.
|
|
25
|
+
- **A vulnerable transitive** → one `overrides` block in the workspace file; **never** duplicated into the root manifest.
|
|
26
26
|
- **A member crosses the publish boundary** (`private: false`) → the [`publishing.md`](publishing.md) fields; the build hook **inside** `scripts` (a top-level `prepublishOnly` key silently never runs); `sideEffects: false` (or the CSS-glob variant) so consumers tree-shake; and the parity harness — `pack` → tarball-install into fixtures → build → `publint` + `attw` — re-run on every `exports`-surface change. Dev-mode success proves nothing about what consumers get.
|
|
27
27
|
|
|
28
28
|
## Deferred until scale — explicitly not required before
|
|
29
29
|
|
|
30
|
-
- **Affected-filtering and task-graph runners** (`--filter "...[origin/main]"`, turbo, nx) — adopt
|
|
30
|
+
- **Affected-filtering and task-graph runners** (`--filter "...[origin/main]"`, turbo, nx) — adopt once full `pnpm -r` runs are slow enough to feel; never day one.
|
|
31
31
|
- **`CODEOWNERS` by directory** — when multiple teams own members; decoration before that.
|
|
32
32
|
|
|
33
33
|
## Enforcement — every rule names its mechanism
|
|
34
34
|
|
|
35
|
-
A rule that depends on discipline drifts; a rule the tooling enforces doesn't.
|
|
35
|
+
A rule that depends on discipline drifts; a rule the tooling enforces doesn't. A rule added to this list **must** name its mechanism and what happens on violation — prefer the variant that fails loudly at install or CI over one a reviewer must notice.
|
|
36
36
|
|
|
37
37
|
| Rule | Mechanism | On violation |
|
|
38
38
|
| --- | --- | --- |
|
|
@@ -2,11 +2,11 @@
|
|
|
2
2
|
|
|
3
3
|
Load when: scaffolding a new pnpm workspace, converting a repo to one, adding a workspace member, or wiring the root toolchain (catalog, scripts, build gating).
|
|
4
4
|
|
|
5
|
-
How a workspace is **assembled** —
|
|
5
|
+
How a workspace is **assembled** — root files, layout globs, catalog, verification. Package naming and boundaries are [`pnpm.md`](../../../rules/pnpm.md) §1–2; how a member publishes is [`publishing.md`](publishing.md); the invariants to keep true after assembly are [`monorepo-master-list.md`](monorepo-master-list.md). Reference implementations: `~/dev/workflows` (the canonical `packages/*` + catalog shape), `~/dev/starter` (adds `fixtures/*` and the publish-parity harness), `~/dev/robin` and `~/dev/sync-info` (the `packages/*` + `apps/*` split).
|
|
6
6
|
|
|
7
7
|
## 1. Three root files own the workspace
|
|
8
8
|
|
|
9
|
-
Everything workspace-wide lives in exactly three root files; packages stay thin
|
|
9
|
+
Everything workspace-wide lives in exactly three root files; packages stay thin, and nothing else at root compiles, publishes, or carries policy.
|
|
10
10
|
|
|
11
11
|
**`pnpm-workspace.yaml`** — membership + shared versions:
|
|
12
12
|
|
|
@@ -46,11 +46,11 @@ onlyBuiltDependencies: # postinstall allow-list — see §4
|
|
|
46
46
|
}
|
|
47
47
|
```
|
|
48
48
|
|
|
49
|
-
- **Fan out with `pnpm -r`; alias with `--filter`.** `--if-present` when not every member has the script; per-app
|
|
50
|
-
- **One toolchain config at root.** `eslint`/`prettier` run from the root over the whole tree — never per-package configs.
|
|
51
|
-
- **No turbo/nx.** Plain `pnpm -r` is the baseline;
|
|
49
|
+
- **Fan out with `pnpm -r`; alias with `--filter`.** Add `--if-present` when not every member has the script; a per-app convenience reads `"dev:marketplace": "pnpm --filter @nurix/nustack.marketplace dev"`.
|
|
50
|
+
- **One toolchain config at root.** `eslint`/`prettier` run from the root over the whole tree — **never** per-package configs.
|
|
51
|
+
- **No turbo/nx by default.** Plain `pnpm -r` is the baseline; adopt a task-graph runner only once the build graph is slow enough to feel — the same threshold as TS project references (`pnpm.md` §2).
|
|
52
52
|
|
|
53
|
-
**`tsconfig.base.json`** — the single TS policy owner (`pnpm.md` §2). The workflows repo's base is the model: `strict`, `noUncheckedIndexedAccess`, `noImplicitOverride`, `noFallthroughCasesInSwitch`, `noUnusedLocals` / `noUnusedParameters`, `declaration` + `declarationMap`, `skipLibCheck`. There is **no root `tsconfig.json`** — the base
|
|
53
|
+
**`tsconfig.base.json`** — the single TS policy owner (`pnpm.md` §2). The workflows repo's base is the model: `strict`, `noUncheckedIndexedAccess`, `noImplicitOverride`, `noFallthroughCasesInSwitch`, `noUnusedLocals` / `noUnusedParameters`, `declaration` + `declarationMap`, `skipLibCheck`. There is **no root `tsconfig.json`** — members extend the base; nothing compiles from it.
|
|
54
54
|
|
|
55
55
|
## 2. Layout — pick the globs by what the repo holds
|
|
56
56
|
|
|
@@ -62,17 +62,17 @@ onlyBuiltDependencies: # postinstall allow-list — see §4
|
|
|
62
62
|
| + `fixtures/*` | consumer-simulation apps for parity testing (§5) | starter |
|
|
63
63
|
|
|
64
64
|
- **Dependency direction is one-way** — `apps/*` depend on `packages/*`, never the reverse; siblings import each other's typed subpath exports, never a deep path into `src/` (`pnpm.md` §2).
|
|
65
|
-
-
|
|
65
|
+
- Default to flat globs; a deep path in the list (`sdk/packages/node/iii`) is for polyglot trees only.
|
|
66
66
|
|
|
67
67
|
## 3. `catalog:` — one version per shared dep
|
|
68
68
|
|
|
69
|
-
Anything two or more members depend on goes in the catalog: the toolchain (`typescript`, `vite`, `vitest`, type packages) and the host singletons (`react`, `react-dom`, `zod`). Members reference it as `"react": "catalog:"` — never a literal version. **A member that pins its own version beside the catalog defeats it** — two `react` copies means two hook dispatchers and runtime errors, not a type error.
|
|
69
|
+
Anything two or more members depend on goes in the catalog: the toolchain (`typescript`, `vite`, `vitest`, type packages) and the host singletons (`react`, `react-dom`, `zod`). Members reference it as `"react": "catalog:"` — **never** a literal version. **A member that pins its own version beside the catalog defeats it** — two `react` copies means two hook dispatchers and runtime errors, not a type error. Set `catalogMode: strict` in `pnpm-workspace.yaml` so such a pin is an install error ([`monorepo-master-list.md`](monorepo-master-list.md) rule 2).
|
|
70
70
|
|
|
71
|
-
Internal siblings are `"workspace:*"`; both protocols
|
|
71
|
+
Internal siblings are `"workspace:*"`; `pnpm publish` rewrites both protocols to real versions.
|
|
72
72
|
|
|
73
73
|
## 4. pnpm 10 build gating
|
|
74
74
|
|
|
75
|
-
pnpm 10
|
|
75
|
+
pnpm 10 runs no dependency postinstall script unless allow-listed. Keep the allow-list in `pnpm-workspace.yaml` — `onlyBuiltDependencies` (or the map-form `allowBuilds`, which `pnpm approve-builds` writes) — for native and codegen deps: `esbuild`, `@swc/core`, `better-sqlite3`. A missing entry is **not** an install error: install succeeds and the missing binary surfaces at runtime.
|
|
76
76
|
|
|
77
77
|
## 5. Adding a member
|
|
78
78
|
|
|
@@ -82,15 +82,15 @@ pnpm 10 refuses to run dependency postinstall scripts unless allow-listed. Keep
|
|
|
82
82
|
4. Dependencies: `workspace:*` for siblings, `catalog:` for shared externals.
|
|
83
83
|
5. Publishable member: dist-based `exports`, `files: ["dist"]`, `publishConfig` — [`publishing.md`](publishing.md) §2 — and cover it with the parity check (§6).
|
|
84
84
|
|
|
85
|
-
> **Agent guard — never commit a `link:` to a sibling repo on disk** (`"@nurix/contracts": "link:../../../NuStack-contracts"`). The path is machine-specific: every fresh clone and CI run breaks. Consume the published restricted package (`pnpm.md` §3);
|
|
85
|
+
> **Agent guard — never commit a `link:` to a sibling repo on disk** (`"@nurix/contracts": "link:../../../NuStack-contracts"`). The path is machine-specific: every fresh clone and CI run breaks. Consume the published restricted package (`pnpm.md` §3); a dep that must be co-developed belongs in this workspace.
|
|
86
86
|
|
|
87
87
|
## 6. Verify — publish parity
|
|
88
88
|
|
|
89
|
-
`pnpm install && pnpm -r typecheck`
|
|
89
|
+
Run `pnpm install && pnpm -r typecheck` after any membership or catalog change. A workspace with publishable members **also** needs a parity harness, because a deep `src/` import or a missing `exports` entry works in workspace-source dev and breaks only in the tarball. The starter's `scripts/publish-parity.mjs` is the model: `npm pack` each publishable member, tarball-install into isolated copies of `fixtures/*`, build them, run `publint` + `attw`. Wire it as the root `parity` script; run it before the first publish and after any change to an `exports` surface.
|
|
90
90
|
|
|
91
91
|
## 7. Quick checklist for a new workspace
|
|
92
92
|
|
|
93
|
-
- [ ] `pnpm-workspace.yaml`: globs (§2) + `catalog:` + build allow-list if needed
|
|
93
|
+
- [ ] `pnpm-workspace.yaml`: globs (§2) + `catalog:` + `catalogMode: strict` + build allow-list if needed
|
|
94
94
|
- [ ] Root `package.json`: `private: true`, exact `packageManager` pin, `engines`, `pnpm -r` scripts
|
|
95
95
|
- [ ] `tsconfig.base.json` with the strict policy set; no root `tsconfig.json`
|
|
96
96
|
- [ ] First member added per §5; siblings via `workspace:*`, shared deps via `catalog:`
|
|
@@ -2,21 +2,19 @@
|
|
|
2
2
|
|
|
3
3
|
Load when: creating or publishing a `@nurix/*` package, wiring or debugging a publish workflow, or diagnosing a failed `npm publish`.
|
|
4
4
|
|
|
5
|
-
How a `@nurix/*` package **ships**:
|
|
5
|
+
How a `@nurix/*` package **ships**: from `package.json` fields to a published version on **npmjs.org with `restricted` access**, installable only by npm users with read rights to the `nurix` org. Naming a package and structuring its workspace are [`pnpm.md`](../../../rules/pnpm.md) §1–2; consuming a published package is `pnpm.md` §3. Reference implementations: `~/dev/components` (`@nurix/components`, library) and `~/dev/etna/packages/cli` (`@nurix/etna`, CLI).
|
|
6
6
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
npmjs is the source of truth for **both publish and install**. A caching proxy may sit in front of it for reads, but it is **never a publish target** — treat a proxy URL in a publish path as a bug. And publish and install must name the **same** registry: a package published to one and installed from another 404s, and that 404 is indistinguishable from "never published", because a restricted read answers 404 rather than 401 so it doesn't leak which packages exist. (The consumer-side pairing check is [`pnpm.md`](../../../rules/pnpm.md) §3.)
|
|
7
|
+
npmjs is the registry for **both publish and install**. A caching proxy may serve reads, but it is **never a publish target** — a proxy URL in a publish path is a bug. Publish and install **must** name the same registry: a restricted read answers `404`, not `401`, so a package published to one registry and installed from another looks exactly like one never published (the consumer-side pairing check is `pnpm.md` §3).
|
|
10
8
|
|
|
11
9
|
## 1. Prerequisites (one-time, org-wide)
|
|
12
10
|
|
|
13
|
-
1.
|
|
14
|
-
2.
|
|
15
|
-
3.
|
|
11
|
+
1. Confirm you are a member of the npm org `nurix` with publish rights — <https://www.npmjs.com/settings/nurix/packages>.
|
|
12
|
+
2. Generate an npm **Automation** token with `Read and write` on the `nurix` scope (npm → Generate New Token → Automation).
|
|
13
|
+
3. Add it as the GitHub Actions secret `NPM_PUBLISH_TOKEN` on every publishing repo. Default to that name; use another only with a strong reason.
|
|
16
14
|
|
|
17
15
|
## 2. `package.json` — required fields
|
|
18
16
|
|
|
19
|
-
**Private by default.** A package is born `"private": true` and stays there until
|
|
17
|
+
**Private by default.** A package is born `"private": true` and stays there until another repo needs it. Publishing creates a contract — once published, a removed export or a widened peer range breaks consumers you cannot see. A package used only inside its repo **never** publishes; it links with `workspace:*` (`pnpm.md` §2). A package crossing the boundary carries:
|
|
20
18
|
|
|
21
19
|
```jsonc
|
|
22
20
|
{
|
|
@@ -28,12 +26,12 @@ npmjs is the source of truth for **both publish and install**. A caching proxy m
|
|
|
28
26
|
}
|
|
29
27
|
```
|
|
30
28
|
|
|
31
|
-
- **`publishConfig.access`** — `"restricted"
|
|
32
|
-
- **`files`** — without it, npm publishes everything not in `.gitignore`, **including source**. Library → `["dist"]`; CLI (etna) → `["bin/", "
|
|
33
|
-
- **Build to `dist` — never ship raw `src`.** A published library compiles with `tsc` (`declaration
|
|
34
|
-
- **Build hooks** — `prepack` for build steps that must produce files in `files` (also runs on install-as-git-dep); `prepublishOnly` for publish-only steps. Either alone is fine; `@nurix/components`
|
|
29
|
+
- **`publishConfig.access`** — default `"restricted"`: visible only to `nurix`-org readers. `"public"` is world-readable — use it only when that is the intent. A scoped package without the field defaults to `restricted`; set it explicitly anyway.
|
|
30
|
+
- **`files`** — without it, npm publishes everything not in `.gitignore`, **including source**. Library → `["dist"]`; CLI (etna) → `["bin/", "lib/", "data/"]`. Omit `package.json` and `README.md` — npm always includes them.
|
|
31
|
+
- **Build to `dist` — never ship raw `src`.** A published library compiles with `tsc` (`declaration` and `declarationMap` come from `tsconfig.base.json`) and points `exports`/`types` at `./dist/*`, `types` first in each subpath block (`pnpm.md` §2 owns the ordering). To keep the dev entry on source, swap source→dist at publish time with `publishConfig.exports`.
|
|
32
|
+
- **Build hooks** — `prepack` for build steps that must produce files in `files` (also runs on install-as-git-dep); `prepublishOnly` for publish-only steps. Either alone is fine; `@nurix/components` builds and verifies its types in `prepublishOnly`. With the build hooked here, a publish can never ship a stale `dist`.
|
|
35
33
|
- **Host singletons are `peerDependencies`** — `react`, `react-dom`, the framework client — so consumers keep exactly one copy; **never** bundle them. Apps depend on them directly.
|
|
36
|
-
- **Dependencies carry registry specs only** — **no** `file:`/`link:` (the workspace-root boundary,
|
|
34
|
+
- **Dependencies carry registry specs only** — **no** `file:`/`link:` (the workspace-root boundary, `pnpm.md` §2). `workspace:*` between members is fine: `pnpm publish` rewrites it into a real range.
|
|
37
35
|
- **Other useful fields** — `type: "module"`, `engines.node`, `bin` (CLIs), `main`/`module`/`types`/`exports` (libraries), `sideEffects: ["**/*.css"]`, `repository` (with `directory` for monorepos), `license: "UNLICENSED"`.
|
|
38
36
|
|
|
39
37
|
## 3. The publish workflow (generic template)
|
|
@@ -75,23 +73,23 @@ jobs:
|
|
|
75
73
|
- run: git push --follow-tags # pushes the bump commit AND the v<version> tag npm created
|
|
76
74
|
```
|
|
77
75
|
|
|
78
|
-
**
|
|
76
|
+
**Keep all three loop safeguards** — dropping any one lets the dual trigger publish in a loop or race: `paths:` scopes the push trigger to artifact-affecting files; `[skip ci]` in the bump commit stops that commit re-triggering the workflow; `cancel-in-progress: false` queues near-simultaneous merges instead of racing for a version number.
|
|
79
77
|
|
|
80
|
-
**`registry-url` is load-bearing** — without
|
|
78
|
+
**`registry-url` is load-bearing** — without it on `setup-node`, no `.npmrc` line reads `$NODE_AUTH_TOKEN`, and the publish falls back to anonymous auth and fails.
|
|
81
79
|
|
|
82
|
-
**The token never enters the repo checkout** — `setup-node` writes its auth `.npmrc` outside the workspace, so the bump commit
|
|
80
|
+
**The token never enters the repo checkout** — `setup-node` writes its auth `.npmrc` outside the workspace, so the pushed bump commit cannot pick it up; a hand-rolled auth line goes to `$HOME/.npmrc`, **never** a repo-path `.npmrc`. Expand the token in the shell step that writes it (`echo "//registry.npmjs.org/:_authToken=$NPM_PUBLISH_TOKEN" >> "$HOME/.npmrc"`), never a literal `${VAR}` left for npm to expand at read time — that breaks silently in any later step lacking the variable.
|
|
83
81
|
|
|
84
|
-
**A pnpm workspace publishes with `pnpm publish --no-git-checks`** — swap it into the publish step above
|
|
82
|
+
**A pnpm workspace publishes with `pnpm publish --no-git-checks`** — swap it into the publish step above and into flow C (§4). Only pnpm rewrites `catalog:`/`workspace:` specifiers into real ranges (§6); without `--no-git-checks`, pnpm refuses to publish from a non-release branch or from a tree carrying the bump commit the previous step made. `npm publish` is correct only for a single-package repo on an npm lockfile.
|
|
85
83
|
|
|
86
84
|
## 4. The three ways to ship
|
|
87
85
|
|
|
88
|
-
- **A. Auto on merge (
|
|
89
|
-
- **B. Manual dispatch (minor/major/none).** **Actions → Publish to npm → Run workflow**, pick `bump = minor|major` — or `none`, which publishes the current version as-is: the
|
|
90
|
-
- **C. Local (CI down
|
|
86
|
+
- **A. Auto on merge (default).** Land on the default branch; a change to a `paths:`-filtered file fires the workflow and ships a patch.
|
|
87
|
+
- **B. Manual dispatch (minor/major/none).** **Actions → Publish to npm → Run workflow**, pick `bump = minor|major` — or `none`, which publishes the current version as-is: the first-publish bootstrap for a repo that ships only from CI. §7's checklist bootstraps from a local publish instead; both are sanctioned.
|
|
88
|
+
- **C. Local (CI down, one-off, pipeline bootstrap).** `npm version <patch|minor|major>` → publish → `git push --follow-tags`. Sanctioned, at the price of bypassing CI's build, lockfile, and version-bump discipline. In a pnpm workspace the publish command is **`pnpm publish --no-git-checks`**, **never** `npm publish` (the catalog-protocol pitfall, §6); in a subdirectory of the repo root, `npm version` needs `--no-git-tag-version` plus a manual commit and tag.
|
|
91
89
|
|
|
92
|
-
Local auth uses two tokens
|
|
90
|
+
Local auth uses two tokens in two homes: a **read** token in `~/.npmrc` (`//registry.npmjs.org/:_authToken=…`) for `npm install`, and `NPM_PUBLISH_TOKEN` as an env var (sourced from `~/.zsh_secrets` or equivalent, **never** in `.npmrc` or the repo) for `npm publish`.
|
|
93
91
|
|
|
94
|
-
> **Agent guard — if `NPM_PUBLISH_TOKEN` isn't in the environment,
|
|
92
|
+
> **Agent guard — if `NPM_PUBLISH_TOKEN` isn't in the environment, never work around it.** No `npm login` swapping the user's auth, no rewriting `~/.npmrc`, no substituting the read token (it lacks publish rights → `403`), no `--registry` override, no temp-file token. **Stop and report**: "This repo can't publish locally — `NPM_PUBLISH_TOKEN` isn't set. Source `~/.zsh_secrets` (or wherever you keep it) and re-run, or trigger the CI workflow."
|
|
95
93
|
|
|
96
94
|
Verify after any flow: `https://www.npmjs.com/package/@nurix/<name>` and a clean-dir `npx @nurix/<name>@latest`.
|
|
97
95
|
|
|
@@ -99,27 +97,30 @@ Verify after any flow: `https://www.npmjs.com/package/@nurix/<name>` and a clean
|
|
|
99
97
|
|
|
100
98
|
When the package lives in `packages/<pkg>/` of a multi-package repo:
|
|
101
99
|
|
|
102
|
-
- **`working-directory: packages/<pkg>`** on the bump and publish steps so they
|
|
100
|
+
- **`working-directory: packages/<pkg>`** on the bump and publish steps, so they act on the right `package.json`.
|
|
103
101
|
- **Scoped concurrency** — `group: publish-<pkg>` lets sibling packages publish in parallel.
|
|
104
|
-
- **Tag prefix
|
|
105
|
-
- **One workflow file per published package** — each `publish-<pkg>.yml` owns its `paths:` filter, `working-directory`, and concurrency group.
|
|
106
|
-
- **Publish dependencies first.** If
|
|
102
|
+
- **Tag prefix only when more than one package cuts release tags** — `npm version --tag-version-prefix="<pkg>-v"` (→ `<pkg>-v0.2.0`) keeps their tags from colliding.
|
|
103
|
+
- **One workflow file per published package** — each `publish-<pkg>.yml` owns its `paths:` filter, `working-directory`, and concurrency group. One shared workflow couples every package's release cadence and republishes siblings on unrelated changes.
|
|
104
|
+
- **Publish dependencies first.** If `A` depends on `B` by `workspace:^`, `B` **must** already exist on the registry, or `A`'s rewritten range resolves to nothing for every consumer.
|
|
107
105
|
|
|
108
|
-
A repo with one publishable package only
|
|
106
|
+
A repo with one publishable package needs only `working-directory`: plain `v0.2.0` tags, concurrency scoping optional.
|
|
109
107
|
|
|
110
108
|
## 6. Common pitfalls
|
|
111
109
|
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
110
|
+
| Symptom | Cause → fix |
|
|
111
|
+
| --- | --- |
|
|
112
|
+
| **402 Payment Required** | `access: "public"` on an org without a plan for free public scoped packages → use `"restricted"` (or upgrade the plan) |
|
|
113
|
+
| **403 Forbidden** | `NPM_PUBLISH_TOKEN` missing or wrong, its user lacks `nurix` write access, or it is a read-only (not Automation) token |
|
|
114
|
+
| **"Cannot publish over previously published version x.y.z"** | a bump ran, the publish failed, and it was re-run without rebasing → `git reset` the bump and let CI re-bump, or `npm version patch && git push --follow-tags && npm publish` |
|
|
115
|
+
| **Publish succeeds but `npx @nurix/foo` is "command not found"** | no `bin` field, `bin` points outside `files`, or the script lacks its `#!/usr/bin/env node` shebang |
|
|
116
|
+
| **"Git working directory not clean" on `npm version`** | the checkout has untracked or modified files → build only into `.gitignore`d paths (`dist/`) |
|
|
117
|
+
| **Published tarball contains source** | `files` missing from `package.json` → add `["dist"]` and republish |
|
|
118
|
+
| **Auto-publish fires too often** | no `paths:` filter, or one too broad → tighten it to artifact-affecting files, or drop the push trigger for `workflow_dispatch` only |
|
|
119
|
+
| **Workflow loops forever** | the bump commit lacks `[skip ci]` → invoke `npm version` with `-m "chore: release v%s [skip ci]"` |
|
|
120
|
+
|
|
121
|
+
- **pnpm consumers fail with "external package declared a dependency using the catalog protocol"** — the package went out via `npm publish` from a pnpm workspace, which ships `catalog:`/`workspace:` specifiers verbatim. A workspace member **always** publishes via `pnpm publish`, locally as well as in CI. Deprecate the broken version and republish.
|
|
122
|
+
- **Tarball has no `dist/` though `prepublishOnly` ran a clean build** — `incremental: true` writes `tsconfig.tsbuildinfo` *next to `tsconfig.json`*, outside `dist/`; a stale one survives `rm -rf dist`, so `tsc` trusts it and emits nothing. The `clean` script **must** purge it: `rm -rf dist *.tsbuildinfo`. The parity harness catches this class before it ships.
|
|
123
|
+
- **A normal commit silently skips its own push** — GitHub scans the head commit's **whole message**, subject _and_ body, for skip directives (`[skip ci]`, `[ci skip]`, `[no ci]`, `[skip actions]`, `***NO_CI***`); a hand-written commit that merely _mentions_ one skips the entire push, and the publish never fires (symptom: branch moved, paths matched, Actions on — zero runs for the SHA). **Never** write a literal skip token in a commit message or PR body unless you mean it; to ship a push that carried one by accident, re-trigger via **Run workflow** / `gh workflow run … --ref dev`.
|
|
123
124
|
|
|
124
125
|
## 7. Quick checklist for a new `@nurix/*` package
|
|
125
126
|
|
|
@@ -128,5 +129,5 @@ A repo with one publishable package only needs `working-directory`; skip the pre
|
|
|
128
129
|
- [ ] Name follows [`pnpm.md`](../../../rules/pnpm.md) §1; if it's a workspace member, boundaries follow `pnpm.md` §2
|
|
129
130
|
- [ ] `.github/workflows/publish.yml` from §3
|
|
130
131
|
- [ ] `NPM_PUBLISH_TOKEN` secret added to the repo
|
|
131
|
-
- [ ] First publish from local (flow C, §4) to verify the pipeline end
|
|
132
|
+
- [ ] First publish from local (flow C, §4) to verify the pipeline end to end, then a no-op commit to verify CI — or, for a repo that ships only from CI, bootstrap with `bump = none` (§4B)
|
|
132
133
|
- [ ] README notes the `@nurix` scope so consumers know they need org access
|
package/data/harness-index.json
CHANGED
|
@@ -7,35 +7,38 @@ description: "Use when a plan or diff might break a cross-cutting contract. Boun
|
|
|
7
7
|
|
|
8
8
|
You diagnose whether a plan's changes would break an application boundary.
|
|
9
9
|
|
|
10
|
-
A boundary is a contract between two pieces of code (or code and infra) that share an assumption with **no static link** between them: the column a raw SQL string names, the JSON envelope a client unwraps, the event string a dispatcher parses, the env var a deploy script writes. Changing one side ships green — compiles, lints, tests pass — and breaks the other at runtime.
|
|
10
|
+
A boundary is a contract between two pieces of code (or code and infra) that share an assumption with **no static link** between them: the column a raw SQL string names, the JSON envelope a client unwraps, the event string a dispatcher parses, the env var a deploy script writes. Changing one side ships green — compiles, lints, tests pass — and breaks the other at runtime. Find those edges in the plan's path _before_ execution.
|
|
11
11
|
|
|
12
|
-
**The litmus is "would it ship green?"** An edge the compiler or an existing test already guards is not a boundary —
|
|
12
|
+
**The litmus is "would it ship green?"** An edge the compiler or an existing test already guards is not a boundary — spend your reading on the edges with no static link.
|
|
13
13
|
|
|
14
14
|
## Contract sources
|
|
15
15
|
|
|
16
16
|
Three layers tell you what to look for; the code tells you what exists.
|
|
17
17
|
|
|
18
|
-
- **The mechanisms** — [`references/mechanisms.md`](references/mechanisms.md)
|
|
19
|
-
- **The technology packs** — [`references/index.md`](references/index.md) routes to the packs under `references/packs/` by the signals in the touched code
|
|
20
|
-
- **Repo doctrine** — `.claude/rules/`, `docs/architecture/` (including `decisions/`), and any contracts package name the repo's bespoke contracts (a documented response envelope, a schema-parity rule, an entity model). A documented contract is a real side of a boundary: a plan that contradicts one is a finding **even when no code consumer exists yet** — this is how greenfield
|
|
21
|
-
|
|
22
|
-
The packs are a floor, not a fence: report any contract that passes the ships-green litmus, pack entry or not.
|
|
18
|
+
- **The mechanisms** — [`references/mechanisms.md`](references/mechanisms.md): the seven failure mechanisms every boundary instantiates (rendezvous-string, serialized-shape, generated-artifact, dual-authority, config-elsewhere, trust-invariant, lifecycle-protocol), each with its trace strategy, universal fix, and severity, plus the two stack-independent domain contracts (entity-model, tenancy). Load it on every check.
|
|
19
|
+
- **The technology packs** — [`references/index.md`](references/index.md) routes to the packs under `references/packs/` by the signals in the touched code; each pack carries one technology area's detect patterns, gotchas, and safe-change sequences. Load **only** the packs the touch list exhibits. The packs are a floor, not a fence: a contract no pack covers is still in scope — diagnose it from its mechanism and report it if it passes the litmus.
|
|
20
|
+
- **Repo doctrine** — `.claude/rules/`, `docs/architecture/` (including `decisions/`), and any contracts package name the repo's bespoke contracts (a documented response envelope, a schema-parity rule, an entity model). A documented contract is a real side of a boundary: a plan that contradicts one is a finding **even when no code consumer exists yet** — this is how a greenfield plan, with no code to trace, still gets checked.
|
|
23
21
|
|
|
24
22
|
## Run
|
|
25
23
|
|
|
26
|
-
**Input:** a plan path (given, or the newest in `docs/history/plans/`) — or a diff
|
|
24
|
+
**Input:** a plan path (given, or the newest in `docs/history/plans/`) — or a diff or named code area for non-plan work, where the diff's hunks stand in for the plan's steps. Steps 1–5 are identical either way; the report's **Missing plan subsections** and **Docs the plan's ledger missed** sections, and step 6, apply only to a plan.
|
|
25
|
+
|
|
26
|
+
1. **Build the touch list.** Parse the plan's Proposed Changes: files created or modified, entities or state added or changed, API changes, anything the author flags as a cross-module contract. Note missing subsections.
|
|
27
|
+
|
|
28
|
+
2. **Identify the contracts.** Read each touched file and its immediate imports. Against the mechanisms, the routed packs, and the repo doctrine, list the contracts this code participates in — the schema it queries, the envelope it emits, the events it publishes or parses, the auth checks it sits behind, the env it reads.
|
|
27
29
|
|
|
28
|
-
|
|
30
|
+
3. **Trace the other side.** The danger lives on the side the plan _doesn't_ mention. For each contract, grep the shared name or shape across the repo — the column name in other SQL strings, the response field in client unwrappers, the event kind in subscribers, the route in fetch calls and scripts, the env key in deploy tooling. Record consumers as `file:line`, doctrine as the doc path. At ≳10 contracts, fan the tracing out to parallel sub-agents — one per pack area, each prompt self-contained with its contracts and grep targets — and judge the returned evidence yourself; a small diff traces inline.
|
|
29
31
|
|
|
30
|
-
|
|
32
|
+
4. **Judge each intersection.** Does the planned change alter a shape a traced consumer depends on, break an invariant the code or doctrine establishes (append-only vocabularies, idempotency keys, fail-closed checks, ordering guarantees), or introduce a new cross-module edge? Grade it:
|
|
31
33
|
|
|
32
|
-
|
|
34
|
+
| Severity | Meaning |
|
|
35
|
+
| --- | --- |
|
|
36
|
+
| **CRITICAL** | Can corrupt persisted data, or open an auth/money path — trust-invariants and persisted or replayed shapes live here |
|
|
37
|
+
| **HIGH** | Silent cross-module breakage that ships green |
|
|
38
|
+
| **MEDIUM** | Drift-prone coupling not yet broken (string-matched, duplicated constants) |
|
|
39
|
+
| **LOW** | Smell |
|
|
33
40
|
|
|
34
|
-
|
|
35
|
-
- **CRITICAL** — can corrupt persisted data, or open an auth/money path.
|
|
36
|
-
- **HIGH** — silent cross-module breakage that would ship green.
|
|
37
|
-
- **MEDIUM** — drift-prone coupling (string-matched, duplicated constants).
|
|
38
|
-
- **LOW** — smell. Loud, immediately-visible runtime breakage caps at HIGH.
|
|
41
|
+
Loud, immediately visible runtime breakage caps at HIGH. Severity is intrinsic to the finding, never curved — three genuine CRITICALs in a small repo is signal, not inflation.
|
|
39
42
|
|
|
40
43
|
5. **Report in chat:**
|
|
41
44
|
|
|
@@ -61,15 +64,23 @@ The packs are a floor, not a fence: report any contract that passes the ships-gr
|
|
|
61
64
|
| Feature doc | Owned path in the touch list | In Doc Obligations? |
|
|
62
65
|
```
|
|
63
66
|
|
|
64
|
-
**
|
|
67
|
+
**Fill the doc cross-check from the touch list you already hold:** resolve its paths against the ownership index and compare the result to the plan's **Doc Obligations** rows. The plan's author wrote that ledger and nobody else checks it — a `None` that contradicts your touch list is the failure this section catches. Report the gap; **never** edit the ledger yourself.
|
|
68
|
+
|
|
69
|
+
**BLOCK** = any CRITICAL; a blocked plan folds the amendments and re-runs the check. HIGH findings yield **PASS WITH AMENDMENTS** — the invoking session folds the amendments into the plan in the same pass, before the plan is shown as finished. Order amendments sequencing-first: many violations dissolve by reordering steps (expand-then-contract, migrate-before-code) rather than changing what ships — prefer that fix, drawn from the pack entry's **Safe change** or the mechanism's **Universal fix**.
|
|
70
|
+
|
|
71
|
+
6. **Record the verdict in the plan's `Gates:` line.** Rewrite only your own segment, to exactly one of:
|
|
65
72
|
|
|
66
|
-
|
|
73
|
+
| Verdict | Segment |
|
|
74
|
+
| --- | --- |
|
|
75
|
+
| PASS | `Boundary: passed YYYY-MM-DD` |
|
|
76
|
+
| PASS WITH AMENDMENTS | `Boundary: passed YYYY-MM-DD` — written only once the amendments are folded |
|
|
77
|
+
| BLOCK | `Boundary: blocked YYYY-MM-DD` |
|
|
67
78
|
|
|
68
|
-
|
|
79
|
+
The compliance hook greps exactly this segment and blocks implementation edits inside the plan's scope until it reads `passed` — `pending` and `blocked` wall alike (the other gates are enforced by section presence, not segment text). A plan with no `Gates:` line gets one added; never skip the write-back.
|
|
69
80
|
|
|
70
81
|
## Rules
|
|
71
82
|
|
|
72
|
-
- **Read-only, one exception.** You never edit the plan's content, the code, or any doc — the invoking session folds your amendments into the plan
|
|
73
|
-
- **Verify in source.** Never flag from plan text alone — read both sides of every finding and cite them: code as `file:line`, doctrine as the doc path.
|
|
74
|
-
- **No other side, no violation.** An intersection with no traced consumer and no doctrine behind it
|
|
83
|
+
- **Read-only, one exception.** You never edit the plan's content, the code, or any doc — the invoking session folds your amendments into the plan; a plan is not finished until its segment reads `passed`. The single write you own is the plan's `Gates:` line (step 6), your segment only.
|
|
84
|
+
- **Verify in source.** **Never** flag from plan text alone — read both sides of every finding and cite them: code as `file:line`, doctrine as the doc path.
|
|
85
|
+
- **No other side, no violation.** An intersection with no traced consumer and no doctrine behind it is a **new edge**, not a violation: report it in its own section with the hardening its mechanism prescribes (version the cache key now, make the enum append-only now), so the fragile edge is born safe.
|
|
75
86
|
- **A clean report is valid.** Zero violations means say so — the coverage table is the evidence it was checked, not skipped. Don't pad findings.
|
|
@@ -1,16 +1,14 @@
|
|
|
1
1
|
# Pack routing index
|
|
2
2
|
|
|
3
|
-
Load when: every boundary check, right after `mechanisms.md`. Match the touch list against the signals below and load **only** the packs whose signals appear in the touched files or the plan's Proposed Changes
|
|
3
|
+
Load when: every boundary check, right after `mechanisms.md`. Match the touch list against the signals below and load **only** the packs whose signals appear in the touched files or the plan's Proposed Changes — a full-stack feature typically routes 2–4. A technology matching no row has a thin entry in `packs/generic-tail.md` or none; either way, diagnose it from its mechanism.
|
|
4
4
|
|
|
5
5
|
| Pack | Load when the touch list shows |
|
|
6
6
|
| --- | --- |
|
|
7
7
|
| `packs/persistence.md` | `schema.ts`, `init.sql`, `sqliteTable(`, `drizzle-kit`, `pgTable`, migrations, `*.seed.sql`, seed generators, JSON columns, `pgEnum`, pgvector, Redis `get`/`set` caching |
|
|
8
8
|
| `packs/wire-api.md` | Express routes/middleware (`app.use(`, `router.`), `res.json({ status`, zod schemas on requests, axios clients/interceptors, `cors(`, inbound webhooks, `rateLimit(` |
|
|
9
|
-
| `packs/frontend.md` | `createSlice`/`configureStore`, RTK Query (`tagTypes`), react-router tables/`useParams`, Tailwind CSS (`@theme`, `personality.css`, `globals.css`), `vite.config.*` (alias, env), `@nurix/components`, localforage/redux-persist, date formatting |
|
|
9
|
+
| `packs/frontend.md` | `createSlice`/`configureStore`, RTK Query (`tagTypes`), react-router tables/`useParams`, Tailwind CSS (`@theme`, `personality.css`, `design.tokens.css`, `globals.css`), `vite.config.*` (alias, env), `@nurix/components`, localforage/redux-persist, date formatting |
|
|
10
10
|
| `packs/auth.md` | `jwtVerify(`/jose, `betterAuth(`/`auth.api.`, session cookies, CSRF, OAuth callbacks/`redirect_uri`, `tenantId`/`organizationId` scoping, password hashing, auth middleware mounts |
|
|
11
11
|
| `packs/cloudflare.md` | `wrangler.jsonc`/`wrangler.toml`, `env.X` bindings, `extends DurableObject`/`[[migrations]]`, KV/R2/D1/Queues, `scheduled(`/crons, `WorkerEntrypoint`, `compatibility_date`, static assets config |
|
|
12
12
|
| `packs/async-messaging.md` | BullMQ (`new Queue(`/`new Worker(`), Redis pub/sub or Streams, WebSockets/socket.io, SSE (`text/event-stream`), channel-name templates, outbound webhook senders |
|
|
13
13
|
| `packs/build-config-runtime.md` | `package.json` (deps/`exports`/scripts/`packageManager`), lockfiles, `.env*`, tsconfig `paths`, Dockerfiles/compose, `.github/workflows/`, pino config, `trust proxy`, ports/healthchecks |
|
|
14
14
|
| `packs/generic-tail.md` | GraphQL, gRPC/protobuf, tRPC, OpenAPI codegen, Kafka/schema registry/CDC, workflow engines, event sourcing, Postgres RLS/pooling/collation, SSR/hydration, module federation, i18n, analytics plans, hosted feature flags, K8s probes/shutdown/limits, service mesh, IaC outputs, SAML, Lambda, IPC |
|
|
15
|
-
|
|
16
|
-
Two files never route — they always load: `mechanisms.md` (the seven failure mechanisms + domain contracts) and this index.
|