1919 validate_multi_mode_deployment ,
2020 validate_running_environment ,
2121)
22+ from openenv .validation import (
23+ format_shared_validation_report ,
24+ run_local_validation ,
25+ ValidationProfile ,
26+ )
2227
2328
2429def _looks_like_url (value : str ) -> bool :
@@ -48,9 +53,23 @@ def validate(
4853 bool ,
4954 typer .Option (
5055 "--json" ,
51- help = "Output local validation report as JSON (runtime validation is JSON by default) " ,
56+ help = "Output the RFC 008 shared validation report as JSON " ,
5257 ),
5358 ] = False ,
59+ profile : Annotated [
60+ str | None ,
61+ typer .Option (
62+ "--profile" ,
63+ help = "Validation profile: static, runtime, or full" ,
64+ ),
65+ ] = None ,
66+ output : Annotated [
67+ Path | None ,
68+ typer .Option (
69+ "--output" ,
70+ help = "Write the shared JSON validation report to this path" ,
71+ ),
72+ ] = None ,
5473 timeout : Annotated [
5574 float ,
5675 typer .Option (
@@ -74,6 +93,11 @@ def validate(
7493
7594 Runtime validation checks if a live OpenEnv server conforms to the
7695 versioned runtime API contract and returns a criteria-based JSON report.
96+ Explicit static, runtime, and full profiles emit the RFC 008 shared report.
97+ Reports identify the served OpenEnv spec and pinned adapter; external task
98+ package formats are intentionally outside this command's dispatch surface.
99+ Automatic runtime launch is intended for trusted local source; connect to
100+ an already isolated server with `--url` for untrusted environments.
77101
78102 Examples:
79103
@@ -91,6 +115,9 @@ def validate(
91115
92116 # Validate specific environment
93117 $ openenv validate envs/echo_env
118+
119+ # Run every locally available check and record remote-only skips
120+ $ openenv validate envs/echo_env --profile full --output report.json
94121 ```
95122 """
96123 runtime_target = url
@@ -114,6 +141,80 @@ def validate(
114141 raise typer .Exit (1 )
115142 runtime_target = target
116143
144+ # Machine-readable and explicit-profile invocations use the RFC 008 shared
145+ # report. Only the unqualified human rendering stays on the legacy path.
146+ if profile is not None or output is not None or json_output :
147+ if profile is None :
148+ selected_profile = (
149+ ValidationProfile .RUNTIME
150+ if runtime_target is not None
151+ else ValidationProfile .STATIC
152+ )
153+ else :
154+ try :
155+ selected_profile = ValidationProfile (profile .lower ())
156+ except ValueError as exc :
157+ typer .echo (
158+ "Error: --profile must be one of: static, runtime, full" ,
159+ err = True ,
160+ )
161+ raise typer .Exit (1 ) from exc
162+
163+ if runtime_target is not None and selected_profile is ValidationProfile .STATIC :
164+ typer .echo (
165+ "Error: The static profile requires a local source directory" ,
166+ err = True ,
167+ )
168+ raise typer .Exit (1 )
169+
170+ if runtime_target is not None :
171+ shared_target : str | Path = runtime_target
172+ shared_runtime_url = runtime_target
173+ else :
174+ shared_target = Path .cwd () if target is None else Path (target )
175+ shared_runtime_url = None
176+ if not shared_target .exists ():
177+ typer .echo (f"Error: Path does not exist: { shared_target } " , err = True )
178+ raise typer .Exit (1 )
179+ if not shared_target .is_dir ():
180+ typer .echo (f"Error: Path is not a directory: { shared_target } " , err = True )
181+ raise typer .Exit (1 )
182+
183+ try :
184+ report = run_local_validation (
185+ shared_target ,
186+ profile = selected_profile ,
187+ runtime_url = shared_runtime_url ,
188+ timeout_s = timeout ,
189+ )
190+ except ValueError as exc :
191+ typer .echo (f"Error: { exc } " , err = True )
192+ raise typer .Exit (1 ) from exc
193+ payload = report .to_dict ()
194+ serialized = json .dumps (payload , indent = 2 )
195+
196+ if output is not None :
197+ try :
198+ output .parent .mkdir (parents = True , exist_ok = True )
199+ output .write_text (f"{ serialized } \n " , encoding = "utf-8" )
200+ except OSError as exc :
201+ typer .echo (
202+ f"Error: Unable to write validation report: { type (exc ).__name__ } " ,
203+ err = True ,
204+ )
205+ raise typer .Exit (1 ) from exc
206+
207+ if json_output :
208+ typer .echo (serialized )
209+ else :
210+ typer .echo (format_shared_validation_report (report ))
211+ if output is not None :
212+ typer .echo (f"Report written to { output } " )
213+
214+ if not report .passed :
215+ raise typer .Exit (1 )
216+ return
217+
117218 if runtime_target is not None :
118219 try :
119220 report = validate_running_environment (runtime_target , timeout_s = timeout )
0 commit comments