Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/e2e.yml
Original file line number Diff line number Diff line change
Expand Up @@ -254,7 +254,7 @@ jobs:
if: github.event_name == 'pull_request'
needs: release-candidate-pr
name: Candidate consumer (${{ matrix.os }}, Node ${{ matrix.node }})
timeout-minutes: 30
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -309,6 +309,16 @@ jobs:
"$bin" --help >/dev/null
HOME="$RUNNER_TEMP/feynman-home" FEYNMAN_HOME="$RUNNER_TEMP/feynman-home" "$bin" packages list
HOME="$RUNNER_TEMP/feynman-home" FEYNMAN_HOME="$RUNNER_TEMP/feynman-home" "$bin" search status
global_prefix="$RUNNER_TEMP/feynman-global"
if command -v cygpath >/dev/null 2>&1; then
global_prefix=$(cygpath -u "$global_prefix")
global_bin="$global_prefix/feynman.cmd"
else
global_bin="$global_prefix/bin/feynman"
fi
npm install --global --prefix "$global_prefix" --omit=dev --no-audit "$tarball"
test "$("$global_bin" --version | tail -1)" = "$version"
"$global_bin" --help >/dev/null
runtime_audit="$RUNNER_TEMP/feynman-runtime-audit"
mkdir -p "$runtime_audit"
runtime_archive="$consumer/node_modules/@companion-ai/feynman/.feynman/runtime-workspace.tgz"
Expand Down
17 changes: 17 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -297,6 +297,7 @@ jobs:
needs:
- version-check
- verify
timeout-minutes: 45
strategy:
fail-fast: false
matrix:
Expand Down Expand Up @@ -344,6 +345,16 @@ jobs:
"$bin" --help >/dev/null
HOME="$RUNNER_TEMP/feynman-home" FEYNMAN_HOME="$RUNNER_TEMP/feynman-home" "$bin" packages list
HOME="$RUNNER_TEMP/feynman-home" FEYNMAN_HOME="$RUNNER_TEMP/feynman-home" "$bin" search status
global_prefix="$RUNNER_TEMP/feynman-global"
if command -v cygpath >/dev/null 2>&1; then
global_prefix=$(cygpath -u "$global_prefix")
global_bin="$global_prefix/feynman.cmd"
else
global_bin="$global_prefix/bin/feynman"
fi
npm install --global --prefix "$global_prefix" --omit=dev --no-audit "$tarball"
test "$("$global_bin" --version | tail -1)" = "$VERSION"
"$global_bin" --help >/dev/null
runtime_audit="$RUNNER_TEMP/feynman-runtime-audit"
mkdir -p "$runtime_audit"
runtime_archive="$consumer/node_modules/@companion-ai/feynman/.feynman/runtime-workspace.tgz"
Expand Down Expand Up @@ -691,6 +702,12 @@ jobs:
"$bin" --help >/dev/null
node "$consumer/node_modules/@companion-ai/feynman/scripts/verify-package-artifact.mjs" \
"$consumer/node_modules/@companion-ai/feynman"
global_prefix="$RUNNER_TEMP/published-global"
npm install --global --prefix "$global_prefix" --omit=dev --no-audit \
"@companion-ai/feynman@$VERSION"
global_bin="$global_prefix/bin/feynman"
test "$("$global_bin" --version | tail -1)" = "$VERSION"
"$global_bin" --help >/dev/null

release_dir="$RUNNER_TEMP/published-release"
mkdir -p "$release_dir"
Expand Down
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,16 @@ Workspace lab notebook for long-running or resumable research work.

Use this file to track chronology, not release notes. Keep entries short, factual, and operational.

### 2026-07-28 14:30 PDT — intake-sweep-global-npm-install

- Objective: Re-run the terminal intake after `0.3.6`, including the documented global npm install path rather than treating a clean project-prefix consumer as equivalent.
- Found: Both an upgrade under `~/.npm-global` and a fresh disposable `npm install --global --prefix ... @companion-ai/feynman@0.3.6` produced an empty `node_modules/@opentelemetry/api` directory. `feynman --version` then failed before command dispatch from the direct telemetry import, while the existing non-global consumer gate passed because npm could hoist `@opentelemetry/api@1.9.1` above the package root.
- Fixed: Bundle the exact direct OpenTelemetry API with the five existing runtime packages, and exercise the exact global install/version/help path across every supported package-consumer matrix plus post-publication registry verification.
- Exact-head CI: Run `30402843325` passed the release candidate, all Linux/macOS consumers, Windows Node `25`, and the native installer. Windows Node `24.18.0` completed both local and global version/help checks but hit the former 30-minute job limit six seconds into runtime extraction; the identical Node `25` path completed in `29m38s`. Both PR and release consumer matrices now use a bounded 45-minute timeout.
- Verified locally: Scoped workflow tests passed (`7/7`); the complete suite passed (`654/654`); typecheck, build, architecture check, actionlint, website lint/typecheck/build (`34` pages), root and website full/production audits, package freshness review, and `git diff --check` passed. Dry and real packs matched at 111,637,876 bytes / 328,227,251 unpacked / 39,692 files with npm integrity `sha512-PFChM+N8VQsNZQ64uFLb9EDytP96vKFvThpooai5P0KnmYfyspXyFrleW5gHeQipEH5zuIxTZnbJZ2Fv/jC/SQ==` and tarball SHA-256 `941389ca83a06b49105a4c07515fad1e250617ced593192ad2a90c4b851b61a0`.
- Installed proof: A clean consumer and extracted runtime each audited at zero, artifact verification passed with runtime SHA-256 `c31b7db649f47997abfc6a3622c5af1758af71789a5eae9c96c98932c3d96ac1`, and clean global installs under npm `11.16.0` and `11.18.0` each contained nonempty bundled OpenTelemetry API `1.9.1` trees and passed version/help. Installed RPC loaded `112` commands including `thinking` and `web-results`.
- State: `verified` locally; exact pushed-SHA Daytona, GitHub Linux/macOS/Windows Node `22`/`24`/`25`, merge, and npm/GitHub `0.3.7` release identity remain required. Next: persist the candidate, require every exact-head gate green, then publish and verify the registry global install.

