@ngockhoale/ukit 3.4.28 → 3.4.29

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/CHANGELOG.md CHANGED
@@ -9,6 +9,13 @@ All notable changes to UKit are documented here.
9
9
  - New `/ukit:ci-check` command + `ci-check.mjs` helper (core pack): prechecks, fail-closed budget guard, local preflight, at most one dispatch per invocation, write-ahead run-id state, `status` (never dispatches), `--ref`, `--dry-run`, per-OS report. Never pushes, merges or publishes.
10
10
  - Default-branch bootstrap stub documented in `docs/design/CI-CROSS-PLATFORM-MATRIX.md` (requires separate approval).
11
11
 
12
+ ## 3.4.29 - 2026-10-09
13
+
14
+ **Fixes (no new features).**
15
+ - `ukit install` inside the UKit source repo no longer overwrites the hand-curated root `CLAUDE.md` / `AGENTS.md` (repo-only Release Policy was lost). Self-install now seeds but never overwrites them, like `docs/`; downstream projects still get them refreshed. Regression: `tests/core/buildPlan.test.js`.
16
+ - `ukit status` no longer reads the ambient project's `context-capacity.json` (CLAUDE_PROJECT_DIR/cwd); the capacity record is scoped to the reported project root, so `statusCommand.test.js` passes with or without a live runtime cache.
17
+ - Completion gate: `yarn release:verify; echo "exit=$?"` (and `&&`/`|` variants without a space) is now recognised as a verification command, so the green run records `verification-evidence`. Failed runs still record as failed. Regression: `tests/hooks/executionLedgerReleaseVerify.test.js`.
18
+
12
19
  ## 3.4.28 - 2026-10-09
13
20
 
14
21
  **Release-safety close-out: config preservation, safe `ukit update`, OMP eval/Bash hardening (local RC; not yet published).**
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ngockhoale/ukit",
3
- "version": "3.4.28",
3
+ "version": "3.4.29",
4
4
  "description": "Install/update an index-first AI workspace for Claude Code, OpenAI Codex and omp (Oh My Pi).",
5
5
  "license": "MIT",
6
6
  "type": "module",
@@ -45,6 +45,16 @@ function safeResolve(basePath, relativePath) {
45
45
  return resolvedPath;
46
46
  }
47
47
 
48
+ // Root instruction files are hand-curated canonical sources in the UKit repo itself (they carry
49
+ // the repo-only Release Policy, rendered from instructions/ + the repo overlay), so a
50
+ // self-install must seed them but never overwrite them.
51
+ const SELF_INSTALL_PROTECTED_ROOT_FILES = new Set(['CLAUDE.md', 'AGENTS.md']);
52
+
53
+ function isSelfInstallProtectedTarget(targetPath, projectRoot) {
54
+ return isDocsTarget(targetPath, projectRoot)
55
+ || SELF_INSTALL_PROTECTED_ROOT_FILES.has(path.relative(projectRoot, targetPath));
56
+ }
57
+
48
58
  function isDocsTarget(targetPath, projectRoot) {
49
59
  const relative = path.relative(projectRoot, targetPath);
50
60
  return relative === 'docs' || relative.startsWith(`docs${path.sep}`);
@@ -88,7 +98,7 @@ async function downgradeSelfInstallDocOverwrites(planEntries, { templatesRoot, p
88
98
  if (entry.type === 'link' || entry.mergeStrategy !== 'overwrite_with_backup') {
89
99
  continue;
90
100
  }
91
- if (isDocsTarget(entry.targetPath, projectRoot)) {
101
+ if (isSelfInstallProtectedTarget(entry.targetPath, projectRoot)) {
92
102
  entry.mergeStrategy = 'skip';
93
103
  }
94
104
  }
@@ -146,7 +146,9 @@ export async function buildStatusReport(projectRoot) {
146
146
  const compactSummary = buildCompactSummary(compactHistory);
147
147
  const compactPressure = await buildCompactPressureState(
148
148
  await readStatusJson(runtimePaths.compactPressurePath),
149
- config,
149
+ // Scope the negotiated-capacity record to the reported project; without projectRoot the
150
+ // reader falls back to CLAUDE_PROJECT_DIR/cwd and leaks another project's runtime cache.
151
+ { ...config, projectRoot },
150
152
  );
151
153
  const promptCacheStats = buildPromptCacheStats(await readStatusJson(runtimePaths.promptCachePath));
152
154
  const outputCompressionStats = summarizeOutputHistory(await readStatusJson(runtimePaths.outputHistoryPath));
@@ -501,15 +501,17 @@ function extractExitCode(payload = {}) {
501
501
  return null;
502
502
  }
503
503
 
504
+ // The trailing boundary accepts shell terminators (`;`, `&`, `|`, `)`) so
505
+ // `yarn release:verify; echo "exit=$?"` — the real-exit-code idiom — still mints a receipt.
504
506
  function isVerificationCommand(command) {
505
507
  const text = String(command || '').trim();
506
508
  if (!text) return false;
507
509
  return [
508
- /(?:^|\s|\.bin\/)(?:vitest|jest|mocha|ava|pytest|py\.test)(?:\s|$)/i,
509
- /(?:^|\s)(?:npm|pnpm|yarn|bun)(?:\s+run)?\s+(?:test|lint|typecheck|check|build)(?:\s|$)/i,
510
- /(?:^|\s)(?:npm|pnpm|yarn|bun)(?:\s+run)?\s+release:verify(?:\s|$)/i,
511
- /(?:^|\s|\.bin\/)(?:tsc|eslint|biome|ruff|mypy)(?:\s|$)/i,
512
- /(?:^|\s)node\s+--check(?:\s|$)/i,
510
+ /(?:^|\s|\.bin\/)(?:vitest|jest|mocha|ava|pytest|py\.test)(?:\s|$|[;&|)])/i,
511
+ /(?:^|\s)(?:npm|pnpm|yarn|bun)(?:\s+run)?\s+(?:test|lint|typecheck|check|build)(?:\s|$|[;&|)])/i,
512
+ /(?:^|\s)(?:npm|pnpm|yarn|bun)(?:\s+run)?\s+release:verify(?:\s|$|[;&|)])/i,
513
+ /(?:^|\s|\.bin\/)(?:tsc|eslint|biome|ruff|mypy)(?:\s|$|[;&|)])/i,
514
+ /(?:^|\s)node\s+--check(?:\s|$|[;&|)])/i,
513
515
  ].some((pattern) => pattern.test(text));
514
516
  }
515
517