// forecast_fixtures_formal_test.go
//
// Formal predicate coverage derived from:
// specs/forecast-compliance-fixtures/README.md
//
// Encodes the following formal predicates (see issue for full illustrative
// TLA+ / Z3 / F* notation):
// P1 Bernoulli success predicate (conclusion -> success indicator)
// P2 ET non-negativity invariant
// P3 Zero-ET fixture models missing artifact (T-FC-022)
// P4 High-ET overflow boundary (T-ET-006)
// P5 Duration derivation from timestamps is non-negative
// P6 Failed-run fixture conclusion sampling (T-FC-035)
// P7 RunSummary schema field presence contract
// P8 Cache-hit determinism (JSON round-trip stability)
// P9 Timestamp ordering invariant (RunStartedAt <= UpdatedAt)
// P10 Monte Carlo input completeness invariant across fixtures
package cli_test
import (
"encoding/json"
"os"
"path/filepath"
"testing"
"time"
"github.com/stretchr/testify/assert"
"github.com/stretchr/testify/require"
)
// minimalRunSummary is a stub — replace with real cli.RunSummary import if
// exported, or reuse the existing package-level struct from pkg/cli/logs_models.go.
// stub — replace with real implementation
type minimalWorkflowRun struct {
Conclusion string `json:"conclusion"`
CreatedAt time.Time `json:"created_at"`
UpdatedAt time.Time `json:"updated_at"`
RunStartedAt time.Time `json:"run_started_at"`
}
// stub — replace with real implementation
type minimalTokenUsageSummary struct {
TotalEffectiveTokens int64 `json:"total_effective_tokens"`
}
// stub — replace with real implementation
type minimalRunSummary struct {
CLIVersion string `json:"cli_version"`
RunID int64 `json:"run_id"`
Run minimalWorkflowRun `json:"run"`
TokenUsageSummary minimalTokenUsageSummary `json:"token_usage_summary"`
}
// loadFixtureRunSummary loads a fixture file from
// specs/forecast-compliance-fixtures/ into the minimal stub struct.
func loadFixtureRunSummary(t *testing.T, name string) minimalRunSummary {
t.Helper()
path := filepath.Join("..", "..", "specs", "forecast-compliance-fixtures", name)
data, err := os.ReadFile(path)
require.NoError(t, err, "fixture file %s should be readable", name)
var rs minimalRunSummary
require.NoError(t, json.Unmarshal(data, &rs), "fixture %s should be valid JSON matching RunSummary schema", name)
return rs
}
// bernoulliSuccess implements P1: maps a conclusion string to a Bernoulli
// success indicator (1 for success, 0 otherwise).
func bernoulliSuccess(conclusion string) int {
if conclusion == "success" {
return 1
}
return 0
}
// TestBernoulliSuccessFromConclusion encodes P1.
func TestBernoulliSuccessFromConclusion(t *testing.T) {
tests := []struct {
name string
conclusion string
want int
}{
{"success maps to 1", "success", 1},
{"failure maps to 0", "failure", 0},
{"cancelled maps to 0", "cancelled", 0},
{"empty conclusion maps to 0", "", 0},
}
for _, tt := range tests {
t.Run(tt.name, func(t *testing.T) {
got := bernoulliSuccess(tt.conclusion)
assert.Equal(t, tt.want, got, "bernoulliSuccess(%q) should equal %d per P1", tt.conclusion, tt.want)
})
}
}
// TestEffectiveTokensNonNegative encodes P2 using the minimal fixture.
func TestEffectiveTokensNonNegative(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_minimal.json")
assert.GreaterOrEqual(t, rs.TokenUsageSummary.TotalEffectiveTokens, int64(0),
"total_effective_tokens must never be negative per P2")
}
// TestZeroETFixtureIndicatesMissingArtifact encodes P3 (T-FC-022).
// Edge case: fixture representing a run whose token artifact was never downloaded.
func TestZeroETFixtureIndicatesMissingArtifact(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_zero_et.json")
assert.Zero(t, rs.TokenUsageSummary.TotalEffectiveTokens,
"run_summary_zero_et.json must have total_effective_tokens == 0 per P3/T-FC-022")
}
// TestHighETFixtureExceedsOverflowThreshold encodes P4 (T-ET-006).
// Edge case: extremely large ET value used to probe overflow handling.
func TestHighETFixtureExceedsOverflowThreshold(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_high_et.json")
assert.GreaterOrEqual(t, rs.TokenUsageSummary.TotalEffectiveTokens, int64(1_000_000),
"run_summary_high_et.json must have total_effective_tokens >= 1,000,000 per P4/T-ET-006")
}
// TestDurationSecondsNonNegative encodes P5.
func TestDurationSecondsNonNegative(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_minimal.json")
duration := rs.Run.UpdatedAt.Sub(rs.Run.RunStartedAt)
assert.GreaterOrEqual(t, duration.Seconds(), 0.0,
"duration computed from run_started_at/updated_at must be non-negative per P5")
}
// TestFailedFixtureHasFailureConclusion encodes P6 (T-FC-035).
// Edge case: fixture representing a failed workflow run.
func TestFailedFixtureHasFailureConclusion(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_failed.json")
assert.Equal(t, "failure", rs.Run.Conclusion,
"run_summary_failed.json must have conclusion == \"failure\" per P6/T-FC-035")
}
// TestRunSummarySchemaFieldsPresent encodes P7.
func TestRunSummarySchemaFieldsPresent(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_minimal.json")
require.NotEmpty(t, rs.Run.Conclusion, "run.conclusion must be present per P7 schema contract")
require.NotZero(t, rs.RunID, "run_id must be present and non-zero per P7 schema contract")
assert.GreaterOrEqual(t, rs.TokenUsageSummary.TotalEffectiveTokens, int64(0),
"token_usage_summary.total_effective_tokens must be present per P7 schema contract")
}
// TestRunSummaryRoundTripSerialization encodes P8: JSON round-trip stability
// for a cached run summary.
func TestRunSummaryRoundTripSerialization(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_minimal.json")
data, err := json.Marshal(rs)
require.NoError(t, err, "marshaling RunSummary must not error per P8 cache determinism")
var roundTripped minimalRunSummary
require.NoError(t, json.Unmarshal(data, &roundTripped), "unmarshaling serialized RunSummary must not error per P8")
assert.Equal(t, rs, roundTripped, "round-tripped RunSummary must equal original per P8 cache-hit determinism")
}
// TestRunStartedBeforeOrEqualUpdated encodes P9.
func TestRunStartedBeforeOrEqualUpdated(t *testing.T) {
rs := loadFixtureRunSummary(t, "run_summary_minimal.json")
assert.True(t, !rs.Run.RunStartedAt.After(rs.Run.UpdatedAt),
"run_started_at must be <= updated_at per P9 timestamp ordering invariant")
}
// TestMonteCarloInputFixtureCompleteness encodes P10 across all known fixtures.
func TestMonteCarloInputFixtureCompleteness(t *testing.T) {
fixtures := []string{
"run_summary_minimal.json",
"run_summary_zero_et.json",
"run_summary_failed.json",
"run_summary_high_et.json",
}
for _, name := range fixtures {
t.Run(name, func(t *testing.T) {
path := filepath.Join("..", "..", "specs", "forecast-compliance-fixtures", name)
if _, err := os.Stat(path); os.IsNotExist(err) {
t.Skipf("fixture %s not yet materialized on disk (only run_summary_minimal.json is committed today) — P10 requires it for full Monte Carlo compliance", name)
return
}
rs := loadFixtureRunSummary(t, name)
assert.Contains(t, []string{"success", "failure"}, rs.Run.Conclusion,
"conclusion must be success or failure per P10 Monte Carlo input completeness")
assert.GreaterOrEqual(t, rs.TokenUsageSummary.TotalEffectiveTokens, int64(0),
"total_effective_tokens must be non-negative per P10")
assert.False(t, rs.Run.RunStartedAt.IsZero(), "run_started_at must be set per P10")
assert.False(t, rs.Run.UpdatedAt.IsZero(), "updated_at must be set per P10")
})
}
}
Caution
agentic threat detected
Threat detection flagged this output in warn mode. Manual review is REQUIRED before any follow-up automation.
Details
The threat detection engine failed to produce results.
Review the workflow run logs for details.
Summary
This spec documents the fixture files used to bootstrap Section 12 compliance tests for the Forecast Monte Carlo engine (
pkg/cli/forecast_montecarlo_test.go,pkg/cli/forecast_test.go). The fixtures representRunSummaryJSON snapshots (run_summary_minimal.json, plus additional zero-ET, failed-run, and high-ET variants) that drive Bernoulli success-rate sampling and bootstrap Effective Token (ET) resampling in the Monte Carlo forecaster. This run formalizes the implicit schema and invariants of these fixtures and their consumers (RunSummary,WorkflowRun,TokenUsageSummary) as testable predicates, and derives a Go testify suite encoding them.Specification
specs/forecast-compliance-fixtures/README.mdFormal Model
Predicates and invariants (illustrative notation)
Behavioral Coverage Map
P1Bernoulli success predicateTestBernoulliSuccessFromConclusionsuccessconclusion maps to Bernoulli success=1, others to 0P2ET non-negativityTestEffectiveTokensNonNegativeTotalEffectiveTokensin fixture is never negativeP3Zero-ET fixture models missing artifactTestZeroETFixtureIndicatesMissingArtifactrun_summary_zero_et.jsonhas ET == 0 (T-FC-022)P4High-ET overflow boundaryTestHighETFixtureExceedsOverflowThresholdrun_summary_high_et.jsonhas ET >= 1,000,000 (T-ET-006)P5Duration derivation non-negativeTestDurationSecondsNonNegativeRunStartedAt/UpdatedAtand checks it's >= 0P6Failed-run conclusion samplingTestFailedFixtureHasFailureConclusionrun_summary_failed.jsonhas conclusion == "failure" (T-FC-035)P7RunSummary schema field presenceTestRunSummarySchemaFieldsPresentRunID,Conclusion,TotalEffectiveTokens) are populated in minimal fixtureP8Cache-hit determinismTestRunSummaryRoundTripSerializationRunSummaryand asserts equality (stub JSON round-trip)P9Timestamp orderingTestRunStartedBeforeOrEqualUpdatedRunStartedAt <= UpdatedAton minimal fixtureP10Monte Carlo input completenessTestMonteCarloInputFixtureCompletenessGenerated Test Suite
📄 `pkg/cli/forecast_fixtures_formal_test.go`
Usage
pkg/cli/forecast_fixtures_formal_test.go.minimalWorkflowRun/minimalTokenUsageSummary/minimalRunSummarystub structs with the realcli.WorkflowRun/cli.TokenUsageSummary/cli.RunSummarytypes frompkg/cli/logs_models.go(test currently lives in packagecli_test, so unexported fields may need an internal test file in packagecliinstead).run_summary_minimal.jsonis confirmed to exist on disk today;run_summary_zero_et.json,run_summary_failed.json, andrun_summary_high_et.jsonare documented in the README's "Available Additional Fixtures" table but may not yet be materialized —TestMonteCarloInputFixtureCompletenessskips gracefully if a fixture file is missing, butTestZeroETFixtureIndicatesMissingArtifact,TestFailedFixtureHasFailureConclusion, andTestHighETFixtureExceedsOverflowThresholdwill need those fixture files created first (per the README's "Adding New Fixtures" guidance).go test ./pkg/cli/... -run FormalContext
specs/forecast-compliance-fixtures/README.md