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
56 changes: 56 additions & 0 deletions .github/workflows/artifact-acceptance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
name: Artifact Acceptance

on:
pull_request:
workflow_dispatch:

permissions:
contents: read
actions: read

concurrency:
group: artifact-acceptance-${{ github.ref }}
cancel-in-progress: true

jobs:
prepare:
runs-on: ubuntu-24.04
timeout-minutes: 20
outputs:
commit: ${{ steps.source.outputs.commit }}
steps:
- name: Checkout candidate
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
persist-credentials: false
- name: Resolve candidate commit
id: source
run: echo "commit=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.19.0'
- name: Install pinned npm and locked dependencies
run: |
npm install -g npm@11.16.0 --ignore-scripts
npm ci --ignore-scripts
- name: Freeze and build reviewed candidate artifacts
run: |
npm run hydrate:model-data
node scripts/release-artifacts.mjs freeze .artifacts/release
npm run build:offline
node scripts/release-artifacts.mjs prepare .artifacts/release
- name: Retain exact private release bytes
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: private-release-${{ steps.source.outputs.commit }}
path: .artifacts/release/
include-hidden-files: true
if-no-files-found: error
retention-days: 14

native-acceptance:
needs: prepare
uses: ./.github/workflows/native-artifact-acceptance.yml
with:
source_commit: ${{ needs.prepare.outputs.commit }}
11 changes: 10 additions & 1 deletion .github/workflows/build-binaries.yml
Original file line number Diff line number Diff line change
Expand Up @@ -126,8 +126,17 @@ jobs:
include-hidden-files: true
retention-days: 90

publish-npm:
native-acceptance:
needs: [resolve-source, prepare-artifacts]
permissions:
contents: read
actions: read
uses: ./.github/workflows/native-artifact-acceptance.yml
with:
source_commit: ${{ needs.resolve-source.outputs.commit }}

publish-npm:
needs: [resolve-source, prepare-artifacts, native-acceptance]
runs-on: ubuntu-latest
environment: npm-publish
permissions:
Expand Down
64 changes: 64 additions & 0 deletions .github/workflows/native-artifact-acceptance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
name: Native Artifact Acceptance

on:
workflow_call:
inputs:
source_commit:
required: true
type: string

permissions:
contents: read
actions: read

jobs:
accept:
timeout-minutes: 30
strategy:
fail-fast: false
matrix:
runner: [ubuntu-24.04, ubuntu-24.04-arm, macos-15, macos-15-intel]
runs-on: ${{ matrix.runner }}
env:
DIFFEX_SOURCE_COMMIT: ${{ inputs.source_commit }}
steps:
- name: Checkout exact artifact source
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
ref: ${{ inputs.source_commit }}
persist-credentials: false
- name: Setup Node
uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
with:
node-version: '22.19.0'
- name: Setup Bun package manager
uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2
with:
bun-version: '1.3.11'
- name: Install pinned npm
run: npm install -g npm@11.16.0 --ignore-scripts
- name: Install controlled Linux search helpers
if: runner.os == 'Linux'
run: |
sudo apt-get update
sudo apt-get install -y fd-find ripgrep
sudo ln -s "$(command -v fdfind)" /usr/local/bin/fd
- name: Install controlled Darwin search helpers
if: runner.os == 'macOS'
run: brew install fd ripgrep
- name: Download exact retained tarballs
env:
GH_TOKEN: ${{ github.token }}
run: gh run download "$GITHUB_RUN_ID" --repo diffexai/diffex --name "private-release-$DIFFEX_SOURCE_COMMIT" --dir .artifacts/release
- name: Verify frozen source and archive bytes
run: node scripts/release-artifacts.mjs verify .artifacts/release
- name: Accept all packages and process boundaries outside the workspace
run: node scripts/artifact-acceptance.mjs .artifacts/release .artifacts/acceptance
- name: Retain private native acceptance evidence
if: always()
uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2
with:
name: native-acceptance-${{ matrix.runner }}-${{ inputs.source_commit }}
path: .artifacts/acceptance/
if-no-files-found: error
retention-days: 90
85 changes: 85 additions & 0 deletions docs/release/artifact-acceptance.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
# Local package and native acceptance

The release gate installs every package returned by `scripts/release-packages.mjs` from its reviewed local tarball.
Each package receives a separate consumer with only its public dependency closure, ordered before the package that requires it.
Consumers live outside the repository in paths containing spaces, with separate homes, configuration, projects, temporary files, and package prefixes.
Each manager starts with an empty registry cache shared only among consumers in that acceptance run, and Bun registry concurrency is bounded.
Installed file identities are rechecked in every consumer even when downloaded dependencies are cached.
Lifecycle scripts are disabled and the Diffex registry scope points to an unavailable loopback endpoint, so a missing local override fails instead of fetching an older Diffex package.
External npm dependencies still resolve from the public registry.

