diff --git a/docs/src/content/docs/specs/effective-tokens-specification.md b/docs/src/content/docs/specs/effective-tokens-specification.md index d8fbb32dcfd..fa79ac1dccd 100644 --- a/docs/src/content/docs/specs/effective-tokens-specification.md +++ b/docs/src/content/docs/specs/effective-tokens-specification.md @@ -18,7 +18,7 @@ sidebar: --- > [!CAUTION] -> **Deprecated.** The Effective Tokens (ET) metric is deprecated and is superseded by **AI Credits (AIC)**. AIC is a direct monetary metric (1 AIC = $0.01 USD) derived from actual provider pricing and provides a more actionable measure of inference cost. Effective Tokens remain available in `gh aw logs` and `gh aw audit` output for backward compatibility, but AIC is now the preferred metric for monitoring and budgeting. See [Cost Management](/gh-aw/reference/cost-management/) for AIC usage examples. +> **Deprecated.** The Effective Tokens (ET) metric is deprecated and is superseded by **AI Credits (AIC)**. AIC is a direct monetary metric (1 AIC = $0.01 USD) derived from actual provider pricing and provides a more actionable measure of inference cost. Effective Tokens remain available in `gh aw logs` and `gh aw audit` output for backward compatibility, but AIC is now the preferred metric for monitoring and budgeting. See [Cost Management](/gh-aw/reference/cost-management/) for AIC usage examples. Migration path: [AI Credits Specification](/gh-aw/specs/ai-credits-specification/). --- diff --git a/docs/src/content/docs/specs/forecast-specification.md b/docs/src/content/docs/specs/forecast-specification.md index 909ce77582a..eda4f5711de 100644 --- a/docs/src/content/docs/specs/forecast-specification.md +++ b/docs/src/content/docs/specs/forecast-specification.md @@ -1040,6 +1040,7 @@ This section maps normative forecast requirements to implementation files. | Monte Carlo engine (Poisson/Bootstrap/Bernoulli) | `pkg/cli/forecast_montecarlo.go` | | Forecast command orchestration and output fields | `pkg/cli/forecast.go`, `pkg/cli/forecast_command.go` | | Workflow discovery, rate-limit backoff, and run sampling | `pkg/cli/forecast.go` | +| §10.7 Safeguards and Appendix F cross-reference | `pkg/cli/forecast.go`, `pkg/cli/forecast_render.go` | | Forecast compliance tests (including rate-limit backoff and λ thresholds) | `pkg/cli/forecast_montecarlo_test.go` | Sync procedure: @@ -1049,8 +1050,13 @@ Sync procedure: Sync follow-up tasks: -- **[Open]** Add and verify explicit AIC-primary / ET-legacy migration assertions for forecast - reporting paths so that ET-facing surfaces remain compatibility-only where applicable. +- **[Resolved 2026-07-07]** Added explicit AIC-primary / ET-legacy migration assertions for + forecast reporting paths. For `pkg/cli/forecast.go` output wiring, AIC-primary fields are: + `avg_aic`, `p50_aic_per_run`, `p95_aic_per_run`, `projected_aic`, `weekly_projected_aic`, + `monthly_projected_aic`, `actual_aic`, and Monte Carlo summary fields ending in + `_projected_aic`. ET-legacy compatibility-only status: no ET-named forecast output fields are + emitted by `gh aw forecast`; ET remains legacy compatibility surface in `gh aw logs`/`gh aw audit`. + Tracking: [#44083](https://github.com/github/gh-aw/issues/44083). - **[Open]** Track promotion-gate evidence until three confirmed production runs are documented in the Promotion Tracking table (§Status of This Document, criterion 1). - **[Open]** Close the forecast compliance-test gap for promotion criterion 3 by recording sustained diff --git a/docs/src/content/docs/specs/frontmatter-hash-specification.md b/docs/src/content/docs/specs/frontmatter-hash-specification.md index cad2865c1e9..e74301bdbe8 100644 --- a/docs/src/content/docs/specs/frontmatter-hash-specification.md +++ b/docs/src/content/docs/specs/frontmatter-hash-specification.md @@ -509,6 +509,17 @@ those prerequisites are met and the migration milestone is approved. **Sync verification (2026-05-12)**: SPDD review reconfirmed that the 2026-05-08 text-based resolution remains in force. +### Deferral acceptance criteria (Section 2 / v2.0.0) + +Section 2 ("Field Selection") remains deferred pending completion of +[#31983](https://github.com/github/gh-aw/issues/31983). Reviewers SHOULD use this subsection as +the canonical acceptance checklist during Sync Notes review (without requiring issue-context lookup). + +- [ ] Confirm and document a selective field-exclusion use case in [#31983](https://github.com/github/gh-aw/issues/31983). +- [ ] Draft a migration guide in [#31983](https://github.com/github/gh-aw/issues/31983), including lock-file invalidation and recompilation steps. +- [ ] Write candidate v2.0.0 cross-language test vectors in [#31983](https://github.com/github/gh-aw/issues/31983) and verify they pass in CI. +- [ ] Approve a rollout plan in [#31983](https://github.com/github/gh-aw/issues/31983), including backward-compatibility impact analysis. + **S-6 sync verification (2026-05-28)**: The 1 MiB cumulative input-size guard (S-6) is confirmed implemented in `pkg/parser/frontmatter_hash.go` via `validateFrontmatterHashInputSize`. The function accumulates normalized byte lengths across the main and all imported frontmatter texts and returns a diff --git a/docs/src/content/docs/specs/mcp-scripts-specification.md b/docs/src/content/docs/specs/mcp-scripts-specification.md index e0695ca0139..dc660ba32ff 100644 --- a/docs/src/content/docs/specs/mcp-scripts-specification.md +++ b/docs/src/content/docs/specs/mcp-scripts-specification.md @@ -1638,6 +1638,7 @@ and run `go test ./pkg/workflow/...` to verify conformance. | §9 | Integration with MCP Gateway | `pkg/workflow/mcp_scripts_renderer.go` (`renderMCPScriptsMCPConfigWithOptions`), `pkg/workflow/mcp_scripts_generator.go` (`GenerateMCPScriptsMCPServerScript`, `GenerateMCPScriptsToolsConfig`) | | §10 | Safeguards | `pkg/workflow/mcp_scripts_renderer.go`, `pkg/workflow/mcp_scripts_generator.go`, `actions/setup/js/mcp_scripts_mcp_server_http.cjs` | | §11 | Norms | `pkg/workflow/mcp_scripts_renderer.go`, `pkg/workflow/mcp_scripts_parser.go` | +| §12.1.4 / T-MCP-051 | Secret-scope isolation conformance (SN-SCOPE-01..04) | `pkg/workflow/mcp_scripts_renderer_test.go` (`TestMCPScriptsScopeConformance`) — **[Open]** | Sync follow-up tasks: @@ -1694,7 +1695,7 @@ Sync follow-up tasks: ### Go Sandbox Constraints for `go` Language Tools When `implementation: go` is specified, the tool executes inside a containerized Go -sandbox with the following constraints: +sandbox with the following constraints (see also §10 Safeguards for normative controls): - **Network access**: No outbound network calls are permitted from within the Go tool sandbox unless the workflow's `network.allowed` list explicitly includes the target host. The diff --git a/pkg/parser/schedule_fuzzy_scatter_test.go b/pkg/parser/schedule_fuzzy_scatter_test.go index 10055c49325..63e508e0110 100644 --- a/pkg/parser/schedule_fuzzy_scatter_test.go +++ b/pkg/parser/schedule_fuzzy_scatter_test.go @@ -880,3 +880,35 @@ func TestScatterScheduleUsesPreferredWindows(t *testing.T) { } } } + +func TestWeightedDailyTimeSlotBestTierFrequency(t *testing.T) { + const sampleSize = 50000 + + var bestTotal, okTotal int + + for i := range sampleSize { + identifier := fmt.Sprintf("scope-check-workflow-%05d", i) + hour, _ := weightedDailyTimeSlot(identifier) + + if hour >= 2 && hour <= 5 { + bestTotal++ + } + if hour >= 19 && hour <= 23 { + okTotal++ + } + } + + if okTotal == 0 { + t.Fatalf("unexpected zero OK-tier samples in weighted scatter distribution") + } + + bestPerHour := float64(bestTotal) / 4.0 // BEST hours: 02-05 + okPerHour := float64(okTotal) / 5.0 // OK hours: 19-23 + ratio := bestPerHour / okPerHour + + // The weighted pool gives each BEST hour weight 3 versus weight 1 for each OK hour. + // Allow tolerance for deterministic hash-modulo skew while preserving the intended 3x shape. + if ratio < 2.7 { + t.Fatalf("expected BEST-hour frequency to be near 3x OK-hour frequency, got ratio=%.3f (best_total=%d ok_total=%d)", ratio, bestTotal, okTotal) + } +} diff --git a/pkg/workflow/mcp_scripts_renderer_test.go b/pkg/workflow/mcp_scripts_renderer_test.go index 6998956a61b..a70e9e3d960 100644 --- a/pkg/workflow/mcp_scripts_renderer_test.go +++ b/pkg/workflow/mcp_scripts_renderer_test.go @@ -3,6 +3,8 @@ package workflow import ( + "encoding/json" + "strings" "testing" ) @@ -82,3 +84,66 @@ func TestCollectMCPScriptsSecretsStability(t *testing.T) { } } } + +func TestMCPScriptsScopeConformance(t *testing.T) { + config := &MCPScriptsConfig{ + Tools: map[string]*MCPScriptToolConfig{ + "alpha-tool": { + Name: "alpha-tool", + Env: map[string]string{ + "ALPHA_ONLY": "${{ secrets.ALPHA_ONLY }}", + "SHARED_SECRET": "${{ secrets.SHARED_SECRET }}", + }, + }, + "beta-tool": { + Name: "beta-tool", + Env: map[string]string{ + "BETA_ONLY": "${{ secrets.BETA_ONLY }}", + "SHARED_SECRET": "${{ secrets.SHARED_SECRET }}", + }, + }, + }, + } + + toolsJSON := GenerateMCPScriptsToolsConfig(config) + if strings.Contains(toolsJSON, "secrets.ALPHA_ONLY") || + strings.Contains(toolsJSON, "secrets.BETA_ONLY") || + strings.Contains(toolsJSON, "secrets.SHARED_SECRET") { + t.Fatalf("tools.json must not contain secret expressions: %s", toolsJSON) + } + + var rendered MCPScriptsConfigJSON + if err := json.Unmarshal([]byte(toolsJSON), &rendered); err != nil { + t.Fatalf("failed to parse tools.json: %v", err) + } + + var alphaEnv, betaEnv map[string]string + for _, tool := range rendered.Tools { + switch tool.Name { + case "alpha-tool": + alphaEnv = tool.Env + case "beta-tool": + betaEnv = tool.Env + } + } + + if alphaEnv == nil || betaEnv == nil { + t.Fatalf("expected both alpha-tool and beta-tool in tools.json: %+v", rendered.Tools) + } + + // SN-SCOPE-01/03: per-tool secret bindings are explicit and isolated in tools.json. + if alphaEnv["ALPHA_ONLY"] != "ALPHA_ONLY" || alphaEnv["SHARED_SECRET"] != "SHARED_SECRET" { + t.Fatalf("alpha-tool env bindings are incorrect: %+v", alphaEnv) + } + if betaEnv["BETA_ONLY"] != "BETA_ONLY" || betaEnv["SHARED_SECRET"] != "SHARED_SECRET" { + t.Fatalf("beta-tool env bindings are incorrect: %+v", betaEnv) + } + + // SN-SCOPE-02: tool-specific bindings do not include other tool-only secrets. + if _, exists := alphaEnv["BETA_ONLY"]; exists { + t.Fatalf("alpha-tool env must not include beta-tool secret binding: %+v", alphaEnv) + } + if _, exists := betaEnv["ALPHA_ONLY"]; exists { + t.Fatalf("beta-tool env must not include alpha-tool secret binding: %+v", betaEnv) + } +}