From e885ebff76778b576711650c4c31f1891371e05a Mon Sep 17 00:00:00 2001 From: Joshua Temple Date: Sat, 18 Jul 2026 10:00:30 -0400 Subject: [PATCH] test(generate): fill correctness-census followup assertions Turn the reviewed followup markers in the generation-correctness census into named assertions that pin each field's emitted shape, and reclassify the one field with no distinct emitted contract. - gpg_key_id / gpg_key_secret: assert the emitted git setup imports the key, turns on commit.gpgsign, and wires the signing key, so state and finalize commits are actually signed. - run_policy (builds, deploys, validate): assert the emitted job if: gate matches the policy (change-detection gate vs always()). - depends_on / optional_depends_on (build and deploy): assert required deps both sequence and gate, while optional deps only sequence. - environments role: assert role: release moves the release stage off the positional-last environment. - pin_mode: assert sha mode emits SHA refs, tag mode emits tag refs. - release_trigger: assert dispatch drops the push trigger. - reconcile.commit: assert append vs followup emit their distinct steps. - reconcile.source: reclassify as not-emitted (single valid value, no sink). Also add a positive deploy-side permissions case, so a configured deploy permission is asserted to emit, not only the least-privilege omission. Signed-off-by: Joshua Temple --- .../generate/correctness_assertions_test.go | 318 +++++++++++++++++- .../generate/correctness_census_map_test.go | 28 +- 2 files changed, 319 insertions(+), 27 deletions(-) diff --git a/internal/generate/correctness_assertions_test.go b/internal/generate/correctness_assertions_test.go index 9cdeb2f..854a550 100644 --- a/internal/generate/correctness_assertions_test.go +++ b/internal/generate/correctness_assertions_test.go @@ -1,6 +1,7 @@ package generate import ( + "regexp" "testing" "github.com/stretchr/testify/assert" @@ -31,22 +32,50 @@ func correctnessDir(t *testing.T) string { // emitted a default block, or leaked one job's scopes onto another, reds here. func TestGenCorrectness_Permissions_LeastPrivilegePerJob(t *testing.T) { dir := correctnessDir(t) - cfg := guardBaseConfig() - cfg.Builds[0].Permissions = map[string]string{"contents": "read", "id-token": "write"} - out, err := NewGenerator(cfg, dir).Generate() - require.NoError(t, err) + // A configured BUILD permission emits, scoped to that job; the permission-less + // deploy emits no block (it inherits, rather than silently receiving elevated + // defaults) and never inherits the build's scopes. + t.Run("build_configured_deploy_omits", func(t *testing.T) { + cfg := guardBaseConfig() + cfg.Builds[0].Permissions = map[string]string{"contents": "read", "id-token": "write"} - build := pass10JobBlock(t, out, "build-app") - assert.Contains(t, build, "permissions:", "a build with a configured permissions map must emit the block") - assert.Contains(t, build, "contents: read", "configured scopes must be emitted verbatim") - assert.Contains(t, build, "id-token: write") + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) - deploy := pass10JobBlock(t, out, "deploy-runner") - assert.NotContains(t, deploy, "permissions:", - "a job with no configured permissions must emit no block (least privilege, not a default grant)") - assert.NotContains(t, deploy, "id-token: write", - "one job's scopes must never leak onto another job") + build := pass10JobBlock(t, out, "build-app") + assert.Contains(t, build, "permissions:", "a build with a configured permissions map must emit the block") + assert.Contains(t, build, "contents: read", "configured scopes must be emitted verbatim") + assert.Contains(t, build, "id-token: write") + + deploy := pass10JobBlock(t, out, "deploy-runner") + assert.NotContains(t, deploy, "permissions:", + "a job with no configured permissions must emit no block (least privilege, not a default grant)") + assert.NotContains(t, deploy, "id-token: write", + "one job's scopes must never leak onto another job") + }) + + // The mirror image: a configured DEPLOY permission emits exactly that scope on + // the deploy job, and the permission-less build emits no block and never + // inherits the deploy's scope. This positively pins the deploy-side emission + // (the case the build-only scenario left implicit). + t.Run("deploy_configured_build_omits", func(t *testing.T) { + cfg := guardBaseConfig() + cfg.Deploys[0].Permissions = map[string]string{"deployments": "write"} + + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + + deploy := pass10JobBlock(t, out, "deploy-runner") + assert.Contains(t, deploy, "permissions:", "a deploy with a configured permissions map must emit the block") + assert.Contains(t, deploy, "deployments: write", "the configured deploy scope must be emitted verbatim") + + build := pass10JobBlock(t, out, "build-app") + assert.NotContains(t, build, "permissions:", + "a job with no configured permissions must emit no block (least privilege, not a default grant)") + assert.NotContains(t, build, "deployments: write", + "one job's scopes must never leak onto another job") + }) } // TestGenCorrectness_SecretsMap_PropagatesSourceToCallee pins the direction of @@ -198,3 +227,266 @@ func TestGenCorrectness_EnvironmentURL_EmitsPerEnvCase(t *testing.T) { assert.Contains(t, out, "prod) environment_url='https://app.example.com'", "the configured environment_url must be threaded as a per-environment shell case") } + +// TestGenCorrectness_GPGSigning_WiresImportAndSigningKey pins the security- +// critical contract for git.gpg_key_id / git.gpg_key_secret: when both are +// configured, the emitted git setup imports the private key, turns on commit +// signing, AND sets the signing key, so state/finalize commits are actually +// GPG-signed. A regression that dropped the import, the gpgsign toggle, or the +// signingkey wiring would SILENTLY produce unsigned commits (valid YAML, +// actionlint-clean), which is exactly the silent-half defect this census hunts. +// The key id and secret are spliced as GHA secret names into ${{ secrets. }}. +// This is emitted-shape correctness; the runtime proof that GitHub honors the +// signature is fleet-only (Part B). +func TestGenCorrectness_GPGSigning_WiresImportAndSigningKey(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + cfg.Git = &config.GitConfig{GPGKeyID: "GPG_KEY_ID", GPGKeySecret: "GPG_PRIVATE_KEY"} + + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + + assert.Contains(t, out, `echo "${{ secrets.GPG_PRIVATE_KEY }}" | gpg --batch --import`, + "the gpg_key_secret must be imported so the runner holds the private key") + assert.Contains(t, out, "git config commit.gpgsign true", + "commit signing must be turned on, or every commit ships unsigned") + assert.Contains(t, out, `git config user.signingkey "${{ secrets.GPG_KEY_ID }}"`, + "the gpg_key_id must be wired as the signing key") +} + +// TestGenCorrectness_RunPolicy_GatesCallbackIf pins that run_policy shapes the +// emitted job if: gate per callback type. The default policy gates a build/deploy +// on change detection (needs.setup.outputs.run_ == 'true'); "always" wraps +// the gate in always() so the job runs even when upstream jobs are skipped. This +// pins the emitted expression SHAPE; whether always() actually rescues a skipped +// ladder at run time is a GitHub Actions runtime property proven by the fleet +// (Part B). +func TestGenCorrectness_RunPolicy_GatesCallbackIf(t *testing.T) { + t.Run("build_default_gates_on_change", func(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + build := pass10JobBlock(t, out, "build-app") + assert.Contains(t, build, "needs.setup.outputs.run_build_app == 'true'", + "a default-policy build must gate on its change-detection output") + assert.NotContains(t, build, "always()", + "a default-policy build must not run unconditionally") + }) + + t.Run("build_always_runs_unconditionally", func(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + cfg.Builds[0].RunPolicy = config.RunPolicyAlways + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + build := pass10JobBlock(t, out, "build-app") + assert.Contains(t, build, "always()", + "a run_policy: always build must wrap its gate in always()") + }) + + t.Run("deploy_always_runs_unconditionally", func(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + cfg.Deploys[0].RunPolicy = config.RunPolicyAlways + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + deploy := pass10JobBlock(t, out, "deploy-runner") + assert.Contains(t, deploy, "always()", + "a run_policy: always deploy must wrap its gate in always()") + }) + + t.Run("validate_default_no_gate_always_gates", func(t *testing.T) { + dir := callbackWorkflowDir(t, "build.yaml", "deploy.yaml", "validate.yaml") + + base := guardBaseConfig() + base.Validate = &config.ValidateConfig{Workflow: "validate.yaml"} + out, err := NewGenerator(base, dir).Generate() + require.NoError(t, err) + validate := pass10JobBlock(t, out, "validate") + assert.NotContains(t, validate, "if:", + "a default-policy validate runs on every orchestrate and emits no if: gate") + + always := guardBaseConfig() + always.Validate = &config.ValidateConfig{Workflow: "validate.yaml", RunPolicy: config.RunPolicyAlways} + out, err = NewGenerator(always, dir).Generate() + require.NoError(t, err) + validate = pass10JobBlock(t, out, "validate") + assert.Contains(t, validate, "if: always()", + "a run_policy: always validate must gate on always()") + }) +} + +// TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates pins the +// sequencing-versus-gating split for depends_on / optional_depends_on. A REQUIRED +// depends_on emits the dependency both into needs: (sequencing) AND into the job +// if: gate (so a failed dependency skips the dependent). An OPTIONAL +// optional_depends_on emits the dependency into needs: only: it orders the job +// after the dependency without gating on its result. A regression that gated an +// optional edge would wrongly skip a job whose optional upstream failed; one that +// dropped the required gate would run a dependent on a broken upstream. The +// needs: ordering and the if: gate are emitted-shape; the runtime skip behavior +// is a GitHub Actions property proven by the fleet (Part B). +func TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + cfg.Builds = []config.BuildConfig{ + {Name: "lib", Workflow: "build.yaml", Triggers: []string{"lib/**"}}, + {Name: "app", Workflow: "build.yaml", Triggers: []string{"app/**"}, DependsOn: []string{"lib"}}, + {Name: "tool", Workflow: "build.yaml", Triggers: []string{"tool/**"}, OptionalDependsOn: []string{"lib"}}, + } + // A required deploy dependency is pinned by TestGM5; here the deploy carries an + // OPTIONAL dependency to pin the deploy-side sequencing-not-gating contract. + cfg.Deploys = []config.DeployConfig{ + {Name: "runner", Workflow: "deploy.yaml", Triggers: []string{"deploy/**"}, OptionalDependsOn: []string{"lib"}}, + } + + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + + app := pass10JobBlock(t, out, "build-app") + assert.Contains(t, app, "needs: [setup, build-lib]", + "a required depends_on must sequence the dependent after its dependency") + assert.Contains(t, app, "needs.build-lib.result == 'success'", + "a required depends_on must also gate the dependent on the dependency succeeding") + + tool := pass10JobBlock(t, out, "build-tool") + assert.Contains(t, tool, "build-lib", + "an optional_depends_on must still sequence the job after its dependency (in needs:)") + assert.NotContains(t, tool, "needs.build-lib.result", + "an optional_depends_on must NOT gate the job on the dependency's result") + + deploy := pass10JobBlock(t, out, "deploy-runner") + assert.Contains(t, deploy, "build-lib", + "a deploy optional_depends_on must sequence the deploy after its dependency (in needs:)") + assert.NotContains(t, deploy, "needs.build-lib.result", + "a deploy optional_depends_on must NOT gate the deploy on the dependency's result") +} + +// TestGenCorrectness_EnvironmentRole_MovesReleaseStage pins that +// environments[].role: release selects which environment is the release (final) +// stage, overriding the positional default (last entry). The prod deploy job's +// name and environment: input are threaded from ReleaseEnvironment(), so a +// role on a non-last environment moves the release stage there. A regression that +// ignored role would publish the wrong environment as the release. +func TestGenCorrectness_EnvironmentRole_MovesReleaseStage(t *testing.T) { + dir := correctnessDir(t) + cfg := guardBaseConfig() + // role: release on staging (NOT the positional-last prod) moves the release + // stage off the default last entry. + cfg.Environments = []config.EnvironmentEntry{ + {Name: "dev"}, + {Name: "staging", Role: config.EnvRoleRelease}, + {Name: "prod"}, + } + + out, err := NewPromoteGenerator(cfg, dir).Generate() + require.NoError(t, err) + + assert.Contains(t, out, "name: Deploy runner (staging)", + "role: release must move the release stage to the declared environment") + assert.Contains(t, out, "environment: staging", + "the prod deploy job's environment input must be the role-declared release env") + assert.NotContains(t, out, "name: Deploy runner (prod)", + "the positional-last env must not be the release stage once a role overrides it") +} + +// TestGenCorrectness_PinMode_ShaEmitsShaRefs pins that pin_mode selects whether a +// built-in action ref is emitted as a commit SHA (with a version comment) or a +// tag. pin_mode: sha hardens supply-chain posture by pinning to an immutable +// commit; the default (tag) keeps the readable tag. A regression that ignored +// pin_mode would silently downgrade a repo that asked for SHA pinning back to a +// mutable tag. +func TestGenCorrectness_PinMode_ShaEmitsShaRefs(t *testing.T) { + dir := correctnessDir(t) + shaRef := regexp.MustCompile(`uses: actions/checkout@[0-9a-f]{40} #`) + tagRef := regexp.MustCompile(`uses: actions/checkout@v[0-9]`) + + t.Run("sha", func(t *testing.T) { + cfg := guardBaseConfig() + cfg.PinMode = config.PinModeSHA + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + assert.Regexp(t, shaRef, out, "pin_mode: sha must emit a 40-hex commit SHA ref for a built-in action") + assert.NotRegexp(t, tagRef, out, "pin_mode: sha must not leave a built-in action on a mutable tag") + }) + + t.Run("tag_default", func(t *testing.T) { + cfg := guardBaseConfig() + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + assert.Regexp(t, tagRef, out, "the default pin_mode (tag) must emit the readable tag ref") + assert.NotRegexp(t, shaRef, out, "the default pin_mode must not emit a SHA ref") + }) +} + +// TestGenCorrectness_ReleaseTrigger_DispatchDropsPush pins that release_trigger +// selects how orchestrate fires: the default (push) keeps the trunk-push trigger +// so every merge cuts a candidate; dispatch drops the push: trigger so orchestrate +// runs only on workflow_dispatch, handing a maintainer the release gate. A +// regression that ignored dispatch would re-arm push and cut a candidate on every +// merge against the operator's intent. +func TestGenCorrectness_ReleaseTrigger_DispatchDropsPush(t *testing.T) { + dir := correctnessDir(t) + + t.Run("push_default", func(t *testing.T) { + cfg := guardBaseConfig() + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + assert.Contains(t, out, " push:\n branches: [main]", + "the default release_trigger (push) must keep the trunk-push trigger") + }) + + t.Run("dispatch_drops_push", func(t *testing.T) { + cfg := guardBaseConfig() + cfg.ReleaseTrigger = config.ReleaseTriggerDispatch + out, err := NewGenerator(cfg, dir).Generate() + require.NoError(t, err) + assert.NotContains(t, out, " push:\n", + "release_trigger: dispatch must drop the push: trigger so orchestrate runs only on dispatch") + assert.Contains(t, out, "workflow_dispatch:", + "dispatch mode must still expose the workflow_dispatch entry point") + }) +} + +// TestGenCorrectness_ReconcileCommit_AppendVsFollowup pins that reconcile.commit +// routes the same-repo adoption commit: the default (append) pushes onto the +// triggering PR's own head branch (with a fork sticky-comment fallback), while +// followup pushes a separate branch and opens a followup PR (the automerge- +// without-review posture). A regression that swapped the routing would push a +// commit onto the wrong branch, or open a PR when the operator wanted an in-place +// push. This pins the emitted step SHAPE. +func TestGenCorrectness_ReconcileCommit_AppendVsFollowup(t *testing.T) { + base := func() *config.TrunkConfig { + return &config.TrunkConfig{ + TrunkBranch: "main", + Environments: config.EnvNames("dev"), + Reconcile: &config.ReconcileConfig{Enabled: true}, + } + } + + t.Run("append_default", func(t *testing.T) { + cfg := base() + out, err := NewReconcileGenerator(cfg, "").GenerateCompanion() + require.NoError(t, err) + assert.Contains(t, out, "name: Push the reconcile commit", + "the default (append) mode must push onto the triggering PR's head branch") + assert.Contains(t, out, "name: Fork fallback (sticky comment)", + "append mode must carry the mandatory fork sticky-comment fallback") + assert.NotContains(t, out, "name: Push the followup branch", + "append mode must not emit the followup-branch step") + }) + + t.Run("followup", func(t *testing.T) { + cfg := base() + cfg.Reconcile.Commit = config.ReconcileCommitFollowup + out, err := NewReconcileGenerator(cfg, "").GenerateCompanion() + require.NoError(t, err) + assert.Contains(t, out, "name: Push the followup branch", + "followup mode must push a separate branch") + assert.Contains(t, out, "name: Open or update the followup PR", + "followup mode must open a followup PR for automerge-without-review") + assert.NotContains(t, out, "name: Push the reconcile commit", + "followup mode must not push in place onto the PR head branch") + }) +} diff --git a/internal/generate/correctness_census_map_test.go b/internal/generate/correctness_census_map_test.go index 2b01e91..152fe20 100644 --- a/internal/generate/correctness_census_map_test.go +++ b/internal/generate/correctness_census_map_test.go @@ -40,12 +40,12 @@ var correctnessCensus = map[string]correctnessCoverage{ "builds[].secrets.map.*": {marker: markerStructural, note: "same secrets: block, pinned by TestGenCorrectness_SecretsMap_PropagatesSourceToCallee"}, "builds[].triggers[]": {marker: markerValidityOnly, note: "paths-filter glob; validity + round-trip is the contract"}, "builds[].workflow": {marker: markerValidityOnly, note: "reusable-workflow callback path spliced into uses:; validity + round-trip is the contract"}, - "builds[].depends_on[]": {marker: markerFollowup, note: "build-to-build needs: edge gating is not pinned; deploy-side gating is pinned by TestGM5_DependentDeploy_JudgesEffectiveResult"}, - "builds[].optional_depends_on[]": {marker: markerFollowup, note: "optional needs: edge gating (build side) is not pinned"}, + "builds[].depends_on[]": {assertion: "TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates"}, + "builds[].optional_depends_on[]": {assertion: "TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates"}, "builds[].env_inputs[key]": {marker: markerValidityOnly, note: "environment reference key; validity + round-trip is the contract"}, "builds[].env_inputs.*": {marker: markerValidityOnly, note: "env-routed JSON matrix payload; validity + round-trip is the contract"}, "builds[].on_failure": {marker: markerNotEmitted, note: "abort (the only emittable value) adds no distinct output; continue is rejected at validation, pinned by TestActionlint_FeatureMatrix on_failure_continue_rejected"}, - "builds[].run_policy": {marker: markerFollowup, note: "run_policy alters the job if: gate; the emitted if: expression is not yet pinned"}, + "builds[].run_policy": {assertion: "TestGenCorrectness_RunPolicy_GatesCallbackIf"}, "builds[].permissions[key]": {assertion: "TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, "builds[].permissions.*": {marker: markerStructural, note: "same permissions: block, pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, @@ -73,11 +73,11 @@ var correctnessCensus = map[string]correctnessCoverage{ "deploys[].triggers[]": {marker: markerValidityOnly, note: "paths-filter glob; validity + round-trip is the contract"}, "deploys[].workflow": {marker: markerValidityOnly, note: "callback path spliced into uses:; validity + round-trip is the contract"}, "deploys[].depends_on[]": {assertion: "TestGM5_DependentDeploy_JudgesEffectiveResult"}, - "deploys[].optional_depends_on[]": {marker: markerFollowup, note: "optional needs: edge gating (deploy side) is not distinctly pinned; required gating is pinned by TestGM5_DependentDeploy_JudgesEffectiveResult"}, + "deploys[].optional_depends_on[]": {assertion: "TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates"}, "deploys[].env_inputs[key]": {marker: markerValidityOnly, note: "environment reference key; validity + round-trip is the contract"}, "deploys[].env_inputs.*": {assertion: "TestPromoteGenerator_UnresolvedEnvStateRefStaysVisible"}, "deploys[].on_failure": {marker: markerNotEmitted, note: "abort (the only emittable value) adds no distinct output; continue is rejected at validation"}, - "deploys[].run_policy": {marker: markerFollowup, note: "run_policy alters the deploy if: gate; the emitted if: expression is not yet pinned"}, + "deploys[].run_policy": {assertion: "TestGenCorrectness_RunPolicy_GatesCallbackIf"}, "deploys[].permissions[key]": {marker: markerStructural, note: "per-job permissions contract (incl. the least-privilege omission) is pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, "deploys[].permissions.*": {marker: markerStructural, note: "same permissions: block, pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, @@ -93,7 +93,7 @@ var correctnessCensus = map[string]correctnessCoverage{ "environments[].environment_url": {assertion: "TestGenCorrectness_EnvironmentURL_EmitsPerEnvCase"}, "environments[].secrets[]": {marker: markerValidityOnly, note: "secret name in the emitted environment secrets payload; validity + round-trip is the contract"}, "environments[].variables[]": {marker: markerValidityOnly, note: "variable name in the emitted environment payload; validity + round-trip is the contract"}, - "environments[].role": {marker: markerFollowup, note: "role selects prerelease/release promotion stage; the resulting ladder ordering is not pinned"}, + "environments[].role": {assertion: "TestGenCorrectness_EnvironmentRole_MovesReleaseStage"}, "environments[].branch_policy": {marker: markerValidityOnly, note: "environment-provisioning API JSON payload; generation tests presence only"}, "environments[].gha_environment": {marker: markerValidityOnly, note: "environment-provisioning API JSON payload; generation tests presence only"}, "environments[].branch_patterns[]": {marker: markerValidityOnly, note: "environment-provisioning API JSON payload; generation tests presence only"}, @@ -109,7 +109,7 @@ var correctnessCensus = map[string]correctnessCoverage{ "external[].deploys[].triggers[]": {marker: markerValidityOnly, note: "paths-filter glob; validity + round-trip is the contract"}, "external[].deploys[].secrets.map[key]": {marker: markerStructural, note: "shared writeSecretsBlock, pinned by TestGenCorrectness_SecretsMap_PropagatesSourceToCallee"}, "external[].deploys[].secrets.map.*": {marker: markerStructural, note: "shared writeSecretsBlock, pinned by TestGenCorrectness_SecretsMap_PropagatesSourceToCallee"}, - "external[].deploys[].optional_depends_on[]": {marker: markerFollowup, note: "external optional needs: edge gating is not pinned"}, + "external[].deploys[].optional_depends_on[]": {marker: markerFollowup, note: "external optional needs: edge gating is not pinned; the same-repo optional sequencing-not-gating contract is pinned by TestGenCorrectness_DependsOn_SequencesButOnlyRequiredGates, but the external-deploy emit path is distinct and not yet asserted"}, "external[].deploys[].permissions[key]": {marker: markerStructural, note: "per-job permissions contract pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, "external[].deploys[].permissions.*": {marker: markerStructural, note: "same permissions: block, pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, @@ -123,8 +123,8 @@ var correctnessCensus = map[string]correctnessCoverage{ "git.mode": {marker: markerStructural, note: "mode: custom triggers the git identity splice, pinned by TestGenCorrectness_GitCustomUser_SplicesNameAndEmail"}, "git.user_name": {assertion: "TestGenCorrectness_GitCustomUser_SplicesNameAndEmail"}, "git.user_email": {marker: markerStructural, note: "same git config splice, pinned by TestGenCorrectness_GitCustomUser_SplicesNameAndEmail"}, - "git.gpg_key_id": {marker: markerFollowup, note: "GPG key id wired into the signing setup; the emitted signing config is not pinned"}, - "git.gpg_key_secret": {marker: markerFollowup, note: "GPG private-key secret wired into the signing setup; the emitted signing config is not pinned"}, + "git.gpg_key_id": {assertion: "TestGenCorrectness_GPGSigning_WiresImportAndSigningKey"}, + "git.gpg_key_secret": {assertion: "TestGenCorrectness_GPGSigning_WiresImportAndSigningKey"}, // -- manifest addressing -------------------------------------------------- "manifest_file": {assertion: "TestReconcileGenerator_EmitsManifestFlags"}, @@ -172,13 +172,13 @@ var correctnessCensus = map[string]correctnessCoverage{ "validate.secrets.map[key]": {marker: markerStructural, note: "shared writeSecretsBlock, pinned by TestGenCorrectness_SecretsMap_PropagatesSourceToCallee"}, "validate.secrets.map.*": {marker: markerStructural, note: "shared writeSecretsBlock, pinned by TestGenCorrectness_SecretsMap_PropagatesSourceToCallee"}, "validate.on_failure": {marker: markerNotEmitted, note: "abort (the only emittable value) adds no distinct output; continue is rejected at validation"}, - "validate.run_policy": {marker: markerFollowup, note: "run_policy alters the validate if: gate; the emitted if: expression is not yet pinned"}, + "validate.run_policy": {assertion: "TestGenCorrectness_RunPolicy_GatesCallbackIf"}, "validate.permissions[key]": {marker: markerStructural, note: "per-job permissions contract pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, "validate.permissions.*": {marker: markerStructural, note: "same permissions: block, pinned by TestGenCorrectness_Permissions_LeastPrivilegePerJob"}, // -- generator-level selectors -------------------------------------------- - "pin_mode": {marker: markerFollowup, note: "pin_mode (tag|sha) changes whether emitted refs are SHAs or tags; not pinned"}, - "release_trigger": {marker: markerFollowup, note: "release_trigger (push|dispatch) changes the emitted release trigger; not pinned"}, - "reconcile.source": {marker: markerFollowup, note: "reconcile source adapter changes the emitted detector; not pinned"}, - "reconcile.commit": {marker: markerFollowup, note: "reconcile commit mode changes the emitted companion commit step; not pinned"}, + "pin_mode": {assertion: "TestGenCorrectness_PinMode_ShaEmitsShaRefs"}, + "release_trigger": {assertion: "TestGenCorrectness_ReleaseTrigger_DispatchDropsPush"}, + "reconcile.source": {marker: markerNotEmitted, note: "dependabot is the only valid value (validateReconcile) and has no emitted consumer in the generate package: enabling reconcile emits the companion, but source selects nothing distinct in the emitted output today, so there is no shape to pin"}, + "reconcile.commit": {assertion: "TestGenCorrectness_ReconcileCommit_AppendVsFollowup"}, }