Added remote artifact listing to narratio status
This commit is contained in:
@@ -231,6 +231,7 @@ Session output includes:
|
||||
- session ID, campaign, workspace, session config source.
|
||||
- local manifest state when present.
|
||||
- remote current archive state when storage is configured.
|
||||
- catalog-based remote output availability for expected transcript and artifact sources.
|
||||
- effective archive locks and conservative next actions.
|
||||
|
||||
Common failure cases:
|
||||
|
||||
@@ -219,6 +219,8 @@ Recommended recovery:
|
||||
narratio status --session-id 2026-04-04
|
||||
```
|
||||
|
||||
This reports local manifest state, committed remote current state, expected remote transcript/artifact availability, and archive locks.
|
||||
|
||||
2. for one manifest file, run:
|
||||
|
||||
```bash
|
||||
@@ -251,6 +253,7 @@ Dry-run does not write restore report files.
|
||||
|
||||
- `status` with no config/session flags still requires explicit `--manifest`.
|
||||
- `status --session-id <id>` uses normal config/session loading, including remote session fallback.
|
||||
- `status --session-id <id>` includes the same source-based remote output availability view as `artifacts list --remote` when storage is configured.
|
||||
- local and S3 audio input modes are mutually exclusive.
|
||||
- archive publish requires upstream stages through `analyze` to be `succeeded`.
|
||||
- required promotion rules can fail when selected analyze artifacts did not generate a required file path.
|
||||
|
||||
@@ -208,6 +208,23 @@ func Status(ctx context.Context, args []string, out io.Writer) error {
|
||||
}
|
||||
|
||||
locks, err := loadEffectiveLocks(ctx, cfg, store)
|
||||
if catalog, catalogErr := buildHelperArtifactCatalog(cfg); catalogErr != nil {
|
||||
fmt.Fprintf(out, "Remote outputs: error: %v\n", catalogErr)
|
||||
} else if storeErr == nil {
|
||||
catalogLocks := locks
|
||||
if err != nil {
|
||||
catalogLocks = &effectiveLocks{
|
||||
Static: staticArchiveLocks(cfg),
|
||||
All: staticArchiveLocks(cfg),
|
||||
}
|
||||
}
|
||||
remoteState := map[string]string{}
|
||||
if store != nil {
|
||||
remoteState = remoteArtifactAvailability(ctx, cfg, store, catalog)
|
||||
}
|
||||
fmt.Fprintln(out, "Remote outputs:")
|
||||
writeArtifactList(out, cfg, catalog, catalogLocks, remoteState)
|
||||
}
|
||||
if err != nil {
|
||||
fmt.Fprintf(out, "Archive locks: error: %v\n", err)
|
||||
} else {
|
||||
|
||||
@@ -337,6 +337,80 @@ func TestExecuteArtifactsListRemoteReportsPromotedAvailability(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteStatusReportsRemoteArtifactCatalog(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
||||
fake := &storage.FakeBackend{}
|
||||
sessionPrefix := artifacts.S3SessionPrefix("dnd", "sample-campaign", "2026-05-03")
|
||||
manifestKey, runIDKey := artifacts.ResolveArchiveCurrentStateKeys(sessionPrefix)
|
||||
trimmedKey := artifacts.S3PromotedArtifactKey(sessionPrefix, "transcripts/trimmed.json")
|
||||
lockKey := artifacts.S3SessionLocksKey(sessionPrefix)
|
||||
fake.SeedObject(storage.FakeObject{Key: runIDKey, Data: []byte("20260519T010203Z-a1b2c3d4\n")})
|
||||
fake.SeedObject(storage.FakeObject{Key: manifestKey, Data: restoreManifestJSON(t, "2026-05-03", "sample-campaign")})
|
||||
fake.SeedObject(storage.FakeObject{Key: trimmedKey, Data: []byte(`{"segments":[]}`)})
|
||||
fake.SeedObject(storage.FakeObject{Key: lockKey, Data: []byte("locks:\n - source: narratio.transcript.trimmed\n reason: remote review\n")})
|
||||
var storeInitCalls int
|
||||
restoreAppConfigTestGlobals(t, fake, &storeInitCalls, []string{sessionPath})
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
code := Execute([]string{
|
||||
"status",
|
||||
"--config", pipelinePath,
|
||||
"--campaign", campaignPath,
|
||||
"--session", sessionPath,
|
||||
"--session-id", "2026-05-03",
|
||||
}, &stdout, &stderr)
|
||||
if code != 0 {
|
||||
t.Fatalf("exit code = %d, want 0; stderr=%q", code, stderr.String())
|
||||
}
|
||||
out := stdout.String()
|
||||
for _, want := range []string{
|
||||
"Remote outputs:",
|
||||
"Built-in:",
|
||||
"Configured:",
|
||||
"Previous-session:",
|
||||
"Promoted:",
|
||||
"narratio.transcript.trimmed locked remote=promoted",
|
||||
"narratio.transcript.merged remote=missing",
|
||||
} {
|
||||
if !strings.Contains(out, want) {
|
||||
t.Fatalf("stdout = %q, want %q", out, want)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteStatusReportsRemoteArtifactCatalogErrorsWithoutFailing(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, campaignPath, sessionPath := writeValidConfigFiles(t, workspaceRoot)
|
||||
fake := &storage.FakeBackend{ExistsErr: fmt.Errorf("exists failed")}
|
||||
var storeInitCalls int
|
||||
restoreAppConfigTestGlobals(t, fake, &storeInitCalls, []string{sessionPath})
|
||||
|
||||
var stdout bytes.Buffer
|
||||
var stderr bytes.Buffer
|
||||
code := Execute([]string{
|
||||
"status",
|
||||
"--config", pipelinePath,
|
||||
"--campaign", campaignPath,
|
||||
"--session", sessionPath,
|
||||
"--session-id", "2026-05-03",
|
||||
}, &stdout, &stderr)
|
||||
if code != 0 {
|
||||
t.Fatalf("exit code = %d, want 0; stderr=%q", code, stderr.String())
|
||||
}
|
||||
out := stdout.String()
|
||||
if !strings.Contains(out, "Remote archive: missing or unavailable:") {
|
||||
t.Fatalf("stdout = %q, want remote archive unavailable state", out)
|
||||
}
|
||||
if !strings.Contains(out, "Remote outputs:") || !strings.Contains(out, "narratio.transcript.trimmed remote=error") {
|
||||
t.Fatalf("stdout = %q, want remote output error state", out)
|
||||
}
|
||||
if !strings.Contains(out, "Archive locks: error:") {
|
||||
t.Fatalf("stdout = %q, want archive locks error", out)
|
||||
}
|
||||
}
|
||||
|
||||
func TestExecuteArchiveLoadsRemoteLocks(t *testing.T) {
|
||||
workspaceRoot := t.TempDir()
|
||||
pipelinePath, campaignPath, sessionPath := writeValidArchiveConfigFiles(t, workspaceRoot)
|
||||
|
||||
Reference in New Issue
Block a user