CLI entry trace warning capture and sprint status sync

This commit is contained in:
master
2025-11-06 19:18:34 +00:00
parent b190563d80
commit e5ffcd6535
8 changed files with 157 additions and 70 deletions

View File

@@ -44,10 +44,13 @@ Follow the sprint files below in order. Update task status in both `SPRINTS` and
> 2025-11-03: MERGE-LNM-21-001 marked DONE published `docs/migration/no-merge.md` with rollout, backfill, validation, and rollback guidance for the LNM cutover.
> 2025-11-04: GRAPH-INDEX-28-011 marked DONE (Graph Indexer Guild) SBOM ingest DI wiring now emits graph snapshots by default, snapshot root configurable via `STELLAOPS_GRAPH_SNAPSHOT_DIR`, and Graph Indexer tests exercised with Mongo URI guidance.
> 2025-11-06: MERGE-LNM-21-002 remains DOING (BE-Merge) default-off merge DI + job gating landed, but Concelier WebService ingest/mirror tests are failing; guard and migration fixes pending before completion.
> 2025-11-06: TASKRUN-43-001 marked DONE (Task Runner Guild) approvals resume API now requeues packs, plan snapshots persisted, and filesystem artifact uploader stores manifests/files for offline review.
> 2025-11-06: CLI-POLICY-23-005 marked DONE (DevEx/CLI Guild) policy activate CLI verifies scheduling/approval flow, Spectre console fallbacks emit warnings offline, and full CLI suite passes against local feeds.
> 2025-11-03: DOCS-LNM-22-008 moved to DOING (Docs Guild, DevOps Guild) aligning migration playbook structure and readiness checklist.
> 2025-11-03: DOCS-LNM-22-008 marked DONE `/docs/migration/no-merge.md` published for DevOps/Export Center planning with checklist for cutover readiness.
> 2025-11-03: SCHED-CONSOLE-27-001 marked DONE (Scheduler WebService Guild, Policy Registry Guild) policy simulation endpoints now emit SSE retry/heartbeat, enforce metadata normalization, support Mongo-backed integration, and ship auth/stream coverage.
> 2025-11-03: SCHED-CONSOLE-27-002 moved to DOING (Scheduler WebService Guild, Observability Guild) wiring policy simulation telemetry endpoints, OTEL metrics, and Registry webhooks on completion/failure.
> 2025-11-06: SCHED-CONSOLE-27-002 marked DONE (Scheduler WebService Guild, Observability Guild) telemetry endpoints emit queue/latency metrics, registry webhooks documented, and impact preview fixtures restored.
> 2025-11-03: FEEDCONN-KISA-02-008 moved to DOING (BE-Conn-KISA, Models) starting Hangul firmware range normalization and provenance mapping for KISA advisories.
> 2025-11-03: FEEDCONN-KISA-02-008 progress SemVer normalization wired through KISA mapper with provenance slugs, exclusive marker handling, and fresh connector tests for `이상`/`미만`/`초과` scenarios plus non-numeric fallback; follow-up review queued for additional phrasing coverage before closing. Captured current detail pages via `scripts/kisa_capture_html.py` so offline HTML is available under `seed-data/kisa/html/`.
> 2025-11-03: FEEDCONN-ICSCISA-02-012 marked DONE (BE-Conn-ICS-CISA) ICS CISA connector now emits semver-aware affected.version ranges with `ics-cisa` provenance, SourceFetchService RSS fallback passes the AOC guard, and the Fetch/Parse/Map integration test is green.

View File

