@bigknoxy/hashpilot 4.8.1 → 4.8.3

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.
@@ -52,3 +52,48 @@ Format:
52
52
  - **Decision:** `runZg` returns a `ZgProcessResult` with separate `timedOut` and `spawnError` fields. The caller checks spawn errors first, then timeouts, then non-zero exits, then parses output.
53
53
  - **Alternatives considered:** (1) throw on spawn error — rejected: the search command should return a structured error, not crash. (2) single `error` field — rejected: timeout and spawn-failure require different recovery paths.
54
54
  - **Consequences:** `SEARCH_FAILED` errors now include actionable diagnostics (`spawnError`, `timedOut`, or `stderr`). Exit-1 with no stderr (ripgrep-style "no matches") returns empty hits, not an error.
55
+
56
+ ## D005: release.yml keeps GH_TOKEN (PAT) — GITHUB_TOKEN can't trigger downstream workflows
57
+
58
+ - **Date:** 2026-09-05
59
+ - **PR:** #199
60
+ - **Context:** Audit finding B70 recommended switching all `secrets.GH_TOKEN` references to `secrets.GITHUB_TOKEN`. The default `GITHUB_TOKEN` is minted per-run, scoped, and auto-expires — strictly better for security. However, GitHub's design prevents `GITHUB_TOKEN` from triggering downstream workflows (to avoid recursive runs). semantic-release's `prepare` phase pushes a version-bump commit to `main`, which must trigger the `gh-pages` workflow. With `GITHUB_TOKEN`, that push is invisible to GitHub's event system.
61
+ - **Decision:** Keep `GH_TOKEN` in `release.yml` (lines 110, 137). Switch `gh-pages.yml` to `GITHUB_TOKEN` since it doesn't need to trigger further workflows.
62
+ - **Alternatives considered:** (1) Switch everything to `GITHUB_TOKEN` — rejected: gh-pages deploy would never trigger after a release. (2) Use `workflow_dispatch` trigger instead — rejected: adds latency and requires a separate orchestration step. (3) Use `workflow_run` trigger — rejected: only fires after the triggering workflow completes, which is too late for the current architecture.
63
+ - **Consequences:** The release workflow retains a PAT with broader scope than ideal. Mitigation: the PAT should be scoped to the minimum permissions (just `contents: write` for the repo). Regular rotation recommended.
64
+
65
+ ## D006: Pin third-party actions by commit SHA, not tag
66
+
67
+ - **Date:** 2026-09-05
68
+ - **PR:** #199
69
+ - **Context:** Audit finding B71 flagged `peaceiris/actions-gh-pages@v4` as a floating tag. The action is handed a write-capable token. A compromised `v4` tag could push malicious content to `gh-pages`.
70
+ - **Decision:** Pin `peaceiris/actions-gh-pages` to commit SHA `329bcc8f12caed2cefe5a5b80781499a6f3b361b` (the `v4` tag at time of pinning). First-party `actions/*` actions (checkout, setup-node, setup-bun) remain on major-version tags — these are GitHub-maintained with strong supply-chain controls and the SHA would need updating on every minor/patch bump.
71
+ - **Alternatives considered:** (1) Pin all actions by SHA — rejected: first-party actions update frequently and pinning creates maintenance burden with no meaningful security gain (GitHub controls both the actions and the runner). (2) Use `actions/checkout` pinned — not done, same reason.
72
+ - **Consequences:** Third-party action pinned; any tag mutation is blocked. First-party actions on tags will auto-update within major versions. Record the SHA in the comment for traceability.
73
+
74
+ ## D007: Pin agent-browser to exact version in CI
75
+
76
+ - **Date:** 2026-09-05
77
+ - **PR:** #199
78
+ - **Context:** Audit finding B69 flagged `npm install -g agent-browser` with no version pin. The job holds `contents: write`. A compromised `agent-browser` package would execute with write access to `gh-pages`.
79
+ - **Decision:** Pin to `agent-browser@0.36.0` (current latest). Add comment documenting the pin rationale.
80
+ - **Alternatives considered:** (1) Remove agent-browser entirely — rejected: it provides real deploy verification. (2) Add npm integrity check — rejected: npm's `--ignore-scripts` would break agent-browser's `install` step; checksum verification requires custom tooling. Version pin + review on updates is the practical baseline.
81
+ - **Consequences:** Supply-chain attack window reduced from "any future version" to "only 0.36.0". Version bumps must be deliberate and reviewed.
82
+
83
+ ## D008: Ship bun.lock in npm package for frozen-lockfile installs
84
+
85
+ - **Date:** 2026-09-05
86
+ - **PR:** #199
87
+ - **Context:** Audit finding B79 noted that npm-sourced installs resolve dependencies fresh (`bun install --production`) instead of using `--frozen-lockfile`, since the npm tarball didn't include `bun.lock`. This means transitive deps could differ from what CI tested.
88
+ - **Decision:** Add `bun.lock` to `package.json`'s `files` array so it ships in the npm tarball. The existing `install.sh` logic already uses `--frozen-lockfile` when `bun.lock` is present.
89
+ - **Alternatives considered:** (1) Generate a lockfile during install — rejected: defeats the purpose of pinning. (2) Keep `bun.lock` out and accept fresh resolution — rejected: supply-chain pinning gap.
90
+ - **Consequences:** npm-installed packages now include `bun.lock` and install with `--frozen-lockfile`. The npm package size increases slightly. The `else` branch in `install.sh` (lines 423-432) becomes unreachable for current npm installs but is kept as a safe fallback.
91
+
92
+ ## D010: npm-sourced installs with a shipped bun.lock use --frozen-lockfile --production
93
+
94
+ - **Date:** 2026-09-06
95
+ - **PR:** hotfix (after v4.8.2)
96
+ - **Context:** v4.8.2 shipped broken for the **default npm install path**. D007 (#199) added `bun.lock` to the npm package's `files`; the dependency-mode guard from #194 treated `NPM_INSTALLED=true && bun.lock present` as a fatal ("refusing to guess which dependency mode is correct"). D007 made that guard's fire-branch the *normal* case, so every fresh npm install of v4.8.2 aborted before installing dependencies. The bug passed CI because smoke installs from `@latest` npm — main's run predated the semantic-release publish and tested 4.8.1 (no lockfile); the breakage only surfaced once v4.8.2 reached the registry.
97
+ - **Decision:** The dependency-mode decision now keys off `$NPM_INSTALLED`, not a bare lockfile-presence check. npm source + shipped lock → `bun install --frozen-lockfile --production` (pinned, devDeps skipped). npm source, no lock → `--production` (legacy). git/local + lock → `--frozen-lockfile` (dev wants devDeps). git/local, no lock → hard error (unchanged).
98
+ - **Alternatives considered:** (1) Revert D007 (don't ship bun.lock) — rejected: pinning transitive deps is the correct supply-chain posture; the packaging was not the flaw, the mode-selector was. (2) Remove the #194 guard entirely and let the old `[ -f bun.lock ]` branch run plain `--frozen-lockfile` — rejected: that would pull devDependencies (semantic-release) into npm installs, breaking the smoke's devDep-exclusion assertion and bloating user installs.
99
+ - **Consequences:** npm-installed packages are pinned to the shipped lockfile while still skipping devDeps. **Coverage hole identified:** smoke's npm-path test installs `@latest` (published version), so an unpublished PR branch is never validated against its own packed tarball — add a version-consistency guard so "testing the wrong release" is loud, not silent.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@bigknoxy/hashpilot",
3
- "version": "4.8.1",
3
+ "version": "4.8.3",
4
4
  "description": "HashPilot — Global Tool-Agnostic Structured Editing Core for Coding Agents",
5
5
  "type": "module",
6
6
  "engines": {
@@ -27,7 +27,8 @@
27
27
  "docs/",
28
28
  "LICENSE",
29
29
  "package.json",
30
- "tsconfig.json"
30
+ "tsconfig.json",
31
+ "bun.lock"
31
32
  ],
32
33
  "repository": {
33
34
  "type": "git",
@@ -405,31 +405,45 @@ log "Installing dependencies..."
405
405
  # (e.g. an npm extraction that somehow shipped a lockfile, or a git/local
406
406
  # source that's missing one) fails loudly here instead of silently
407
407
  # guessing.
408
- if [ "$NPM_INSTALLED" = "true" ] && [ -f "$SOURCE_DIR/bun.lock" ]; then
409
- err "npm-sourced install unexpectedly has a bun.lock — refusing to guess which dependency mode is correct"
410
- exit 1
411
- fi
412
408
  if [ "$NPM_INSTALLED" = "false" ] && [ ! -f "$SOURCE_DIR/bun.lock" ]; then
413
409
  err "Source is missing bun.lock and wasn't installed from npm — refusing to guess which dependency mode is correct"
414
410
  err "(local-clone and --source installs are expected to have bun.lock, same as the git repo does)"
415
411
  exit 1
416
412
  fi
417
413
 
418
- if [ -f "$SOURCE_DIR/bun.lock" ]; then
414
+ # Dependency mode decided here, not by a bare [ -f bun.lock ] presence check:
415
+ # - git/local source with bun.lock -> --frozen-lockfile (dev wants devDeps)
416
+ # - npm source with bun.lock (D007) -> --frozen-lockfile --production (pinned,
417
+ # but skip devDeps — the npm package lists them in package.json even though
418
+ # `files` excludes them; a plain --frozen-lockfile would pull semantic-release)
419
+ # - npm source without bun.lock -> --production (legacy, pre-D007)
420
+ if [ "$NPM_INSTALLED" = "true" ]; then
421
+ # npm always skips devDependencies. When the shipped lockfile is present
422
+ # (D007), pin to it too; otherwise resolve production deps fresh.
423
+ if [ -f "$SOURCE_DIR/bun.lock" ]; then
424
+ detail "bun.lock shipped (npm package) — installing pinned production dependencies"
425
+ cd "$TARGET_DIR/structured-editing"
426
+ bun install --frozen-lockfile --production 2>&1 | while IFS= read -r line; do detail "$line"; done
427
+ cd "$OLDPWD"
428
+ else
429
+ # The npm-published package.json still lists devDependencies (npm's
430
+ # `files` field controls which FILES ship, not which package.json fields
431
+ # do) — a plain `bun install` would resolve and install semantic-release,
432
+ # fast-check, and the rest of the dev toolchain for no reason on an end
433
+ # user's machine. --production skips them; the CLI never needs them.
434
+ detail "No bun.lock shipped (npm package install) — resolving production dependencies fresh"
435
+ rm -f "$TARGET_DIR/structured-editing/bun.lock"
436
+ cd "$TARGET_DIR/structured-editing"
437
+ bun install --production 2>&1 | while IFS= read -r line; do detail "$line"; done
438
+ cd "$OLDPWD"
439
+ fi
440
+ elif [ -f "$SOURCE_DIR/bun.lock" ]; then
419
441
  cd "$TARGET_DIR/structured-editing"
420
442
  bun install --frozen-lockfile 2>&1 | while IFS= read -r line; do detail "$line"; done
421
443
  cd "$OLDPWD"
422
444
  else
423
- # The npm-published package.json still lists devDependencies (npm's
424
- # `files` field controls which FILES ship, not which package.json fields
425
- # do) — a plain `bun install` would resolve and install semantic-release,
426
- # fast-check, and the rest of the dev toolchain for no reason on an end
427
- # user's machine. --production skips them; the CLI never needs them.
428
- detail "No bun.lock shipped (npm package install) — resolving production dependencies fresh"
429
- rm -f "$TARGET_DIR/structured-editing/bun.lock"
430
- cd "$TARGET_DIR/structured-editing"
431
- bun install --production 2>&1 | while IFS= read -r line; do detail "$line"; done
432
- cd "$OLDPWD"
445
+ err "Source has no bun.lock and was not installed from npm — nothing to pin against"
446
+ exit 1
433
447
  fi
434
448
  detail "Dependencies installed"
435
449