The archive step's promise is that identical committed sources yield byte-identical archives on any machine. In a linked worktree (git worktree add) the promise quietly inverts: the same commit produces different bytes than a plain checkout, with no warning that anything degraded.
Describe the Bug
With mtime: git (or epoch), building from a plain checkout stamps entries with real commit timestamps. Building the same commit from a linked worktree stamps every entry with the 1980-01-01 fallback, so the two archives differ byte-for-byte and any hash-based consumer (S3 etags, SSM package manifests, lockfiles) sees a phantom change.
Expected Behavior
The docs scope the fallback to operating "outside a Git repository" or untracked paths. A linked worktree is inside a git repository (git log works normally there), so I expected commit timestamps, matching the plain checkout.
Actual Behavior
Every entry gets the fallback. unzip -l on two builds of the same commit:
=== control: plain checkout ===
6 07-28-2026 17:48 a.sh
=== trial: linked worktree of the same commit ===
6 12-31-1979 18:00 a.sh
(The trial row is 1980-01-01 00:00 UTC rendered in local time.)
Steps to Reproduce
Self-contained script and one fresh run (Atmos 1.224.1, darwin/arm64)
#!/usr/bin/env bash
# Repro: archive step mtime git/epoch silently fall back to 1980-01-01
# inside a linked git worktree, while a plain checkout of the same commit
# resolves real commit timestamps.
set -euo pipefail
ATMOS="${ATMOS:-atmos}"
tmp="$(mktemp -d)"
trap 'rm -rf "$tmp"' EXIT
cd "$tmp"
"$ATMOS" version
git init -q repo
cd repo
mkdir src
printf 'hello\n' > src/a.sh
git add src/a.sh
git -c user.name=repro -c user.email=repro@example.invalid -c commit.gpgsign=false \
commit -qm 'add a.sh'
git log -1 --format='committed: %cI'
cat > atmos.yaml <<'YAML'
base_path: ""
components: { terraform: { base_path: "components" } }
stacks: { base_path: "stacks", included_paths: ["**/*"], name_pattern: "{stage}" }
workflows: { base_path: "workflows" }
YAML
mkdir -p workflows stacks
cat > workflows/repro.yaml <<'YAML'
workflows:
build:
steps:
- name: pkg
type: archive
source: src
destination: out.zip
format: zip
action: replace
mtime: git
YAML
echo "=== control: plain checkout ==="
"$ATMOS" workflow build -f repro
unzip -l out.zip | grep 'a.sh'
echo "=== trial: linked worktree of the same commit ==="
git worktree add -q ../wt HEAD
cp atmos.yaml ../wt/
cp -R workflows ../wt/
mkdir -p ../wt/stacks
cd ../wt
"$ATMOS" workflow build -f repro
unzip -l out.zip | grep 'a.sh'
Transcript:
👽 Atmos 1.224.1 on darwin/arm64
committed: 2026-07-28T17:48:06-05:00
=== control: plain checkout ===
6 07-28-2026 17:48 a.sh
=== trial: linked worktree of the same commit ===
6 12-31-1979 18:00 a.sh
Mechanism
newMtimeConfig is deliberately fail-open: any error locating the repository or its history falls back to mtimeFallbackEpoch, which is the right call for temp build dirs and untracked files but also swallows this case. In a linked worktree .git is a gitdir pointer file rather than a directory; go-git's linked-worktree support looks partial (go-git/go-git#1812 reviewed the API for opening them), and somewhere along PlainOpenWithOptions(DetectDotGit) / Worktree() / Head() the resolution fails, so the fallback engages with no signal. This may be the archive-step sibling of the worktree gaps #1509 addresses for describe affected.
Relevant paths: pkg/archive/mtime.go (newMtimeConfig, modTimeFor, mtimeFallbackEpoch).
Ask
Could newMtimeConfig resolve the gitdir pointer for linked worktrees, or at least log a warning when a .git entry exists but git resolution falls back to the epoch? The silent divergence is the painful part; a build that warns is easy to live with. Happy to follow up with a PR.
Environment: Atmos 1.224.1 (pkg/archive/mtime.go is unchanged on main), darwin/arm64, git version 2.55.0.
The archive step's promise is that identical committed sources yield byte-identical archives on any machine. In a linked worktree (
git worktree add) the promise quietly inverts: the same commit produces different bytes than a plain checkout, with no warning that anything degraded.Describe the Bug
With
mtime: git(orepoch), building from a plain checkout stamps entries with real commit timestamps. Building the same commit from a linked worktree stamps every entry with the 1980-01-01 fallback, so the two archives differ byte-for-byte and any hash-based consumer (S3 etags, SSM package manifests, lockfiles) sees a phantom change.Expected Behavior
The docs scope the fallback to operating "outside a Git repository" or untracked paths. A linked worktree is inside a git repository (
git logworks normally there), so I expected commit timestamps, matching the plain checkout.Actual Behavior
Every entry gets the fallback.
unzip -lon two builds of the same commit:(The trial row is 1980-01-01 00:00 UTC rendered in local time.)
Steps to Reproduce
Self-contained script and one fresh run (Atmos 1.224.1, darwin/arm64)
Transcript:
Mechanism
newMtimeConfigis deliberately fail-open: any error locating the repository or its history falls back tomtimeFallbackEpoch, which is the right call for temp build dirs and untracked files but also swallows this case. In a linked worktree.gitis a gitdir pointer file rather than a directory; go-git's linked-worktree support looks partial (go-git/go-git#1812 reviewed the API for opening them), and somewhere alongPlainOpenWithOptions(DetectDotGit)/Worktree()/Head()the resolution fails, so the fallback engages with no signal. This may be the archive-step sibling of the worktree gaps #1509 addresses fordescribe affected.Relevant paths:
pkg/archive/mtime.go(newMtimeConfig,modTimeFor,mtimeFallbackEpoch).Ask
Could
newMtimeConfigresolve the gitdir pointer for linked worktrees, or at least log a warning when a.gitentry exists but git resolution falls back to the epoch? The silent divergence is the painful part; a build that warns is easy to live with. Happy to follow up with a PR.Environment: Atmos 1.224.1 (
pkg/archive/mtime.gois unchanged onmain), darwin/arm64, git version 2.55.0.