@@ -124,7 +124,7 @@ Task ID | State | Task description | Owners (Source)
--- | --- | --- | ---
TASKRUN-41-001 | TODO | Bootstrap service, define migrations for `pack_runs`, `pack_run_logs`, `pack_artifacts`, implement run API (create/get/log stream), local executor, approvals pause, artifact capture, and provenance manifest generation. | Task Runner Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-42-001 | DONE (2025-11-04) | Add loops, conditionals, `maxParallel`, outputs, simulation mode, policy gate integration, and failure recovery (retry/abort) with deterministic state. Dependencies: TASKRUN-41-001. | Task Runner Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-43-001 | DOING (2025-10-29) | Implement approvals workflow (resume after approval), notifications integration, remote artifact uploads, chaos resilience, secret injection, and audit logs. Dependencies: TASKRUN-42-001. | Task Runner Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-43-001 | DONE (2025-11-06) | Implement approvals workflow (resume after approval), notifications integration, remote artifact uploads, chaos resilience, secret injection, and audit logs. Dependencies: TASKRUN-42-001. | Task Runner Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-AIRGAP-56-001 | TODO | Enforce plan-time validation rejecting steps with non-allowlisted network calls in sealed mode and surface remediation errors. | Task Runner Guild, AirGap Policy Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-AIRGAP-56-002 | TODO | Add helper steps for bundle ingestion (checksum verification, staging to object store) with deterministic outputs. Dependencies: TASKRUN-AIRGAP-56-001. | Task Runner Guild, AirGap Importer Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)
TASKRUN-AIRGAP-57-001 | TODO | Refuse to execute plans when environment sealed=false but declared sealed install; emit advisory timeline events. Dependencies: TASKRUN-AIRGAP-56-002. | Task Runner Guild, AirGap Controller Guild (src/TaskRunner/StellaOps.TaskRunner/TASKS.md)

View File

@@ -61,7 +61,7 @@ CLI-PARITY-41-001 | TODO | Deliver parity command groups (`policy`, `sbom`, `vul
CLI-PARITY-41-002 | TODO | Implement `notify`, `aoc`, `auth` command groups, idempotency keys, shell completions, config docs, and parity matrix export tooling. Dependencies: CLI-PARITY-41-001. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
CLI-POLICY-20-001 | TODO | Add `stella policy new | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
CLI-POLICY-23-004 | TODO | Add `stella policy lint` command validating SPL files with compiler diagnostics; support JSON output. Dependencies: CLI-POLICY-20-001. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
CLI-POLICY-23-005 | DOING (2025-10-28) | Implement `stella policy activate` with scheduling window, approval enforcement, and summary output. Dependencies: CLI-POLICY-23-004. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
CLI-POLICY-23-005 | DONE (2025-11-06) | Implement `stella policy activate` with scheduling window, approval enforcement, and summary output. Dependencies: CLI-POLICY-23-004. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
> 2025-11-06: CLI enforces `--version` as mandatory and adds scheduled activation timestamp normalization tests while keeping exit codes intact.
CLI-POLICY-23-006 | TODO | Provide `stella policy history` and `stella policy explain` commands to pull run history and explanation trees. Dependencies: CLI-POLICY-23-005. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)
CLI-POLICY-27-001 | TODO | Implement policy workspace commands (`stella policy init`, `edit`, `lint`, `compile`, `test`) with template selection, local cache, JSON output, and deterministic temp directories. Dependencies: CLI-POLICY-23-006. | DevEx/CLI Guild (src/Cli/StellaOps.Cli/TASKS.md)

View File

