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
1 change: 1 addition & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ jobs:
run: |
python3 -m unittest scripts/test_verify_real_repo_lane_scale.py -v
python3 -m unittest scripts/test_check_real_repo_lane_scale.py -v
python3 -m unittest scripts/test_check_real_framework_handoff.py -v
- name: Check real-repository harness syntax
run: bash -n scripts/verify-real-repo-lane-scale.sh

Expand Down
65 changes: 65 additions & 0 deletions .github/workflows/layered-workspaces.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,9 @@ on:
- "scripts/verify-linux-command-recipe-sandbox.sh"
- "scripts/verify-windows-command-recipe-sandbox.ps1"
- "scripts/verify-macos-nfs-framework-layers.sh"
- "scripts/verify-real-framework-handoff.sh"
- "scripts/check-real-framework-handoff.py"
- "scripts/test_check_real_framework_handoff.py"
- "scripts/verify-environment-adapter-plugin.sh"
- "scripts/verify-artifact-adapter-conformance.sh"
- "scripts/verify-artifact-real-tool-gates.sh"
Expand All @@ -37,6 +40,11 @@ on:
required: false
default: false
type: boolean
run_real_framework_handoffs:
description: "Qualify pinned Go, pnpm, npm, Python, and CMake repositories through A -> B -> C macOS NFS lanes"
required: false
default: false
type: boolean

jobs:
core:
Expand Down Expand Up @@ -232,6 +240,63 @@ jobs:
- uses: Swatinem/rust-cache@v2
- run: scripts/verify-macos-nfs-framework-layers.sh

real-framework-candidate:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_real_framework_handoffs }}
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: dtolnay/rust-toolchain@stable
- uses: Swatinem/rust-cache@v2
with:
key: real-framework-candidate
- name: Build candidate Trail once
run: cargo build -p trail --release --locked
env:
CARGO_TARGET_DIR: ${{ runner.temp }}/trail-real-framework-target
- uses: actions/upload-artifact@v4
with:
name: trail-real-framework-candidate-${{ github.sha }}
path: ${{ runner.temp }}/trail-real-framework-target/release/trail
if-no-files-found: error

real-framework-handoffs:
needs: real-framework-candidate
strategy:
fail-fast: false
matrix:
framework: [go, pnpm, npm, python, cmake]
runs-on: macos-latest
steps:
- uses: actions/checkout@v4
- uses: actions/download-artifact@v4
with:
name: trail-real-framework-candidate-${{ github.sha }}
path: ${{ runner.temp }}/trail-real-framework-candidate
- name: Restore candidate executable permission
run: chmod 0755 "${{ runner.temp }}/trail-real-framework-candidate/trail"
- if: ${{ matrix.framework == 'go' }}
uses: actions/setup-go@v5
with:
go-version: "1.26.x"
- if: ${{ matrix.framework == 'pnpm' || matrix.framework == 'npm' }}
uses: actions/setup-node@v4
with:
node-version: "22"
- if: ${{ matrix.framework == 'pnpm' }}
run: corepack enable && corepack prepare pnpm@10.14.0 --activate
- name: Qualify ${{ matrix.framework }} A -> B -> C handoff
run: scripts/verify-real-framework-handoff.sh "${{ matrix.framework }}"
env:
TRAIL_BIN: ${{ runner.temp }}/trail-real-framework-candidate/trail
TRAIL_FRAMEWORK_EVIDENCE_DIR: ${{ runner.temp }}/trail-real-framework-${{ matrix.framework }}
TRAIL_FRAMEWORK_WORK_ROOT: ${{ runner.temp }}/trail-real-framework-work-${{ matrix.framework }}
- uses: actions/upload-artifact@v4
if: ${{ always() }}
with:
name: trail-real-framework-${{ matrix.framework }}
path: ${{ runner.temp }}/trail-real-framework-${{ matrix.framework }}
if-no-files-found: error

dokan-conformance:
if: ${{ github.event_name == 'workflow_dispatch' && inputs.run_dokan_conformance }}
runs-on: windows-latest
Expand Down
23 changes: 23 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,20 @@ All notable changes to Trail are documented in this file. Trail follows