### 2026-07-28 08:30 PDT — intake-sweep-manual-release-provenance-gate

- Objective: Keep manual release recovery from creating an immutable npm package whose provenance the push-only identity policy would reject.
Expand Down
10 changes: 10 additions & 0 deletions RELEASES.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,16 @@ GitHub release notes are generated from the matching `## vX.Y.Z` section in this

## Unreleased

## v0.3.7 - 2026-07-28

### Reliability

- Fixed `npm install -g @companion-ai/feynman` producing an unusable CLI when npm left the direct OpenTelemetry API hoist target empty beside Feynman's bundled Pi packages. The package now bundles its exact direct telemetry API so global installs can start reliably.

### Validation

- Added clean global-install version and help smokes to every supported Linux, macOS, Windows, and Node `22`/`24`/`25` package-consumer gate, plus post-publication verification of the registry package.

## v0.3.6 - 2026-07-28

- Raised the npm-install Node 22 floor to `22.22.0`, matching the direct telemetry runtime's actual engine contract; standalone installers continue to bundle Node `24.18.0`.
Expand Down
7 changes: 4 additions & 3 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@companion-ai/feynman",
"version": "0.3.6",
"version": "0.3.7",
"description": "Research-first CLI agent built on Pi and alphaXiv",
"license": "MIT",
"type": "module",
Expand Down Expand Up @@ -57,7 +57,8 @@
"@earendil-works/pi-agent-core",
"@earendil-works/pi-ai",
"@earendil-works/pi-coding-agent",
"@earendil-works/pi-tui"
"@earendil-works/pi-tui",
"@opentelemetry/api"
],
"scripts": {
"preinstall": "node ./scripts/check-node-version.mjs",
Expand Down
40 changes: 40 additions & 0 deletions tests/release-workflows.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@ import test from "node:test";

const e2eWorkflow = readFileSync(".github/workflows/e2e.yml", "utf8");
const publishWorkflow = readFileSync(".github/workflows/publish.yml", "utf8");
const packageManifest = JSON.parse(readFileSync("package.json", "utf8")) as {
bundleDependencies?: string[];
};

test("pull-request release gates validate the merge candidate", () => {
assert.match(e2eWorkflow, /pull_request:\s*\n\s+branches: \[main\]/);
Expand Down Expand Up @@ -42,6 +45,43 @@ test("PR and publish workflows require clean package and consumer audits", () =>
}
});

test("package gates exercise the global npm install path", () => {
assert.ok(
packageManifest.bundleDependencies?.includes("@opentelemetry/api"),
"the direct telemetry API must be bundled so npm global installs cannot leave an empty hoist target",
);
assert.equal(
(e2eWorkflow.match(/npm install --global --prefix "\$global_prefix"/g) ?? []).length,
1,
);
assert.equal(
(publishWorkflow.match(/npm install --global --prefix "\$global_prefix"/g) ?? []).length,
2,
);
for (const workflow of [e2eWorkflow, publishWorkflow]) {
assert.match(workflow, /global_bin="\$global_prefix\/feynman\.cmd"/);
assert.match(workflow, /global_bin="\$global_prefix\/bin\/feynman"/);
assert.match(workflow, /test "\$\("\$global_bin" --version \| tail -1\)"/);
assert.match(workflow, /"\$global_bin" --help >\/dev\/null/);
}
assert.match(publishWorkflow, /global_prefix="\$RUNNER_TEMP\/published-global"/);
assert.match(publishWorkflow, /"@companion-ai\/feynman@\$VERSION"/);
});

test("package consumer matrices allow bounded Windows global-install time", () => {
const candidateConsumerJob = e2eWorkflow.match(
/\n supported-node-consumers-pr:[\s\S]*?(?=\n windows-native-installer-pr:)/,
);
assert.ok(candidateConsumerJob, "PR workflow must define the candidate consumer job");
assert.match(candidateConsumerJob[0], /\n timeout-minutes: 45\n/);

const releaseConsumerJob = publishWorkflow.match(
/\n verify-package-consumers:[\s\S]*?(?=\n publish-npm:)/,
);
assert.ok(releaseConsumerJob, "publish workflow must define the package consumer job");
assert.match(releaseConsumerJob[0], /\n timeout-minutes: 45\n/);
});

test("publish uses the exact verified tarball after native bundles pass", () => {
assert.match(publishWorkflow, /concurrency:\s*\n\s+group: publish-/);
assert.match(publishWorkflow, /workflow_dispatch:/);
Expand Down
10 changes: 10 additions & 0 deletions website/src/content/docs/reference/releases.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,16 @@ This page summarizes what changed in recent Feynman releases. GitHub releases us

## Unreleased

## v0.3.7 - 2026-07-28

### Reliability

- Fixed `npm install -g @companion-ai/feynman` producing an unusable CLI when npm left the direct OpenTelemetry API hoist target empty beside Feynman's bundled Pi packages. The package now bundles its exact direct telemetry API so global installs can start reliably.

### Validation

- Added clean global-install version and help smokes to every supported Linux, macOS, Windows, and Node `22`/`24`/`25` package-consumer gate, plus post-publication verification of the registry package.

## v0.3.6 - 2026-07-28

- Raised the npm-install Node 22 floor to `22.22.0`, matching the direct telemetry runtime's actual engine contract; standalone installers continue to bundle Node `24.18.0`.
Expand Down