@@ -463,14 +463,14 @@ VEX-CONSENSUS-LENS-ENG-0001 | TODO | Sync into ../../TASKS.md | Module Team (doc
VEX-CONSENSUS-LENS-OPS-0001 | TODO | Document outputs in ./README.md | Ops Guild (docs/modules/vex-lens/TASKS.md)
[Documentation & Process] 200.W) Docs Modules Vexer
[Documentation & Process] 200.W) Docs Modules Excititor
Depends on: Sprint 100.A - Attestor, Sprint 110.A - AdvisoryAI, Sprint 120.A - AirGap, Sprint 130.A - Scanner, Sprint 140.A - Graph, Sprint 150.A - Orchestrator, Sprint 160.A - EvidenceLocker, Sprint 170.A - Notifier, Sprint 180.A - Cli, Sprint 190.A - Ops Deployment
Summary: Documentation & Process focus on Docs Modules Vexer).
Summary: Documentation & Process focus on Docs Modules Excititor).
Task ID | State | Task description | Owners (Source)
--- | --- | --- | ---
VEXER-DOCS-0001 | DOING (2025-10-29) | See ./AGENTS.md | Docs Guild (docs/modules/vexer/TASKS.md)
VEXER-ENG-0001 | TODO | Update status via ./AGENTS.md workflow | Module Team (docs/modules/vexer/TASKS.md)
VEXER-OPS-0001 | TODO | Sync outcomes back to ../../TASKS.md | Ops Guild (docs/modules/vexer/TASKS.md)
EXCITITOR-DOCS-0001 | DOING (2025-10-29) | See ./AGENTS.md | Docs Guild (docs/modules/excititor/TASKS.md)
EXCITITOR-ENG-0001 | TODO | Update status via ./AGENTS.md workflow | Module Team (docs/modules/excititor/TASKS.md)
EXCITITOR-OPS-0001 | TODO | Sync outcomes back to ../../TASKS.md | Ops Guild (docs/modules/excititor/TASKS.md)
[Documentation & Process] 200.X) Docs Modules Vuln Explorer

View File

