diff --git a/.gds/bundle.lock.yaml b/.gds/bundle.lock.yaml index 8431314..41fa04f 100644 --- a/.gds/bundle.lock.yaml +++ b/.gds/bundle.lock.yaml @@ -5,14 +5,14 @@ bundle: version: "0.8.0-dev" release_sequence: 0 channel: "development" - source_tree_digest: "sha256:6a8592e0dfce5ef08447af8ec211bd2cec86989212cd67e34e5a883b65a254b5" - digest: "sha256:d5ffeabd533767685ec8b9dfd201d2eb11b4f02fb0bc8f3d0b90bf87d6fd4111" + source_tree_digest: "sha256:ed474c953d73b3ec1afab5fd9a576cfe593fc979d1010389574acbe097d3ec01" + digest: "sha256:43889e1cb7cc1d2911819e402b9382e32e0d87ceae4b301f7ecb8e97b0501861" projection: - input_digest: "sha256:cc20922901a369d2a459a1e588c355cef791fd5403febe0b8050ec20dc2f0813" - output_digest: "sha256:dea9a8ba1077a5844061077b195fd2264a4dfe4989c774071678fbfeac1f40ca" + input_digest: "sha256:0ede97348a7da35fcd2d86fe3c27782ffb57935712c8cacb9c058440d673c830" + output_digest: "sha256:18fbc4bd685017f12bf53578fb43248f57738685b95a65285101434896aba652" files: - path: ".gds/compiled-policy.json" digest: "sha256:b5517ed46f67866220c2b18dbfbda4a40d99f00327611e56742a118d0ac59d0b" - path: ".github/workflows/gds-ci.yml" - digest: "sha256:b080309060c56fb05eb6ad3890d9fea6531fa58b87cd384eed61a2d2d0b6863b" + digest: "sha256:b5174442c13c9f3b9cea05da57296239a72b3819d4064a46af14cec97fcacb79" diff --git a/.github/workflows/gds-ci.yml b/.github/workflows/gds-ci.yml index 78773d0..23eac94 100644 --- a/.github/workflows/gds-ci.yml +++ b/.github/workflows/gds-ci.yml @@ -1,8 +1,8 @@ # GENERATED FILE - DO NOT EDIT DIRECTLY # generator: gds # bundle: 0.8.0-dev -# source-tree-digest: sha256:6a8592e0dfce5ef08447af8ec211bd2cec86989212cd67e34e5a883b65a254b5 -# input-digest: sha256:cc20922901a369d2a459a1e588c355cef791fd5403febe0b8050ec20dc2f0813 +# source-tree-digest: sha256:ed474c953d73b3ec1afab5fd9a576cfe593fc979d1010389574acbe097d3ec01 +# input-digest: sha256:0ede97348a7da35fcd2d86fe3c27782ffb57935712c8cacb9c058440d673c830 # output-digest: sha256:01fb4854784be9e4564bcc84e70786484b370879be5e5ab1dd49f8b73ea2dea4 # edit-source: # - .gds/repository.yaml diff --git a/core/app/projection_operations.go b/core/app/projection_operations.go index d3656dd..79f04be 100644 --- a/core/app/projection_operations.go +++ b/core/app/projection_operations.go @@ -139,6 +139,20 @@ func (services *Services) PlanRepositoryProjection( Plan: plan, StatePath: statePath, Candidate: current.candidate, }) envelope.Scope["repository_id"] = current.repositoryID + // A lock generated over uncommitted sources binds worktree content: the + // moment those files are committed, the canonical source digest moves and + // the freshly applied lock reads as GDS_CONTEXT_POLICY_SOURCE_DIGEST_ + // MISMATCH. That cost two regenerate round-trips in one day before it was + // named. The plan still works -- a deliberate worktree materialization is + // legitimate -- so this is a warning at the one moment an operator reads + // findings, not a refusal. + if status, statusErr := services.Git.InspectStatus(ctx, current.root); statusErr == nil && + status.Changes.Staged+status.Changes.Unstaged+status.Changes.Conflicted > 0 { + envelope.Findings = append(envelope.Findings, domain.Finding{ + Code: "GDS_PROJECTION_SOURCES_UNCOMMITTED", Severity: domain.SeverityMedium, + Message: "Tracked sources are uncommitted: a lock applied from this plan will mismatch the canonical source digest once they are committed. Commit first, then plan, then apply.", + }) + } return envelope } diff --git a/core/cli/root_test.go b/core/cli/root_test.go index 8d0c680..d5b9ed8 100644 --- a/core/cli/root_test.go +++ b/core/cli/root_test.go @@ -177,6 +177,14 @@ func TestGenerateRepositoryReturnsCandidateWithoutMutation(t *testing.T) { if envelope.Mutation.Attempted || envelope.Mutation.Completed { t.Fatalf("mutation = %#v", envelope.Mutation) } + // A committed tree plans without the uncommitted-sources warning; the + // warning's loud half fires only on dirty tracked files, where a lock + // applied now would mismatch the canonical digest after the commit. + for _, finding := range envelope.Findings { + if finding.Code == "GDS_PROJECTION_SOURCES_UNCOMMITTED" { + t.Fatalf("clean tree warned about uncommitted sources: %#v", finding) + } + } assertEnvelopeSchema(t, envelope) }