### Fixed

- Managed lane commands now derive fixed policy, resolved executable, cache,
and output bindings from each active environment adapter instead of injecting
Cargo/npm defaults globally. Go, pnpm/npm/Yarn/Bun, Python, and CMake commands
receive isolated framework-native caches and exact tool paths, while inactive
frameworks no longer leak variables into the command.
- Node dependency executables and CMake build trees now bind directly from the
lane's generated upper, avoiding metadata-heavy build/dependency traversal
through macOS NFS while preserving a mounted source path and lane-private
mutation. Python also exposes its path-correct interpreter through
`TRAIL_VENV_PYTHON`.
- macOS NFS lane mounts now retain attributes and negative lookups for up to 60
seconds within a mounted execution. Same-client mutations still invalidate
cached entries and synchronous writes remain enabled, while unchanged Go and
Node source/dependency walks avoid repeated userspace NFS round trips.
- Lane/root diff addition and deletion totals now come from the emitted text
diff rather than stable-line identity churn, so statistics agree with the
unified patch while line-identity inspection remains available separately.
Expand Down Expand Up @@ -35,6 +49,11 @@ All notable changes to Trail are documented in this file. Trail follows
clone/reflink, Cargo revalidates the seed and recompiles affected workspace
code, and lockfile, manifest, toolchain, target, platform, or build-policy
changes still force an unseeded construction.
- Built-in Node dependency layers produced by a lockfile-frozen,
script-disabled install now allow public private-key example literals in
ordinary documentation, source, and type declarations. Strict scanning still
rejects secret-bearing paths such as `.env`, credential, `.pem`, and `.key`
files; custom or script-enabled producers receive no exemption.
- `trail env ... --path .` now selects the repository-root component instead
of failing path normalization.
- Changed-path daemon authority now follows workspace generation changes and
Expand All @@ -45,6 +64,10 @@ All notable changes to Trail are documented in this file. Trail follows

### Added

- Added an opt-in macOS real-framework qualification matrix for pinned bbolt,
date-fns, uuid, httpx, and LevelDB revisions. Each row produces checksummed
Agent A → B → C evidence for source-only handoff, parent-generation
inheritance, framework checks, cache/layer reuse, and lane-private outputs.
- Added deterministic 10k/100k/1M artifact and source scale matrices for
1/5/20 lanes, fail-closed JSON evidence validation, and compositional
owning-host NFS/FUSE/Dokan qualification that distinguishes mounted backend
Expand Down
8 changes: 8 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -200,6 +200,14 @@ seed and recompiles affected workspace code; Trail never treats the predecessor
as the final artifact for the new source root. Manifest, lockfile, toolchain,
target, platform, or build-policy changes remain hard compatibility misses.

Managed command bindings are adapter-owned rather than Cargo-specific. Active
Go components receive shared module/build caches and an exact `TRAIL_GO`;
Node components receive package-manager caches plus a direct private
`TRAIL_NODE_MODULES`; Python components receive a lane-private `VIRTUAL_ENV`
and `TRAIL_VENV_PYTHON`; and CMake components receive a direct lane-private
`TRAIL_CMAKE_BUILD_DIR`. Inactive frameworks inject no cache, tool, or output
variables into the command.