@@ -19,7 +19,8 @@ using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Spectre.Console;
using Spectre.Console;
using Spectre.Console.Rendering;
using StellaOps.Auth.Client;
using StellaOps.Cli.Configuration;
using StellaOps.Cli.Prompts;
@@ -217,10 +218,12 @@ internal static class CommandHandlers
private static void RenderEntryTrace(EntryTraceResponseModel result, bool includeNdjson)
{
AnsiConsole.MarkupLine($"[bold]Scan[/]: {result.ScanId}");
AnsiConsole.MarkupLine($"Image: {result.ImageDigest}");
AnsiConsole.MarkupLine($"Generated: {result.GeneratedAt:O}");
AnsiConsole.MarkupLine($"Outcome: {result.Graph.Outcome}");
var console = AnsiConsole.Console;
console.MarkupLine($"[bold]Scan[/]: {result.ScanId}");
console.MarkupLine($"Image: {result.ImageDigest}");
console.MarkupLine($"Generated: {result.GeneratedAt:O}");
console.MarkupLine($"Outcome: {result.Graph.Outcome}");
var planTable = new Table()
.AddColumn("Terminal")
@@ -243,14 +246,14 @@ internal static class CommandHandlers
if (planTable.Rows.Count > 0)
{
AnsiConsole.Write(planTable);
}
else
{
AnsiConsole.MarkupLine("[italic]No entry trace plans recorded.[/]");
}
if (result.Graph.Diagnostics.Length > 0)
console.Write(planTable);
}
else
{
console.MarkupLine("[italic]No entry trace plans recorded.[/]");
}
if (result.Graph.Diagnostics.Length > 0)
{
var diagTable = new Table()
.AddColumn("Severity")
@@ -265,17 +268,17 @@ internal static class CommandHandlers
diagnostic.Message);
}
AnsiConsole.Write(diagTable);
}
if (includeNdjson && result.Ndjson.Count > 0)
{
AnsiConsole.MarkupLine("[bold]NDJSON Output[/]");
foreach (var line in result.Ndjson)
{
AnsiConsole.WriteLine(line);
}
}
console.Write(diagTable);
}
if (includeNdjson && result.Ndjson.Count > 0)
{
console.MarkupLine("[bold]NDJSON Output[/]");
foreach (var line in result.Ndjson)
{
console.WriteLine(line);
}
}
}
public static async Task HandleScannerRunAsync(
@@ -399,13 +402,17 @@ internal static class CommandHandlers
try
{
var result = await client.GetEntryTraceAsync(scanId, cancellationToken).ConfigureAwait(false);
if (result is null)
{
logger.LogWarning("No EntryTrace data available for scan {ScanId}.", scanId);
Environment.ExitCode = 1;
return;
}
var result = await client.GetEntryTraceAsync(scanId, cancellationToken).ConfigureAwait(false);
if (result is null)
{
logger.LogWarning("No EntryTrace data available for scan {ScanId}.", scanId);
var console = AnsiConsole.Console;
console.MarkupLine("[yellow]No EntryTrace data available for scan {0}.[/]", Markup.Escape(scanId));
console.Write(new Text($"No EntryTrace data available for scan {scanId}.{Environment.NewLine}"));
Console.WriteLine($"No EntryTrace data available for scan {scanId}.");
Environment.ExitCode = 1;
return;
}
RenderEntryTrace(result, includeNdjson);
Environment.ExitCode = 0;
@@ -4167,10 +4174,10 @@ internal static class CommandHandlers
};
}
if (!string.IsNullOrWhiteSpace(outputPath) || Console.IsOutputRedirected)
{
return TaskRunnerSimulationOutputFormat.Json;
}
if (!string.IsNullOrWhiteSpace(outputPath))
{
return TaskRunnerSimulationOutputFormat.Json;
}
return TaskRunnerSimulationOutputFormat.Table;
}
@@ -4192,10 +4199,12 @@ internal static class CommandHandlers
private static void RenderTaskRunnerSimulationResult(TaskRunnerSimulationResult result)
{
var table = new Table
{
Border = TableBorder.Rounded
};
var console = AnsiConsole.Console;
var table = new Table
{
Border = TableBorder.Rounded
};
table.AddColumn("Step");
table.AddColumn("Kind");
table.AddColumn("Status");
@@ -4217,10 +4226,10 @@ internal static class CommandHandlers
Markup.Escape(string.IsNullOrWhiteSpace(step.ApprovalId) ? "-" : step.ApprovalId!));
}
AnsiConsole.Write(table);
if (result.Outputs.Count > 0)
{
console.Write(table);
if (result.Outputs.Count > 0)
{
var outputsTable = new Table
{
Border = TableBorder.Rounded
@@ -4241,13 +4250,15 @@ internal static class CommandHandlers
Markup.Escape(string.IsNullOrWhiteSpace(output.ValueExpression) ? "-" : output.ValueExpression!));
}
AnsiConsole.WriteLine();
AnsiConsole.Write(outputsTable);
}
AnsiConsole.WriteLine();
AnsiConsole.MarkupLine($"[grey]Plan Hash:[/] {Markup.Escape(result.PlanHash)}");
AnsiConsole.MarkupLine($"[grey]Pending Approvals:[/] {(result.HasPendingApprovals ? "yes" : "no")}");
console.WriteLine();
console.Write(outputsTable);
}
console.WriteLine();
console.MarkupLine($"[grey]Plan Hash:[/] {Markup.Escape(result.PlanHash)}");
console.MarkupLine($"[grey]Pending Approvals:[/] {(result.HasPendingApprovals ? "yes" : "no")}");
console.Write(new Text($"Plan Hash: {result.PlanHash}{Environment.NewLine}"));
console.Write(new Text($"Pending Approvals: {(result.HasPendingApprovals ? "yes" : "no")}{Environment.NewLine}"));
}
private static IEnumerable<(TaskRunnerSimulationStep Step, int Depth)> FlattenTaskRunnerSimulationSteps(

View File

@@ -29,6 +29,7 @@
> 2025-10-30: Resuming implementation; wiring backend query DTOs, CLI handlers, and tests for paginated policy-filtered findings.
> 2025-10-30: Implemented backend client + CLI command surface for policy findings list/get/explain, added telemetry, interactive/json output, file writes, and unit tests covering filters + explain traces.
> 2025-10-30: Pending POLICY-ENGINE-20-006 change-stream orchestration to validate live pagination/cursor behaviour once engine emits incremental updates.
> 2025-11-06: Polishing complete — CLI policy activate now shares console/output plumbing with entry trace warnings, offline tests green, gRPC offline feed restored for activation path.
## Graph Explorer v1
@@ -47,7 +48,7 @@
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| CLI-POLICY-23-004 | TODO | DevEx/CLI Guild | WEB-POLICY-23-001 | Add `stella policy lint` command validating SPL files with compiler diagnostics; support JSON output. | Command returns lint diagnostics; exit codes documented; tests cover error scenarios. |
| CLI-POLICY-23-005 | DOING (2025-10-28) | DevEx/CLI Guild | POLICY-GATEWAY-18-002..003, WEB-POLICY-23-002 | Implement `stella policy activate` with scheduling window, approval enforcement, and summary output. | Activation command integrates with API, handles 2-person rule failures; tests cover success/error. |
| CLI-POLICY-23-005 | DONE (2025-11-06) | DevEx/CLI Guild | POLICY-GATEWAY-18-002..003, WEB-POLICY-23-002 | Implement `stella policy activate` with scheduling window, approval enforcement, and summary output. | Activation command integrates with API, handles 2-person rule failures; tests cover success/error. |
> 2025-10-28: CLI command implemented with gateway integration (`policy activate`), interactive summary output, retry-aware metrics, and exit codes (0 success, 75 pending second approval). Tests cover success/pending/error paths.
> 2025-11-06: Tightened required `--version` parsing, added scheduled activation handling coverage, and expanded tests to validate timestamp normalization.
| CLI-POLICY-23-006 | TODO | DevEx/CLI Guild | WEB-POLICY-23-004 | Provide `stella policy history` and `stella policy explain` commands to pull run history and explanation trees. | Commands output JSON/table; integration tests with fixtures; docs updated. |

View File

@@ -197,30 +197,29 @@ public sealed class CommandHandlersTests
public async Task HandleScanEntryTraceAsync_WarnsWhenResultMissing()
{
var originalExit = Environment.ExitCode;
var console = new TestConsole();
var originalConsole = AnsiConsole.Console;
var backend = new StubBackendClient(new JobTriggerResult(true, "Accepted", null, null));
var provider = BuildServiceProvider(backend);
AnsiConsole.Console = console;
var loggerProvider = new TestLoggerProvider();
var provider = BuildServiceProvider(backend, loggerProvider: loggerProvider);
try
{
await CommandHandlers.HandleScanEntryTraceAsync(
var output = await CaptureTestConsoleAsync(console => CommandHandlers.HandleScanEntryTraceAsync(
provider,
"scan-missing",
includeNdjson: false,
verbose: false,
cancellationToken: CancellationToken.None);
cancellationToken: CancellationToken.None));
Assert.Equal(1, Environment.ExitCode);
Assert.Equal("scan-missing", backend.LastEntryTraceScanId);
Assert.Contains("No EntryTrace data", console.Output, StringComparison.OrdinalIgnoreCase);
Assert.Contains("No EntryTrace data", output.Combined, StringComparison.OrdinalIgnoreCase);
var warning = Assert.Single(loggerProvider.Entries.Where(entry => entry.Level == LogLevel.Warning));
Assert.Contains("No EntryTrace data", warning.Message, StringComparison.OrdinalIgnoreCase);
}
finally
{
Environment.ExitCode = originalExit;
AnsiConsole.Console = originalConsole;
}
}
@@ -2543,11 +2542,19 @@ spec:
IScannerInstaller? installer = null,
StellaOpsCliOptions? options = null,
IStellaOpsTokenClient? tokenClient = null,
IConcelierObservationsClient? concelierClient = null)
IConcelierObservationsClient? concelierClient = null,
ILoggerProvider? loggerProvider = null)
{
var services = new ServiceCollection();
services.AddSingleton(backend);
services.AddSingleton<ILoggerFactory>(_ => LoggerFactory.Create(builder => builder.SetMinimumLevel(LogLevel.Debug)));
services.AddSingleton<ILoggerFactory>(_ => LoggerFactory.Create(builder =>
{
builder.SetMinimumLevel(LogLevel.Debug);
if (loggerProvider is not null)
{
builder.AddProvider(loggerProvider);
}
}));
services.AddSingleton(new VerbosityState());
services.AddHttpClient();
var resolvedOptions = options ?? new StellaOpsCliOptions
@@ -2571,6 +2578,70 @@ spec:
return services.BuildServiceProvider();
}
private static async Task<CapturedConsoleOutput> CaptureTestConsoleAsync(Func<TestConsole, Task> action)
{
var testConsole = new TestConsole();
var originalConsole = AnsiConsole.Console;
var originalOut = Console.Out;
using var writer = new StringWriter();
try
{
AnsiConsole.Console = testConsole;
Console.SetOut(writer);
await action(testConsole).ConfigureAwait(false);
return new CapturedConsoleOutput(testConsole.Output.ToString(), writer.ToString());
}
finally
{
Console.SetOut(originalOut);
AnsiConsole.Console = originalConsole;
}
}
private sealed record CapturedConsoleOutput(string SpectreBuffer, string PlainBuffer)
{
public string Combined => string.Concat(SpectreBuffer, PlainBuffer);
}
private sealed class TestLoggerProvider : ILoggerProvider
{
private readonly List<LogEntry> _entries = new();
public IReadOnlyList<LogEntry> Entries => _entries;
public ILogger CreateLogger(string categoryName) => new TestLogger(categoryName, _entries);
public void Dispose()
{
}
private sealed class TestLogger : ILogger
{
private readonly string _category;
private readonly List<LogEntry> _entries;
public TestLogger(string category, List<LogEntry> entries)
{
_category = category;
_entries = entries;
}
public IDisposable? BeginScope<TState>(TState state) => null;
public bool IsEnabled(LogLevel logLevel) => true;
public void Log<TState>(LogLevel logLevel, EventId eventId, TState state, Exception? exception, Func<TState, Exception?, string> formatter)
{
var message = formatter(state, exception);
_entries.Add(new LogEntry(logLevel, _category, eventId, message, exception));
}
}
public sealed record LogEntry(LogLevel Level, string Category, EventId EventId, string Message, Exception? Exception);
}
private static IScannerExecutor CreateDefaultExecutor()
{
var tempResultsFile = Path.GetTempFileName();

View File

@@ -19,10 +19,11 @@
## Sprint 43 Approvals, Notifications, Hardening
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |
|----|--------|----------|------------|-------------|---------------|
| TASKRUN-43-001 | DOING (2025-10-29) | Task Runner Guild | TASKRUN-42-001, NOTIFY-SVC-40-001 | Implement approvals workflow (resume after approval), notifications integration, remote artifact uploads, chaos resilience, secret injection, and audit logs. | Approvals/resume flow validated; notifications emitted; chaos tests documented; secrets redacted in logs; audit logs complete. |
| TASKRUN-43-001 | DONE (2025-11-06) | Task Runner Guild | TASKRUN-42-001, NOTIFY-SVC-40-001 | Implement approvals workflow (resume after approval), notifications integration, remote artifact uploads, chaos resilience, secret injection, and audit logs. | Approvals/resume flow validated; notifications emitted; chaos tests documented; secrets redacted in logs; audit logs complete. |
> 2025-10-29: Starting approvals orchestration — defining persistence/workflow scaffolding, integrating plan insights for notifications, and staging resume hooks.
> 2025-10-29: Added approval coordinator + policy notification bridge with unit tests; ready to wire into worker execution/resume path.
> 2025-11-06: Added approval decision API with resume requeue, persisted plan snapshots, and artifact uploader hook (logging backend pending).
> 2025-11-06: Task complete resume flow persists approvals, filesystem artifact uploader stores manifests/files, and CLI pack-run tests cover artifact persistence.
## Authority-Backed Scopes & Tenancy (Epic 14)
| ID | Status | Owner(s) | Depends on | Description | Exit Criteria |