Declared runtime exports, wildcard exports, declarations, main files, and executable targets must exist inside the installed package.
Runtime imports and representative operations execute in fresh Node processes.
Installed runtime inventories must equal the inventory extracted from the checked tarball, and every recorded installed file must match its SHA-256 digest.
The operation inventory fails closed if a new public package has no representative operation.

| Package | Representative operation |
| --- | --- |
| `diffex-agent-core` | Construct an agent and change its state |
| `diffex-ai` | Complete and consume an assistant event stream |
| `diffex-client` | Construct and dispose a client; additionally list a hosted session over WebSocket |
| `diffex` | Initialize model runtime, load clipboard native module, execute image WASM, export a session to HTML |
| `diffex-coding-agent-server` | Close usage reporter; start its actual executable, reject wrong credentials, list its session, terminate cleanly |
| `diffex-harness-state` | Persist and reload the initial harness version |
| `diffex-protocol` | Encode and decode CBOR |
| `diffex-server` | Start and close a loopback WebSocket listener |
| `diffex-session-backend-sqlite-node` | Create, write, and query SQLite |
| `diffex-telemetry` | Execute a telemetry span callback |
| `diffex-tui` | Render text and load the current platform's native modifier helper where applicable |

The packed CLI additionally executes help, version, model listing, controlled inference, a terminal prompt/reply, account-menu inspection, and clean exit.
The terminal path exercises packaged themes and the HTML export path exercises its templates, CSS, and bundled renderer resources.
Clipboard coverage loads the native module without reading or replacing the user's clipboard.
Socket guards reject non-loopback process traffic; the terminal fixture supplies the known no-release HTTP response and fails on unexpected socket attempts.
No provider credentials are inherited.
The controlled environment must supply native `fd` and `ripgrep` executables on PATH.
Acceptance executes file discovery and content search, records their versions, and keeps automatic helper downloads blocked.
Native runners install those prerequisites through apt or Homebrew before package acceptance.
This validates the npm installation with controlled system helpers; it does not establish the separate automatic-helper-download lifecycle.

## Supported distribution scope

The approved channel remains npm, installed with npm or Bun and executed on Node 22.19.0 or newer.
Bun package installation is distinct from a Bun standalone executable.
Standalone publication remains deferred for every platform under the approved redistribution policy, and the manifest advertises no binary assets or binary platforms.
Windows npm targets are now explicitly deferred until equivalent terminal acceptance is available.
The advertised npm targets are Darwin arm64/x64 and Linux arm64/x64.
The reusable native workflow uses clean native runners for those four targets, downloads the same retained tarballs, verifies their source and checksums, and runs the complete acceptance command before npm publication can proceed.
Runner labels follow the [GitHub-hosted runner reference](https://docs.github.com/en/actions/reference/runners/github-hosted-runners).

## Commands and evidence

Build, pack, and accept the current local candidate:

```sh
npm run release:local -- --out '/tmp/diffex local release'
```

Accept existing reviewed artifacts without rebuilding or repacking:

```sh
node scripts/artifact-acceptance.mjs /path/to/artifacts /path/to/private-evidence
```

The command accepts both the local manifest/tarballs layout and the frozen release artifact layout.
`acceptance.json` records the source commit, OS, architecture, Node and package-manager versions, each package's imports and operation result, exact tarball checksums, process outcomes, and cleanup.
A failed run records failure and the completed subset; it cannot produce a passing overall status.
Terminal transcripts and smoke events are retained beside it in private evidence storage.
Every test-owned consumer, session, cache, configuration directory, and server process is removed, including on failure.

Verify final tarball bytes from their directory using the retained checksum list:

```sh
shasum -a 256 -c /path/to/private-evidence/SHA256SUMS
```

On Linux, `sha256sum -c` accepts the same checksum file.
Regression coverage alters archive bytes and confirms verification fails before installation.
The release workflow retains the source snapshot and manifests privately, while npm publication supplies registry integrity and provenance through the existing publisher.
Public acceptance must compare the actual downloaded npm bytes with these retained identities after separately authorized publication.

The baseline CLI-only acceptance passed on Darwin arm64 but emitted no per-package operation or hosted-server result.
The expanded local acceptance covers all eleven packages for both managers and the server lifecycle.
The owner specified that no live provider test environment is available, so real login and inference remain pending.
Native runner results and exact committed-candidate evidence are recorded in the implementation pull request and accompanying acceptance record.
Public downloads and public-to-public upgrades remain unexecuted until publication; local fixture inference does not establish those results.
Loading
Loading