```sh
trail env discover fix-login
trail env plan fix-login
Expand Down
13 changes: 11 additions & 2 deletions docs/design/environment-adapter-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,9 @@ generation activation. The first built-ins are:
graph-aware multi-module adapter is available;
- `trail/cmake-build@1`: provisions a `writable_private` build tree with no synthetic
shared layer. Configure is deliberately deferred until execution inside the mounted
lane so absolute paths in `CMakeCache.txt` name the stable lane workdir rather than a
disposable staging directory;
lane so its source path names the stable lane workdir rather than disposable staging;
the writable build directory is bound directly from the generated upper so configure,
compilation, and tests do not route build-tree metadata through NFS/FUSE/Dokan;
- `trail/python-venv@1`: recognizes `pyproject.toml` and the common uv, Poetry, PDM,
Pipenv, and requirements lock/manifest files, provisions a layer-free lane-private
`.venv`, and keys it by every present dependency file plus the resolved Python
Expand Down Expand Up @@ -57,6 +58,14 @@ scope. Host cache storage paths are rewritten to logical cache names before hash
moving `.trail` storage cannot change artifact correctness identity. This projection
does not advertise or invoke plugin protocol v3 and grants no v3-only capability.

Ordinary managed commands receive bindings declared by each active built-in adapter:
fixed policy values, resolved absolute tools, cache namespace subpaths, and generated
outputs. Direct output bindings are limited to lane-private or private-seeded policies;
they never expose a shared immutable layer for mutation. Binding-name collisions fail
closed, and an inactive adapter contributes nothing. This keeps command execution
framework-neutral while allowing Cargo targets, Node dependency trees, and CMake build
trees to bypass metadata-heavy filesystem transports safely.

The mapping deliberately preserves adapter semantics: Go's module/build stores remain
performance-only while its vendor output remains an immutable private seed; CMake's
path-bound build tree remains writable-private with no layer; OCI images and services
Expand Down
9 changes: 9 additions & 0 deletions docs/design/guardrails-security-and-redaction.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,15 @@ rejects credential-like content, protected paths, escaping links, special
files, unsafe modes/xattrs, case collisions, concurrent mutation, and declared
entry/byte/depth limit violations before publishing an envelope.

One narrow content-scanning distinction applies to the built-in Node adapter.
When a dependency tree is produced from a frozen lockfile with lifecycle
scripts disabled and a cleared environment, public example literals in ordinary
documentation, source, and type declarations do not taint the tree merely
because they spell a private-key marker. Secret-bearing paths (`.env`,
credentials, `.pem`, `.key`, and their protected equivalents) remain rejected.
Custom adapters, script-enabled installs, private-output promotion, and every
other artifact producer retain strict scanning.

```mermaid
flowchart TB
Input["User, agent, CLI, HTTP, or MCP request"]
Expand Down
13 changes: 13 additions & 0 deletions docs/design/layered-lane-workspaces.md
Original file line number Diff line number Diff line change
Expand Up @@ -738,6 +738,19 @@ The compiler cache is the primary cross-branch reuse mechanism. Target seeds
are an optimization and may be disabled if toolchain behavior or absolute-path
inputs make them unreliable.

### Managed command bindings

The workspace host does not inject Cargo or npm variables unconditionally.
Each active built-in adapter declares its fixed policy values, resolved tools,
cache namespace paths, and output paths. Go receives `GOMODCACHE`, `GOCACHE`,
and `TRAIL_GO`; Node receives manager caches and a direct lane-private
`TRAIL_NODE_MODULES`; Python receives its mounted `VIRTUAL_ENV` and
`TRAIL_VENV_PYTHON`; CMake receives a direct writable-private
`TRAIL_CMAKE_BUILD_DIR`. Cargo retains direct private `CARGO_TARGET_DIR` and
managed `CARGO_HOME`/compiler-cache bindings. Output bindings may bypass the
mounted transport only for private-seeded, writable-private, or disposable
state, never for a live shared immutable directory.

### Generic adapters

