Removed legacy interfaces and old documentation references to the previous on-disk layout
This commit is contained in:
@@ -60,14 +60,6 @@ func runPostArchiveCleanup(ctx context.Context, env *Env, manifestPath string, m
|
||||
strings.TrimSpace(env.Config.Session.SessionID),
|
||||
strings.TrimSpace(m.RunID),
|
||||
)
|
||||
if info, err := os.Stat(workDir); err != nil || !info.IsDir() {
|
||||
workDir = artifacts.SessionRunWorkDir(
|
||||
env.Config.Pipeline.Workspace.Root,
|
||||
strings.TrimSpace(env.Config.Session.Campaign),
|
||||
strings.TrimSpace(env.Config.Session.SessionID),
|
||||
strings.TrimSpace(m.RunID),
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
if spoolRequested {
|
||||
|
||||
@@ -210,7 +210,7 @@ func TestPostArchiveCleanupNotRunWhenPromotionIsMissing(t *testing.T) {
|
||||
assertExists(t, seed.spoolAudioDir)
|
||||
assertExists(t, seed.runWorkDir)
|
||||
assertExists(t, filepath.Join(seed.runWorkDir, "manifest.json"))
|
||||
assertExists(t, artifacts.SessionRunWorkDir(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, runID))
|
||||
assertExists(t, artifacts.SessionRunRootForCampaign(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, runID))
|
||||
}
|
||||
|
||||
func TestPostArchiveCleanupNotRunWhenCurrentManifestUploadFails(t *testing.T) {
|
||||
@@ -271,8 +271,8 @@ func cleanupFixtureConfig(t *testing.T) (*config.Config, cleanupSeed) {
|
||||
cfg.Pipeline.Spool.Root = filepath.Join(t.TempDir(), "spool")
|
||||
|
||||
runID := "20260516T010203Z-1a2b3c4d"
|
||||
runWorkDir := artifacts.SessionRunWorkDir(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, runID)
|
||||
otherRunDir := artifacts.SessionRunWorkDir(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, "20260516T010204Z-5e6f7a8b")
|
||||
runWorkDir := artifacts.SessionRunRootForCampaign(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, runID)
|
||||
otherRunDir := artifacts.SessionRunRootForCampaign(cfg.Pipeline.Workspace.Root, cfg.Session.Campaign, cfg.Session.SessionID, "20260516T010204Z-5e6f7a8b")
|
||||
spoolAudioDir := artifacts.SessionSpoolAudioDir(cfg.Pipeline.Spool.Root, cfg.Session.Campaign, cfg.Session.SessionID, runID)
|
||||
|
||||
mustWriteFile(t, filepath.Join(runWorkDir, "manifest.json"), "{}\n")
|
||||
|
||||
@@ -84,12 +84,11 @@ func Resume(ctx context.Context, args []string, out io.Writer) error {
|
||||
}
|
||||
|
||||
func loadManifestIfPresent(ctx context.Context, cfg *config.Config) (*manifest.Manifest, error) {
|
||||
localStore := artifacts.NewLocalStore(cfg.Pipeline.Workspace.Root)
|
||||
paths, err := localStore.ResolveSessionPathsFor(cfg.Session.Campaign, cfg.Session.SessionID)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("resolve session workspace paths: %w", err)
|
||||
}
|
||||
path := paths.ManifestPath
|
||||
path := artifacts.SessionManifestPathForCampaign(
|
||||
cfg.Pipeline.Workspace.Root,
|
||||
cfg.Session.Campaign,
|
||||
cfg.Session.SessionID,
|
||||
)
|
||||
exists, err := fileExists(path)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("check manifest %q: %w", path, err)
|
||||
|
||||
@@ -30,18 +30,13 @@ func NewLocalStore(workspaceRoot string) *LocalStore {
|
||||
return &LocalStore{WorkspaceRoot: workspaceRoot}
|
||||
}
|
||||
|
||||
// SessionPaths resolves legacy paths for a session workdir.
|
||||
func (s *LocalStore) SessionPaths(sessionID string) SessionPaths {
|
||||
return buildLegacySessionPaths(s.WorkspaceRoot, sessionID)
|
||||
}
|
||||
|
||||
// SessionPathsFor resolves canonical campaign-aware paths for a session workdir.
|
||||
func (s *LocalStore) SessionPathsFor(campaign, sessionID string) SessionPaths {
|
||||
return buildSessionPaths(s.WorkspaceRoot, campaign, sessionID)
|
||||
}
|
||||
|
||||
// ResolveSessionPathsFor resolves the active session path with legacy compatibility.
|
||||
func (s *LocalStore) ResolveSessionPathsFor(campaign, sessionID string) (SessionPaths, error) {
|
||||
// EnsureLayoutFor creates and verifies campaign-aware session layout.
|
||||
func (s *LocalStore) EnsureLayoutFor(campaign, sessionID string) (SessionPaths, error) {
|
||||
if strings.TrimSpace(s.WorkspaceRoot) == "" {
|
||||
return SessionPaths{}, fmt.Errorf("workspace root is required")
|
||||
}
|
||||
@@ -51,50 +46,10 @@ func (s *LocalStore) ResolveSessionPathsFor(campaign, sessionID string) (Session
|
||||
|
||||
campaign = strings.TrimSpace(campaign)
|
||||
if campaign == "" {
|
||||
return s.SessionPaths(sessionID), nil
|
||||
return SessionPaths{}, fmt.Errorf("campaign is required")
|
||||
}
|
||||
|
||||
canonical := s.SessionPathsFor(campaign, sessionID)
|
||||
legacy := s.SessionPaths(sessionID)
|
||||
canonicalExists, err := dirExists(canonical.Root)
|
||||
if err != nil {
|
||||
return SessionPaths{}, fmt.Errorf("check canonical session root %q: %w", canonical.Root, err)
|
||||
}
|
||||
legacyExists, err := dirExists(legacy.Root)
|
||||
if err != nil {
|
||||
return SessionPaths{}, fmt.Errorf("check legacy session root %q: %w", legacy.Root, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case canonicalExists && legacyExists:
|
||||
return SessionPaths{}, fmt.Errorf(
|
||||
"ambiguous session workspace roots for campaign %q session %q: canonical=%q legacy=%q",
|
||||
campaign,
|
||||
sessionID,
|
||||
canonical.Root,
|
||||
legacy.Root,
|
||||
)
|
||||
case canonicalExists:
|
||||
return canonical, nil
|
||||
case legacyExists:
|
||||
return legacy, nil
|
||||
default:
|
||||
return canonical, nil
|
||||
}
|
||||
}
|
||||
|
||||
// EnsureLayout creates and verifies the canonical session workdir directory layout.
|
||||
func (s *LocalStore) EnsureLayout(sessionID string) (SessionPaths, error) {
|
||||
return s.ensureLayout(s.SessionPaths(sessionID))
|
||||
}
|
||||
|
||||
// EnsureLayoutFor creates and verifies campaign-aware session layout, with controlled legacy compatibility.
|
||||
func (s *LocalStore) EnsureLayoutFor(campaign, sessionID string) (SessionPaths, error) {
|
||||
paths, err := s.ResolveSessionPathsFor(campaign, sessionID)
|
||||
if err != nil {
|
||||
return SessionPaths{}, err
|
||||
}
|
||||
return s.ensureLayout(paths)
|
||||
return s.ensureLayout(s.SessionPathsFor(campaign, sessionID))
|
||||
}
|
||||
|
||||
func (s *LocalStore) ensureLayout(paths SessionPaths) (SessionPaths, error) {
|
||||
@@ -129,15 +84,6 @@ func (s *LocalStore) ensureLayout(paths SessionPaths) (SessionPaths, error) {
|
||||
return paths, nil
|
||||
}
|
||||
|
||||
// CopyInput copies an input file into the session workdir under destRelativePath.
|
||||
func (s *LocalStore) CopyInput(sessionID, srcPath, destRelativePath string) (Ref, error) {
|
||||
paths, err := s.EnsureLayout(sessionID)
|
||||
if err != nil {
|
||||
return Ref{}, err
|
||||
}
|
||||
return s.copyInputWithPaths(paths, sessionID, srcPath, destRelativePath)
|
||||
}
|
||||
|
||||
// CopyInputFor copies an input file into the campaign-aware session workdir under destRelativePath.
|
||||
func (s *LocalStore) CopyInputFor(campaign, sessionID, srcPath, destRelativePath string) (Ref, error) {
|
||||
paths, err := s.EnsureLayoutFor(campaign, sessionID)
|
||||
@@ -248,15 +194,6 @@ func (s *LocalStore) Checksum(path string) (string, error) {
|
||||
return digest, nil
|
||||
}
|
||||
|
||||
// AcquireSessionLock acquires an exclusive lock file for a session workdir.
|
||||
func (s *LocalStore) AcquireSessionLock(sessionID string) (*LockHandle, error) {
|
||||
paths, err := s.EnsureLayout(sessionID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return s.acquireSessionLockForPaths(paths)
|
||||
}
|
||||
|
||||
// AcquireSessionLockFor acquires an exclusive lock file for a campaign/session workdir.
|
||||
func (s *LocalStore) AcquireSessionLockFor(campaign, sessionID string) (*LockHandle, error) {
|
||||
paths, err := s.EnsureLayoutFor(campaign, sessionID)
|
||||
@@ -290,17 +227,6 @@ func (s *LocalStore) acquireSessionLockForPaths(paths SessionPaths) (*LockHandle
|
||||
return &LockHandle{path: paths.LockPath, file: f}, nil
|
||||
}
|
||||
|
||||
func dirExists(path string) (bool, error) {
|
||||
info, err := os.Stat(path)
|
||||
if err == nil {
|
||||
return info.IsDir(), nil
|
||||
}
|
||||
if errors.Is(err, os.ErrNotExist) {
|
||||
return false, nil
|
||||
}
|
||||
return false, err
|
||||
}
|
||||
|
||||
// ReleaseSessionLock releases a previously acquired session lock.
|
||||
func (s *LocalStore) ReleaseSessionLock(lock *LockHandle) error {
|
||||
if lock == nil {
|
||||
|
||||
@@ -36,40 +36,14 @@ func TestEnsureLayoutCreatesExpectedDirectories(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSessionPathsForLegacyFallback(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
store := NewLocalStore(root)
|
||||
legacyRoot := SessionWorkDir(root, "session-1")
|
||||
if err := os.MkdirAll(legacyRoot, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll() error = %v", err)
|
||||
}
|
||||
|
||||
paths, err := store.ResolveSessionPathsFor("sample-campaign", "session-1")
|
||||
if err != nil {
|
||||
t.Fatalf("ResolveSessionPathsFor() error = %v", err)
|
||||
}
|
||||
if paths.Root != legacyRoot {
|
||||
t.Fatalf("paths.Root = %q, want legacy root %q", paths.Root, legacyRoot)
|
||||
}
|
||||
}
|
||||
|
||||
func TestResolveSessionPathsForAmbiguousRoots(t *testing.T) {
|
||||
root := t.TempDir()
|
||||
store := NewLocalStore(root)
|
||||
legacyRoot := SessionWorkDir(root, "session-1")
|
||||
canonicalRoot := SessionWorkDirForCampaign(root, "sample-campaign", "session-1")
|
||||
for _, dir := range []string{legacyRoot, canonicalRoot} {
|
||||
if err := os.MkdirAll(dir, 0o755); err != nil {
|
||||
t.Fatalf("MkdirAll(%q) error = %v", dir, err)
|
||||
}
|
||||
}
|
||||
|
||||
_, err := store.ResolveSessionPathsFor("sample-campaign", "session-1")
|
||||
func TestEnsureLayoutForRequiresCampaign(t *testing.T) {
|
||||
store := NewLocalStore(t.TempDir())
|
||||
_, err := store.EnsureLayoutFor("", "session-1")
|
||||
if err == nil {
|
||||
t.Fatal("expected ambiguity error, got nil")
|
||||
t.Fatal("expected campaign-required error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "ambiguous session workspace roots") {
|
||||
t.Fatalf("error = %v, want ambiguity message", err)
|
||||
if !strings.Contains(err.Error(), "campaign is required") {
|
||||
t.Fatalf("error = %v, want campaign-required error", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -27,11 +27,6 @@ type SessionPaths struct {
|
||||
LockPath string
|
||||
}
|
||||
|
||||
// SessionWorkDir returns the legacy work directory for one session.
|
||||
func SessionWorkDir(rootDir, sessionID string) string {
|
||||
return filepath.Join(rootDir, config.PathWorkDirSegment, sessionID)
|
||||
}
|
||||
|
||||
// SessionWorkDirForCampaign returns the canonical campaign-aware work directory for one session.
|
||||
func SessionWorkDirForCampaign(rootDir, campaign, sessionID string) string {
|
||||
return filepath.Join(rootDir, config.PathWorkDirSegment, campaign, sessionID)
|
||||
@@ -62,21 +57,11 @@ func SessionRunStageDirForCampaign(rootDir, campaign, sessionID, runID, stageNam
|
||||
return filepath.Join(SessionRunRootForCampaign(rootDir, campaign, sessionID, runID), stageName)
|
||||
}
|
||||
|
||||
// SessionRunWorkDir returns the legacy campaign/session/run scoped local work directory.
|
||||
func SessionRunWorkDir(rootDir, campaign, sessionID, runID string) string {
|
||||
return filepath.Join(rootDir, config.PathWorkDirSegment, campaign, sessionID, runID)
|
||||
}
|
||||
|
||||
// SessionSpoolAudioDir returns the campaign/session/run scoped local spool audio path.
|
||||
func SessionSpoolAudioDir(spoolRoot, campaign, sessionID, runID string) string {
|
||||
return filepath.Join(spoolRoot, campaign, sessionID, runID, config.PathAudioDirSegment)
|
||||
}
|
||||
|
||||
func buildLegacySessionPaths(workspaceRoot, sessionID string) SessionPaths {
|
||||
root := SessionWorkDir(workspaceRoot, sessionID)
|
||||
return buildSessionPathsFromRoot(workspaceRoot, "", sessionID, root)
|
||||
}
|
||||
|
||||
func buildSessionPaths(workspaceRoot, campaign, sessionID string) SessionPaths {
|
||||
root := SessionWorkDirForCampaign(workspaceRoot, campaign, sessionID)
|
||||
return buildSessionPathsFromRoot(workspaceRoot, campaign, sessionID, root)
|
||||
|
||||
@@ -5,15 +5,6 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSessionRunWorkDir(t *testing.T) {
|
||||
root := "/tmp/workspace"
|
||||
got := SessionRunWorkDir(root, "forsaken", "2026-04-19", "20260515T031522Z-a1b2c3d4")
|
||||
want := filepath.Join(root, "work", "forsaken", "2026-04-19", "20260515T031522Z-a1b2c3d4")
|
||||
if got != want {
|
||||
t.Fatalf("SessionRunWorkDir() = %q, want %q", got, want)
|
||||
}
|
||||
}
|
||||
|
||||
func TestSessionWorkDirForCampaign(t *testing.T) {
|
||||
root := "/tmp/workspace"
|
||||
got := SessionWorkDirForCampaign(root, "forsaken", "2026-04-19")
|
||||
|
||||
@@ -11,36 +11,16 @@ type S3Store struct {
|
||||
Prefix string
|
||||
}
|
||||
|
||||
// SessionPaths is not implemented for S3-backed storage.
|
||||
func (s *S3Store) SessionPaths(_ string) SessionPaths {
|
||||
return SessionPaths{}
|
||||
}
|
||||
|
||||
// SessionPathsFor is not implemented for S3-backed storage.
|
||||
func (s *S3Store) SessionPathsFor(_, _ string) SessionPaths {
|
||||
return SessionPaths{}
|
||||
}
|
||||
|
||||
// ResolveSessionPathsFor is not implemented for S3-backed storage.
|
||||
func (s *S3Store) ResolveSessionPathsFor(_, _ string) (SessionPaths, error) {
|
||||
return SessionPaths{}, fmt.Errorf("artifacts s3 resolve session paths: not yet implemented")
|
||||
}
|
||||
|
||||
// EnsureLayout returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) EnsureLayout(_ string) (SessionPaths, error) {
|
||||
return SessionPaths{}, fmt.Errorf("artifacts s3 ensure layout: not yet implemented")
|
||||
}
|
||||
|
||||
// EnsureLayoutFor returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) EnsureLayoutFor(_, _ string) (SessionPaths, error) {
|
||||
return SessionPaths{}, fmt.Errorf("artifacts s3 ensure layout for campaign/session: not yet implemented")
|
||||
}
|
||||
|
||||
// CopyInput returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) CopyInput(_, _, _ string) (Ref, error) {
|
||||
return Ref{}, fmt.Errorf("artifacts s3 copy input: not yet implemented")
|
||||
}
|
||||
|
||||
// CopyInputFor returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) CopyInputFor(_, _, _, _ string) (Ref, error) {
|
||||
return Ref{}, fmt.Errorf("artifacts s3 copy input for campaign/session: not yet implemented")
|
||||
@@ -66,11 +46,6 @@ func (s *S3Store) Checksum(_ string) (string, error) {
|
||||
return "", fmt.Errorf("artifacts s3 checksum: not yet implemented")
|
||||
}
|
||||
|
||||
// AcquireSessionLock returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) AcquireSessionLock(_ string) (*LockHandle, error) {
|
||||
return nil, fmt.Errorf("artifacts s3 acquire lock: not yet implemented")
|
||||
}
|
||||
|
||||
// AcquireSessionLockFor returns a not-yet-implemented error in the scaffold.
|
||||
func (s *S3Store) AcquireSessionLockFor(_, _ string) (*LockHandle, error) {
|
||||
return nil, fmt.Errorf("artifacts s3 acquire lock for campaign/session: not yet implemented")
|
||||
|
||||
@@ -15,18 +15,13 @@ type Ref struct {
|
||||
|
||||
// Store is the local artifact/workdir abstraction used by orchestration code.
|
||||
type Store interface {
|
||||
SessionPaths(sessionID string) SessionPaths
|
||||
SessionPathsFor(campaign, sessionID string) SessionPaths
|
||||
ResolveSessionPathsFor(campaign, sessionID string) (SessionPaths, error)
|
||||
EnsureLayout(sessionID string) (SessionPaths, error)
|
||||
EnsureLayoutFor(campaign, sessionID string) (SessionPaths, error)
|
||||
CopyInput(sessionID, srcPath, destRelativePath string) (Ref, error)
|
||||
CopyInputFor(campaign, sessionID, srcPath, destRelativePath string) (Ref, error)
|
||||
Exists(path string) (bool, error)
|
||||
ExistsRef(ref Ref) (bool, error)
|
||||
WriteFileAtomic(path string, data []byte, perm os.FileMode) error
|
||||
Checksum(path string) (string, error)
|
||||
AcquireSessionLock(sessionID string) (*LockHandle, error)
|
||||
AcquireSessionLockFor(campaign, sessionID string) (*LockHandle, error)
|
||||
ReleaseSessionLock(lock *LockHandle) error
|
||||
}
|
||||
|
||||
@@ -264,27 +264,14 @@ func resolveArchiveRunRoot(env *Env, m *manifest.Manifest) (string, error) {
|
||||
}
|
||||
|
||||
canonical := filepath.Clean(artifacts.SessionRunRootForCampaign(env.Config.Pipeline.Workspace.Root, campaign, sessionID, runID))
|
||||
legacy := filepath.Clean(artifacts.SessionRunWorkDir(env.Config.Pipeline.Workspace.Root, campaign, sessionID, runID))
|
||||
|
||||
canonicalExists, err := directoryExists(canonical)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("check canonical run root %q: %w", canonical, err)
|
||||
}
|
||||
legacyExists, err := directoryExists(legacy)
|
||||
if err != nil {
|
||||
return "", fmt.Errorf("check legacy run root %q: %w", legacy, err)
|
||||
}
|
||||
|
||||
switch {
|
||||
case canonicalExists && legacyExists && canonical != legacy:
|
||||
return "", fmt.Errorf("ambiguous run roots for campaign %q session %q run %q: canonical=%q legacy=%q", campaign, sessionID, runID, canonical, legacy)
|
||||
case canonicalExists:
|
||||
return canonical, nil
|
||||
case legacyExists:
|
||||
return legacy, nil
|
||||
default:
|
||||
return "", fmt.Errorf("run root not found for campaign %q session %q run %q (checked canonical=%q legacy=%q)", campaign, sessionID, runID, canonical, legacy)
|
||||
if !canonicalExists {
|
||||
return "", fmt.Errorf("run root not found for campaign %q session %q run %q at canonical path %q", campaign, sessionID, runID, canonical)
|
||||
}
|
||||
return canonical, nil
|
||||
}
|
||||
|
||||
func resolveArchiveSessionRoot(env *Env, m *manifest.Manifest) (string, error) {
|
||||
@@ -299,11 +286,10 @@ func resolveArchiveSessionRoot(env *Env, m *manifest.Manifest) (string, error) {
|
||||
if sessionID == "" {
|
||||
return "", fmt.Errorf("session id is required")
|
||||
}
|
||||
paths, err := artifacts.NewLocalStore(env.Config.Pipeline.Workspace.Root).ResolveSessionPathsFor(campaign, sessionID)
|
||||
if err != nil {
|
||||
return "", err
|
||||
if campaign == "" {
|
||||
return "", fmt.Errorf("campaign is required")
|
||||
}
|
||||
return filepath.Clean(paths.Root), nil
|
||||
return filepath.Clean(artifacts.SessionWorkDirForCampaign(env.Config.Pipeline.Workspace.Root, campaign, sessionID)), nil
|
||||
}
|
||||
|
||||
func archiveRunPrefix(env *Env, m *manifest.Manifest) (string, error) {
|
||||
|
||||
@@ -183,61 +183,18 @@ func TestArchiveFailsWhenRequiredPromotionMissing(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchivePromotionFailsOnAmbiguousSessionRoots(t *testing.T) {
|
||||
env, m, _ := archiveFixture(t)
|
||||
sessionWorkDir := artifacts.SessionWorkDir(env.Config.Pipeline.Workspace.Root, m.SessionID)
|
||||
writeStageTestFile(t, filepath.Join(sessionWorkDir, "transcripts", "trimmed.json"), "{}\n")
|
||||
|
||||
_, err := archiveStage{}.Run(context.Background(), env, m)
|
||||
if err == nil {
|
||||
t.Fatal("expected ambiguity error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "ambiguous session workspace roots") {
|
||||
t.Fatalf("error = %v, want ambiguity error", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchiveFallsBackToLegacyRunRootWhenCanonicalMissing(t *testing.T) {
|
||||
func TestArchiveFailsWhenCanonicalRunRootMissing(t *testing.T) {
|
||||
env, m, runRoot := archiveFixture(t)
|
||||
legacyRoot := artifacts.SessionRunWorkDir(
|
||||
env.Config.Pipeline.Workspace.Root,
|
||||
env.Config.Session.Campaign,
|
||||
env.Config.Session.SessionID,
|
||||
m.RunID,
|
||||
)
|
||||
if err := os.MkdirAll(filepath.Dir(legacyRoot), 0o755); err != nil {
|
||||
t.Fatalf("create legacy run-root parent: %v", err)
|
||||
}
|
||||
if err := os.Rename(runRoot, legacyRoot); err != nil {
|
||||
t.Fatalf("move canonical run root to legacy root: %v", err)
|
||||
}
|
||||
m.LocalWorkDir = legacyRoot
|
||||
|
||||
if _, err := (archiveStage{}).Run(context.Background(), env, m); err != nil {
|
||||
t.Fatalf("Run() error = %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestArchiveFailsOnAmbiguousCanonicalAndLegacyRunRoots(t *testing.T) {
|
||||
env, m, runRoot := archiveFixture(t)
|
||||
legacyRoot := artifacts.SessionRunWorkDir(
|
||||
env.Config.Pipeline.Workspace.Root,
|
||||
env.Config.Session.Campaign,
|
||||
env.Config.Session.SessionID,
|
||||
m.RunID,
|
||||
)
|
||||
writeStageTestFile(t, filepath.Join(legacyRoot, "manifest.json"), "{}\n")
|
||||
writeStageTestFile(t, filepath.Join(legacyRoot, "prepare", "inputs", "session.yml"), "session_id: 2026-04-19\n")
|
||||
if runRoot == legacyRoot {
|
||||
t.Fatalf("test requires distinct canonical and legacy run roots, got %q", runRoot)
|
||||
if err := os.RemoveAll(runRoot); err != nil {
|
||||
t.Fatalf("remove run root: %v", err)
|
||||
}
|
||||
|
||||
_, err := archiveStage{}.Run(context.Background(), env, m)
|
||||
if err == nil {
|
||||
t.Fatal("expected ambiguity error, got nil")
|
||||
t.Fatal("expected missing run-root error, got nil")
|
||||
}
|
||||
if !strings.Contains(err.Error(), "ambiguous run roots") {
|
||||
t.Fatalf("error = %v, want run-root ambiguity", err)
|
||||
if !strings.Contains(err.Error(), "run root not found") {
|
||||
t.Fatalf("error = %v, want missing run-root error", err)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -358,6 +358,10 @@ func pathsWorkDirForManifest(env *Env, m *manifest.Manifest, sessionID string) s
|
||||
if env == nil || env.Config == nil || env.Config.Pipeline == nil || env.Config.Session == nil {
|
||||
return ""
|
||||
}
|
||||
campaign := strings.TrimSpace(env.Config.Session.Campaign)
|
||||
if campaign == "" {
|
||||
return ""
|
||||
}
|
||||
if m != nil && strings.TrimSpace(m.LocalWorkDir) != "" {
|
||||
return strings.TrimSpace(m.LocalWorkDir)
|
||||
}
|
||||
@@ -366,15 +370,7 @@ func pathsWorkDirForManifest(env *Env, m *manifest.Manifest, sessionID string) s
|
||||
runID = strings.TrimSpace(m.RunID)
|
||||
}
|
||||
if runID != "" {
|
||||
campaign := strings.TrimSpace(env.Config.Session.Campaign)
|
||||
if campaign != "" {
|
||||
return artifacts.SessionRunRootForCampaign(env.Config.Pipeline.Workspace.Root, campaign, sessionID, runID)
|
||||
}
|
||||
return artifacts.SessionRunWorkDir(env.Config.Pipeline.Workspace.Root, campaign, sessionID, runID)
|
||||
}
|
||||
campaign := strings.TrimSpace(env.Config.Session.Campaign)
|
||||
if campaign == "" {
|
||||
return artifacts.SessionWorkDir(env.Config.Pipeline.Workspace.Root, sessionID)
|
||||
return artifacts.SessionRunRootForCampaign(env.Config.Pipeline.Workspace.Root, campaign, sessionID, runID)
|
||||
}
|
||||
return artifacts.SessionWorkDirForCampaign(env.Config.Pipeline.Workspace.Root, campaign, sessionID)
|
||||
}
|
||||
|
||||
@@ -160,7 +160,7 @@ func TestPrepareStageS3AudioDownloadAndMaterialization(t *testing.T) {
|
||||
env.Config.Pipeline.Spool = config.SpoolConfig{Root: filepath.Join(t.TempDir(), "spool")}
|
||||
env.Config.Pipeline.Storage.S3 = &config.StorageS3Config{Bucket: "my-dnd-archive", RootPrefix: "dnd"}
|
||||
m.RunID = "20260515T031522Z-a1b2c3d4"
|
||||
m.LocalWorkDir = artifacts.SessionRunWorkDir(env.Config.Pipeline.Workspace.Root, "forsaken", m.SessionID, m.RunID)
|
||||
m.LocalWorkDir = artifacts.SessionRunRootForCampaign(env.Config.Pipeline.Workspace.Root, "forsaken", m.SessionID, m.RunID)
|
||||
m.LocalSpoolDir = artifacts.SessionSpoolAudioDir(env.Config.Pipeline.Spool.Root, "forsaken", m.SessionID, m.RunID)
|
||||
|
||||
fake := &storage.FakeBackend{}
|
||||
@@ -256,7 +256,7 @@ func TestPrepareStageS3AudioFailures(t *testing.T) {
|
||||
env.Config.Pipeline.Spool = config.SpoolConfig{Root: filepath.Join(t.TempDir(), "spool")}
|
||||
env.Config.Pipeline.Storage.S3 = &config.StorageS3Config{Bucket: "my-dnd-archive", RootPrefix: "dnd"}
|
||||
m.RunID = "20260515T031522Z-a1b2c3d4"
|
||||
m.LocalWorkDir = artifacts.SessionRunWorkDir(env.Config.Pipeline.Workspace.Root, "forsaken", m.SessionID, m.RunID)
|
||||
m.LocalWorkDir = artifacts.SessionRunRootForCampaign(env.Config.Pipeline.Workspace.Root, "forsaken", m.SessionID, m.RunID)
|
||||
m.LocalSpoolDir = artifacts.SessionSpoolAudioDir(env.Config.Pipeline.Spool.Root, "forsaken", m.SessionID, m.RunID)
|
||||
|
||||
fake := &storage.FakeBackend{}
|
||||
|
||||
@@ -11,9 +11,6 @@ func sessionPathsForEnv(env *Env, sessionID string) artifacts.SessionPaths {
|
||||
if env != nil && env.Config != nil && env.Config.Session != nil {
|
||||
campaign = strings.TrimSpace(env.Config.Session.Campaign)
|
||||
}
|
||||
if campaign == "" {
|
||||
return env.ArtifactStore.SessionPaths(sessionID)
|
||||
}
|
||||
return env.ArtifactStore.SessionPathsFor(campaign, sessionID)
|
||||
}
|
||||
|
||||
@@ -22,8 +19,5 @@ func ensureLayoutForEnv(env *Env, sessionID string) (artifacts.SessionPaths, err
|
||||
if env != nil && env.Config != nil && env.Config.Session != nil {
|
||||
campaign = strings.TrimSpace(env.Config.Session.Campaign)
|
||||
}
|
||||
if campaign == "" {
|
||||
return env.ArtifactStore.EnsureLayout(sessionID)
|
||||
}
|
||||
return env.ArtifactStore.EnsureLayoutFor(campaign, sessionID)
|
||||
}
|
||||
|
||||
@@ -233,7 +233,7 @@ func setupTranscribeEnv(t *testing.T, audioFiles []string) (*Env, *manifest.Mani
|
||||
|
||||
sessionPath := filepath.Join(cfgDir, "session.yml")
|
||||
pipelinePath := filepath.Join(cfgDir, "pipeline.yml")
|
||||
writeFile(t, sessionPath, "session_id: 2026-05-03\n")
|
||||
writeFile(t, sessionPath, "session_id: 2026-05-03\ncampaign: sample-campaign\n")
|
||||
writeFile(t, pipelinePath, "workspace:\n root: "+workspace+"\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "speakers.yml"), "alice: alice.flac\n")
|
||||
writeFile(t, filepath.Join(cfgDir, "autocorrect.yml"), "[]\n")
|
||||
@@ -257,6 +257,7 @@ func setupTranscribeEnv(t *testing.T, audioFiles []string) (*Env, *manifest.Mani
|
||||
},
|
||||
Session: &config.SessionConfig{
|
||||
SessionID: "2026-05-03",
|
||||
Campaign: "sample-campaign",
|
||||
Inputs: config.SessionInputsConfig{
|
||||
AudioDir: "./audio",
|
||||
SpeakersFile: "./speakers.yml",
|
||||
|
||||
Reference in New Issue
Block a user