@@ -10,6 +10,7 @@ import (
1010
1111 "github.com/LAA-Software-Engineering/agentic-control-plane/internal/config"
1212 "github.com/LAA-Software-Engineering/agentic-control-plane/internal/plan"
13+ "github.com/LAA-Software-Engineering/agentic-control-plane/internal/policy"
1314 "github.com/LAA-Software-Engineering/agentic-control-plane/internal/render"
1415 "github.com/LAA-Software-Engineering/agentic-control-plane/internal/state/sqlite"
1516 "github.com/spf13/cobra"
@@ -28,8 +29,10 @@ unless overridden by global --state.
2829
2930Environment for stored rows is taken from -e / --env when set, otherwise "local".
3031
31- Writes .agentic/resolved-config.json (digest of resolved graph + env + state path) for plan→run
32- contract checks. JSON/YAML output includes resolvedConfigDigest alongside deploymentBaseline.
32+ Writes .agentic/resolved-config.json (digest of resolved graph + env + state path) and
33+ .agentic/policy-snapshot.json (compiled effective policy digest) for plan→run contract checks.
34+ JSON/YAML output includes resolvedConfigDigest, policyDigest, and effectivePolicy (project
35+ default policy only; workflows/agents may bind other policy names) alongside deploymentBaseline.
3336
3437Exit codes (section 11.2):
3538 0 — success
@@ -80,8 +83,8 @@ func runPlan(cmd *cobra.Command, args []string) error {
8083 if err := writePlanOutput (cmd , env , dsn , pl , rc , g ); err != nil {
8184 return err
8285 }
83- if err := config . WriteSnapshot (rc ); err != nil {
84- return fmt .Errorf ("plan: write resolved config snapshot: %w" , err )
86+ if err := persistSnapshots (rc ); err != nil {
87+ return fmt .Errorf ("plan: %w" , err )
8588 }
8689 return nil
8790}
@@ -97,8 +100,18 @@ func writePlanOutput(cmd *cobra.Command, env, dsn string, p *plan.Plan, rc *conf
97100 if _ , err := fmt .Fprintf (out , "Environment: %s\n State: %s\n \n " , env , dsn ); err != nil {
98101 return err
99102 }
100- _ , err := fmt .Fprintf (out , "%s\n " , plan .FormatPlan (p ))
101- return err
103+ if _ , err := fmt .Fprintf (out , "%s" , plan .FormatPlan (p )); err != nil {
104+ return err
105+ }
106+ if section := compiledPolicyPlanSection (rc ); section != "" {
107+ if _ , err := fmt .Fprintf (out , "%s\n " , section ); err != nil {
108+ return err
109+ }
110+ } else {
111+ _ , err := fmt .Fprintln (out )
112+ return err
113+ }
114+ return nil
102115 }
103116}
104117
@@ -158,9 +171,52 @@ func planJSONModel(env, dsn string, p *plan.Plan, rc *config.ResolvedConfig) map
158171 if rc != nil && rc .Digest () != "" {
159172 m ["resolvedConfigDigest" ] = rc .Digest ()
160173 }
174+ if rc != nil {
175+ if cp , digest , err := compiledPolicySummary (rc ); err == nil && cp != nil {
176+ m ["policyDigest" ] = digest
177+ m ["effectivePolicy" ] = effectivePolicyJSON (cp )
178+ }
179+ }
161180 return m
162181}
163182
183+ // compiledPolicySummary returns the snapshot-set digest and the compiled project-default policy.
184+ // Workflows and agents may reference other policy names; only the default is shown in plan output.
185+ func compiledPolicySummary (rc * config.ResolvedConfig ) (* policy.CompiledPolicy , string , error ) {
186+ graph := rc .Graph ()
187+ policies , err := policy .CompileReferenced (graph )
188+ if err != nil {
189+ return nil , "" , err
190+ }
191+ digest , err := policy .SnapshotSetDigest (policies )
192+ if err != nil {
193+ return nil , "" , err
194+ }
195+ name := policy .DefaultPolicyName (graph )
196+ return policies [name ], digest , nil
197+ }
198+
199+ func compiledPolicyPlanSection (rc * config.ResolvedConfig ) string {
200+ cp , _ , err := compiledPolicySummary (rc )
201+ if err != nil || cp == nil {
202+ return ""
203+ }
204+ return plan .FormatEffectivePolicy (policy .DefaultPolicyName (rc .Graph ()), cp )
205+ }
206+
207+ func effectivePolicyJSON (cp * policy.CompiledPolicy ) []map [string ]string {
208+ entries := cp .EffectivePolicyEntries ()
209+ out := make ([]map [string ]string , len (entries ))
210+ for i , e := range entries {
211+ out [i ] = map [string ]string {
212+ "tool" : e .Tool ,
213+ "decision" : string (e .Decision ),
214+ "source" : string (e .Source ),
215+ }
216+ }
217+ return out
218+ }
219+
164220func riskStrings (p * plan.Plan ) []string {
165221 if p == nil || len (p .Risk .Messages ) == 0 {
166222 return []string {}
0 commit comments