A generic cache profile can declare:
Expand Down
32 changes: 32 additions & 0 deletions docs/guides/performance-and-scale-benchmarks.md
Original file line number Diff line number Diff line change
Expand Up @@ -118,6 +118,38 @@ TRAIL_SCALE_LABEL=manual-scale \
scripts/cli-scale-bench.sh
```

## Real-Framework A → B → C Gate

Framework adapters have a separate opt-in macOS qualification matrix. Each row
fetches an exact upstream revision, uses the candidate Trail binary, and moves
three native NFS lanes through a source edit, environment synchronization, and
real framework check. The gate covers bbolt/Go, date-fns/pnpm, uuid/npm,
httpx/Python virtual environments, and LevelDB/CMake:

```sh
TRAIL_BIN=/absolute/path/to/trail \
TRAIL_FRAMEWORK_EVIDENCE_DIR=/absolute/new/evidence/go \
TRAIL_FRAMEWORK_WORK_ROOT=/absolute/new/work/go \
scripts/verify-real-framework-handoff.sh go
```

Valid selectors are `go`, `pnpm`, `npm`, `python`, and `cmake`. The output
and optional work directories must not exist. When omitted, the work directory
defaults to `<evidence>.work` and remains outside the uploaded evidence set.
`evidence.json` pins the repository/revision, three
distinct source roots, active component keys, layer identities, cache
namespaces, lane-private output identities, and SHA-256 digests of every raw
Trail report. The checker requires each edit to checkpoint only `README.md`,
each child to inherit its parent's active generation before editing, and every
framework command to pass. Go additionally requires compatible-predecessor
seeding with nonzero avoided bytes; npm/pnpm require one exact immutable layer;
Python and CMake require three distinct writable-private outputs.

Dispatch `layered-workspaces.yml` with
`run_real_framework_handoffs=true` to run all five rows on clean macOS hosts
and upload their complete evidence directories. Synthetic tests or a skipped
matrix row are not production-readiness evidence for that framework.

## Real-Repository Concurrent Lane Gate

Use the blocking real-repository harness for release qualification of native-COW
Expand Down
34 changes: 32 additions & 2 deletions docs/lanes/large-repository-environments.md
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@ Use `immutable_shared` for read-only generated content,
`writable_private` for persistent lane-only state, and `disposable` for scratch.
Never model a live mutable tree as shared.

## Executable Cargo and Node examples
## Executable Cargo, Go, Node, Python, and CMake examples

The built-in adapters need no framework-specific Trail configuration. In a
Git-tracked Rust repository with `Cargo.toml` and `Cargo.lock`:
Expand Down Expand Up @@ -85,9 +85,39 @@ trail lane exec node-b -- npm test
```

The immutable dependency lower is referenced by identity while consumer writes
go to each lane's private upper. Package-manager caches are performance-only
go to each lane's private upper. Trail exposes that upper directly through
`TRAIL_NODE_MODULES` and `NODE_PATH`, prepends its `.bin` directory to `PATH`,
and binds the selected package manager through `TRAIL_NPM`, `TRAIL_PNPM`,
`TRAIL_YARN`, or `TRAIL_BUN`. This avoids metadata-heavy dependency traversal
through the mounted source view. Package-manager caches are performance-only
namespaces; they are never accepted as dependency correctness evidence.

The other built-ins use the same lane handoff:

```sh
# Go: a shared module/build cache plus a lane-private vendor seed.
trail env sync all agent-a
trail lane exec agent-a -- sh -c '"$TRAIL_GO" test ./...'

# Python: a lane-private, path-correct virtual environment.
trail env sync all agent-a
trail lane exec agent-a -- sh -c '"$TRAIL_VENV_PYTHON" -m compileall -q .'

# CMake: configure and build outside the NFS/FUSE/Dokan transport.
trail env sync all agent-a
trail lane exec agent-a -- sh -c '
"$TRAIL_CMAKE" -S . -B "$TRAIL_CMAKE_BUILD_DIR"
"$TRAIL_CMAKE" --build "$TRAIL_CMAKE_BUILD_DIR" --parallel
'
```

Go binds `GOMODCACHE`, `GOCACHE`, `TRAIL_GO`, and a local-toolchain/offline
vendor policy. Python binds `PIP_CACHE_DIR`, `UV_CACHE_DIR`, `VIRTUAL_ENV`,
`TRAIL_VENV_PYTHON`, and the venv executable directory. CMake binds the exact
host executable as `TRAIL_CMAKE` and a lane-private direct build path as
`TRAIL_CMAKE_BUILD_DIR`. A login shell may replace `PATH`; use the absolute
`TRAIL_*` executable variables in automated lane commands.

The framework-neutral TOML above is executable as `trail.environment.toml`.
Create its declared input, record it, and run:

Expand Down
Loading
Loading