Skip to main content

codex/
wrapper_coverage_manifest.rs

1use serde::{Deserialize, Serialize};
2
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
4#[serde(rename_all = "snake_case")]
5pub enum CoverageLevel {
6    Explicit,
7    Passthrough,
8    Unsupported,
9    IntentionallyUnsupported,
10    Unknown,
11}
12
13#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
14pub struct WrapperSurfaceScopedTargets {
15    #[serde(skip_serializing_if = "Option::is_none")]
16    pub platforms: Option<Vec<String>>,
17    #[serde(skip_serializing_if = "Option::is_none")]
18    pub target_triples: Option<Vec<String>>,
19}
20
21#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
22pub struct WrapperFlagCoverageV1 {
23    pub key: String,
24    pub level: CoverageLevel,
25    #[serde(skip_serializing_if = "Option::is_none")]
26    pub note: Option<String>,
27    #[serde(skip_serializing_if = "Option::is_none")]
28    pub scope: Option<WrapperSurfaceScopedTargets>,
29}
30
31#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
32pub struct WrapperArgCoverageV1 {
33    pub name: String,
34    pub level: CoverageLevel,
35    #[serde(skip_serializing_if = "Option::is_none")]
36    pub note: Option<String>,
37    #[serde(skip_serializing_if = "Option::is_none")]
38    pub scope: Option<WrapperSurfaceScopedTargets>,
39}
40
41#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
42pub struct WrapperCommandCoverageV1 {
43    pub path: Vec<String>,
44    pub level: CoverageLevel,
45    #[serde(skip_serializing_if = "Option::is_none")]
46    pub note: Option<String>,
47    #[serde(skip_serializing_if = "Option::is_none")]
48    pub scope: Option<WrapperSurfaceScopedTargets>,
49    #[serde(skip_serializing_if = "Option::is_none")]
50    pub flags: Option<Vec<WrapperFlagCoverageV1>>,
51    #[serde(skip_serializing_if = "Option::is_none")]
52    pub args: Option<Vec<WrapperArgCoverageV1>>,
53}
54
55#[derive(Debug, Clone, PartialEq, Eq, Serialize, Deserialize)]
56pub struct WrapperCoverageManifestV1 {
57    pub schema_version: u32,
58    #[serde(skip_serializing_if = "Option::is_none")]
59    pub generated_at: Option<String>,
60    #[serde(skip_serializing_if = "Option::is_none")]
61    pub wrapper_version: Option<String>,
62    pub coverage: Vec<WrapperCommandCoverageV1>,
63}
64
65pub fn wrapper_crate_version() -> &'static str {
66    env!("CARGO_PKG_VERSION")
67}
68
69/// The single source of truth for wrapper coverage declarations.
70///
71/// This value is consumed by `xtask codex-wrapper-coverage` to generate
72/// `cli_manifests/codex/wrapper_coverage.json`.
73pub fn wrapper_coverage_manifest() -> WrapperCoverageManifestV1 {
74    fn flag(key: &str, level: CoverageLevel) -> WrapperFlagCoverageV1 {
75        WrapperFlagCoverageV1 {
76            key: key.to_string(),
77            level,
78            note: None,
79            scope: None,
80        }
81    }
82
83    fn flag_note(key: &str, level: CoverageLevel, note: &str) -> WrapperFlagCoverageV1 {
84        WrapperFlagCoverageV1 {
85            key: key.to_string(),
86            level,
87            note: Some(note.to_string()),
88            scope: None,
89        }
90    }
91
92    fn arg(name: &str, level: CoverageLevel) -> WrapperArgCoverageV1 {
93        WrapperArgCoverageV1 {
94            name: name.to_string(),
95            level,
96            note: None,
97            scope: None,
98        }
99    }
100
101    fn arg_note(name: &str, level: CoverageLevel, note: &str) -> WrapperArgCoverageV1 {
102        WrapperArgCoverageV1 {
103            name: name.to_string(),
104            level,
105            note: Some(note.to_string()),
106            scope: None,
107        }
108    }
109
110    fn command(
111        path: &[&str],
112        level: CoverageLevel,
113        note: Option<&str>,
114        flags: Vec<WrapperFlagCoverageV1>,
115        args: Vec<WrapperArgCoverageV1>,
116    ) -> WrapperCommandCoverageV1 {
117        WrapperCommandCoverageV1 {
118            path: path.iter().map(|s| s.to_string()).collect(),
119            level,
120            note: note.map(|s| s.to_string()),
121            scope: None,
122            flags: (!flags.is_empty()).then_some(flags),
123            args: (!args.is_empty()).then_some(args),
124        }
125    }
126
127    WrapperCoverageManifestV1 {
128        schema_version: 1,
129        generated_at: None,
130        wrapper_version: None,
131        coverage: vec![
132            // Scenario 0: root/global flags and probe flags.
133            command(
134                &[],
135                CoverageLevel::Explicit,
136                None,
137                vec![
138                    flag("--help", CoverageLevel::Explicit),
139                    flag("--version", CoverageLevel::Explicit),
140                    flag("--model", CoverageLevel::Explicit),
141                    flag("--image", CoverageLevel::Explicit),
142                    flag_note("--add-dir", CoverageLevel::Explicit, "capability-guarded"),
143                    flag_note(
144                        "--config",
145                        CoverageLevel::Passthrough,
146                        "Generic config overrides are forwarded as strings; keys are not typed/validated by the wrapper.",
147                    ),
148                    flag_note(
149                        "--enable",
150                        CoverageLevel::Passthrough,
151                        "Generic feature toggles are forwarded as strings; individual feature flags are not typed by the wrapper.",
152                    ),
153                    flag_note(
154                        "--disable",
155                        CoverageLevel::Passthrough,
156                        "Generic feature toggles are forwarded as strings; individual feature flags are not typed by the wrapper.",
157                    ),
158                    flag("--profile", CoverageLevel::Explicit),
159                    flag("--cd", CoverageLevel::Explicit),
160                    flag("--remote", CoverageLevel::Explicit),
161                    flag("--remote-auth-token-env", CoverageLevel::Explicit),
162                    flag("--ask-for-approval", CoverageLevel::Explicit),
163                    flag("--sandbox", CoverageLevel::Explicit),
164                    flag("--full-auto", CoverageLevel::Explicit),
165                    flag(
166                        "--dangerously-bypass-approvals-and-sandbox",
167                        CoverageLevel::Explicit,
168                    ),
169                    flag("--local-provider", CoverageLevel::Explicit),
170                    flag("--oss", CoverageLevel::Explicit),
171                    flag("--search", CoverageLevel::Explicit),
172                ],
173                vec![],
174            ),
175            // Scenario 1+2: `codex exec` (single-response + streaming).
176            command(
177                &["exec"],
178                CoverageLevel::Explicit,
179                None,
180                vec![
181                    flag("--color", CoverageLevel::Explicit),
182                    flag("--ephemeral", CoverageLevel::Explicit),
183                    flag("--ignore-rules", CoverageLevel::Explicit),
184                    flag("--ignore-user-config", CoverageLevel::Explicit),
185                    flag("--skip-git-repo-check", CoverageLevel::Explicit),
186                    flag("--json", CoverageLevel::Explicit),
187                    flag("--output-last-message", CoverageLevel::Explicit),
188                    flag_note(
189                        "--output-schema",
190                        CoverageLevel::Explicit,
191                        "capability-guarded",
192                    ),
193                ],
194                vec![arg("PROMPT", CoverageLevel::Explicit)],
195            ),
196            // Scenario 3: `codex exec resume` (streaming resume).
197            command(
198                &["exec", "resume"],
199                CoverageLevel::Explicit,
200                None,
201                vec![
202                    flag("--ephemeral", CoverageLevel::Explicit),
203                    flag("--ignore-rules", CoverageLevel::Explicit),
204                    flag("--ignore-user-config", CoverageLevel::Explicit),
205                    flag("--json", CoverageLevel::Explicit),
206                    flag("--output-last-message", CoverageLevel::Explicit),
207                    flag("--skip-git-repo-check", CoverageLevel::Explicit),
208                    flag("--last", CoverageLevel::Explicit),
209                    flag("--all", CoverageLevel::Explicit),
210                ],
211                vec![
212                    arg("PROMPT", CoverageLevel::Explicit),
213                    arg("SESSION_ID", CoverageLevel::Explicit),
214                ],
215            ),
216            // Scenario 4: `codex apply <TASK_ID>`.
217            command(
218                &["apply"],
219                CoverageLevel::Explicit,
220                None,
221                vec![],
222                vec![arg("TASK_ID", CoverageLevel::Explicit)],
223            ),
224            // Scenario 4: `codex cloud diff <TASK_ID>`.
225            command(
226                &["cloud", "diff"],
227                CoverageLevel::Explicit,
228                None,
229                vec![flag("--attempt", CoverageLevel::Explicit)],
230                vec![arg("TASK_ID", CoverageLevel::Explicit)],
231            ),
232            command(
233                &["cloud", "apply"],
234                CoverageLevel::Explicit,
235                None,
236                vec![flag("--attempt", CoverageLevel::Explicit)],
237                vec![arg("TASK_ID", CoverageLevel::Explicit)],
238            ),
239            command(
240                &["cloud", "status"],
241                CoverageLevel::Explicit,
242                None,
243                vec![],
244                vec![arg("TASK_ID", CoverageLevel::Explicit)],
245            ),
246            command(
247                &["cloud", "list"],
248                CoverageLevel::Explicit,
249                None,
250                vec![
251                    flag("--cursor", CoverageLevel::Explicit),
252                    flag("--env", CoverageLevel::Explicit),
253                    flag("--json", CoverageLevel::Explicit),
254                    flag("--limit", CoverageLevel::Explicit),
255                ],
256                vec![],
257            ),
258            command(
259                &["cloud", "exec"],
260                CoverageLevel::Explicit,
261                None,
262                vec![
263                    flag("--env", CoverageLevel::Explicit),
264                    flag("--attempts", CoverageLevel::Explicit),
265                    flag("--branch", CoverageLevel::Explicit),
266                ],
267                vec![arg("QUERY", CoverageLevel::Explicit)],
268            ),
269            // Scenario 5: login/logout.
270            command(
271                &["login"],
272                CoverageLevel::Explicit,
273                None,
274                vec![
275                    flag_note("--mcp", CoverageLevel::Explicit, "capability-guarded"),
276                    flag("--api-key", CoverageLevel::Explicit),
277                    flag("--device-auth", CoverageLevel::Explicit),
278                    flag("--with-api-key", CoverageLevel::Explicit),
279                ],
280                vec![],
281            ),
282            command(
283                &["login", "status"],
284                CoverageLevel::Explicit,
285                None,
286                vec![],
287                vec![],
288            ),
289            command(&["logout"], CoverageLevel::Explicit, None, vec![], vec![]),
290            // Scenario 6: `codex features list`.
291            command(
292                &["features", "list"],
293                CoverageLevel::Explicit,
294                None,
295                vec![],
296                vec![],
297            ),
298            // Scenario 7: `codex app-server generate-*`.
299            command(
300                &["app-server", "generate-ts"],
301                CoverageLevel::Explicit,
302                None,
303                vec![
304                    flag("--experimental", CoverageLevel::Explicit),
305                    flag("--out", CoverageLevel::Explicit),
306                    flag("--prettier", CoverageLevel::Explicit),
307                ],
308                vec![],
309            ),
310            command(
311                &["app-server", "generate-json-schema"],
312                CoverageLevel::Explicit,
313                None,
314                vec![
315                    flag("--experimental", CoverageLevel::Explicit),
316                    flag("--out", CoverageLevel::Explicit),
317                ],
318                vec![],
319            ),
320            // Scenario 8: `codex responses-api-proxy`.
321            command(
322                &["responses-api-proxy"],
323                CoverageLevel::Explicit,
324                None,
325                vec![
326                    flag("--port", CoverageLevel::Explicit),
327                    flag("--server-info", CoverageLevel::Explicit),
328                    flag("--http-shutdown", CoverageLevel::Explicit),
329                    flag("--upstream-url", CoverageLevel::Explicit),
330                ],
331                vec![],
332            ),
333            // Scenario 9: `codex stdio-to-uds`.
334            command(
335                &["stdio-to-uds"],
336                CoverageLevel::Explicit,
337                None,
338                vec![],
339                vec![arg("SOCKET_PATH", CoverageLevel::Explicit)],
340            ),
341            // Scenario 10: `codex sandbox <platform>`.
342            command(&["sandbox"], CoverageLevel::Explicit, None, vec![], vec![]),
343            command(
344                &["sandbox", "macos"],
345                CoverageLevel::Explicit,
346                None,
347                vec![
348                    flag("--allow-unix-socket", CoverageLevel::Explicit),
349                    flag("--log-denials", CoverageLevel::Explicit),
350                ],
351                vec![arg("COMMAND", CoverageLevel::Explicit)],
352            ),
353            command(
354                &["sandbox", "linux"],
355                CoverageLevel::Explicit,
356                None,
357                vec![],
358                vec![arg("COMMAND", CoverageLevel::Explicit)],
359            ),
360            command(
361                &["sandbox", "windows"],
362                CoverageLevel::Explicit,
363                None,
364                vec![],
365                vec![arg("COMMAND", CoverageLevel::Explicit)],
366            ),
367            // Scenario 11: `codex execpolicy check`.
368            command(
369                &["execpolicy", "check"],
370                CoverageLevel::Explicit,
371                None,
372                vec![
373                    flag("--policy", CoverageLevel::Explicit),
374                    flag("--pretty", CoverageLevel::Explicit),
375                ],
376                vec![arg("COMMAND", CoverageLevel::Explicit)],
377            ),
378            // Scenario 12: stdio servers.
379            command(
380                &["mcp-server"],
381                CoverageLevel::Explicit,
382                None,
383                vec![],
384                vec![],
385            ),
386            command(
387                &["app-server"],
388                CoverageLevel::Explicit,
389                None,
390                vec![
391                    flag("--analytics-default-enabled", CoverageLevel::Explicit),
392                    flag("--listen", CoverageLevel::Explicit),
393                    flag("--ws-audience", CoverageLevel::Explicit),
394                    flag("--ws-auth", CoverageLevel::Explicit),
395                    flag("--ws-issuer", CoverageLevel::Explicit),
396                    flag("--ws-max-clock-skew-seconds", CoverageLevel::Explicit),
397                    flag("--ws-shared-secret-file", CoverageLevel::Explicit),
398                    flag("--ws-token-file", CoverageLevel::Explicit),
399                    flag("--ws-token-sha256", CoverageLevel::Explicit),
400                ],
401                vec![],
402            ),
403            command(
404                &["app-server", "proxy"],
405                CoverageLevel::Explicit,
406                None,
407                vec![flag("--sock", CoverageLevel::Explicit)],
408                vec![],
409            ),
410            command(&["cloud"], CoverageLevel::Explicit, None, vec![], vec![]),
411            command(
412                &["exec-server"],
413                CoverageLevel::Explicit,
414                None,
415                vec![flag("--listen", CoverageLevel::Explicit)],
416                vec![],
417            ),
418            command(&["mcp"], CoverageLevel::Explicit, None, vec![], vec![]),
419            command(
420                &["mcp", "list"],
421                CoverageLevel::Explicit,
422                None,
423                vec![flag("--json", CoverageLevel::Explicit)],
424                vec![],
425            ),
426            command(
427                &["mcp", "get"],
428                CoverageLevel::Explicit,
429                None,
430                vec![flag("--json", CoverageLevel::Explicit)],
431                vec![arg("NAME", CoverageLevel::Explicit)],
432            ),
433            command(
434                &["mcp", "add"],
435                CoverageLevel::Explicit,
436                None,
437                vec![
438                    flag("--url", CoverageLevel::Explicit),
439                    flag("--bearer-token-env-var", CoverageLevel::Explicit),
440                    flag("--env", CoverageLevel::Explicit),
441                ],
442                vec![
443                    arg("NAME", CoverageLevel::Explicit),
444                    arg("COMMAND", CoverageLevel::Explicit),
445                ],
446            ),
447            command(
448                &["mcp", "remove"],
449                CoverageLevel::Explicit,
450                None,
451                vec![],
452                vec![arg("NAME", CoverageLevel::Explicit)],
453            ),
454            command(
455                &["mcp", "logout"],
456                CoverageLevel::Explicit,
457                None,
458                vec![],
459                vec![arg("NAME", CoverageLevel::Explicit)],
460            ),
461            command(
462                &["mcp", "login"],
463                CoverageLevel::Explicit,
464                None,
465                vec![flag("--scopes", CoverageLevel::Explicit)],
466                vec![arg("NAME", CoverageLevel::Explicit)],
467            ),
468            // `codex help` command family (variadic COMMAND).
469            command(
470                &["help"],
471                CoverageLevel::Explicit,
472                None,
473                vec![],
474                vec![arg("COMMAND", CoverageLevel::Explicit)],
475            ),
476            command(
477                &["exec", "help"],
478                CoverageLevel::Explicit,
479                None,
480                vec![],
481                vec![arg("COMMAND", CoverageLevel::Explicit)],
482            ),
483            command(
484                &["features", "help"],
485                CoverageLevel::Explicit,
486                None,
487                vec![],
488                vec![arg("COMMAND", CoverageLevel::Explicit)],
489            ),
490            command(
491                &["login", "help"],
492                CoverageLevel::Explicit,
493                None,
494                vec![],
495                vec![arg("COMMAND", CoverageLevel::Explicit)],
496            ),
497            command(
498                &["app-server", "help"],
499                CoverageLevel::Explicit,
500                None,
501                vec![],
502                vec![arg("COMMAND", CoverageLevel::Explicit)],
503            ),
504            command(
505                &["sandbox", "help"],
506                CoverageLevel::Explicit,
507                None,
508                vec![],
509                vec![arg("COMMAND", CoverageLevel::Explicit)],
510            ),
511            command(
512                &["cloud", "help"],
513                CoverageLevel::Explicit,
514                None,
515                vec![],
516                vec![arg("COMMAND", CoverageLevel::Explicit)],
517            ),
518            command(
519                &["mcp", "help"],
520                CoverageLevel::Explicit,
521                None,
522                vec![],
523                vec![arg("COMMAND", CoverageLevel::Explicit)],
524            ),
525            // New 0.92.0+ command surfaces.
526            command(&["features"], CoverageLevel::Explicit, None, vec![], vec![]),
527            // New 0.97.0 command surfaces.
528            command(
529                &["features", "enable"],
530                CoverageLevel::Explicit,
531                None,
532                vec![],
533                vec![arg("FEATURE", CoverageLevel::Explicit)],
534            ),
535            command(
536                &["features", "disable"],
537                CoverageLevel::Explicit,
538                None,
539                vec![],
540                vec![arg("FEATURE", CoverageLevel::Explicit)],
541            ),
542            command(&["debug"], CoverageLevel::Explicit, None, vec![], vec![]),
543            command(
544                &["debug", "help"],
545                CoverageLevel::Explicit,
546                None,
547                vec![],
548                vec![arg("COMMAND", CoverageLevel::Explicit)],
549            ),
550            command(
551                &["debug", "app-server"],
552                CoverageLevel::Explicit,
553                None,
554                vec![],
555                vec![],
556            ),
557            command(
558                &["debug", "app-server", "help"],
559                CoverageLevel::Explicit,
560                None,
561                vec![],
562                vec![arg("COMMAND", CoverageLevel::Explicit)],
563            ),
564            command(
565                &["debug", "app-server", "send-message-v2"],
566                CoverageLevel::Explicit,
567                None,
568                vec![],
569                vec![arg("USER_MESSAGE", CoverageLevel::Explicit)],
570            ),
571            command(
572                &["debug", "models"],
573                CoverageLevel::Explicit,
574                None,
575                vec![flag("--bundled", CoverageLevel::Explicit)],
576                vec![],
577            ),
578            command(
579                &["debug", "prompt-input"],
580                CoverageLevel::Explicit,
581                None,
582                vec![flag("--image", CoverageLevel::Explicit)],
583                vec![arg("PROMPT", CoverageLevel::Explicit)],
584            ),
585            command(
586                &["exec", "review"],
587                CoverageLevel::Explicit,
588                None,
589                vec![
590                    flag("--base", CoverageLevel::Explicit),
591                    flag("--commit", CoverageLevel::Explicit),
592                    flag("--ephemeral", CoverageLevel::Explicit),
593                    flag("--ignore-rules", CoverageLevel::Explicit),
594                    flag("--ignore-user-config", CoverageLevel::Explicit),
595                    flag("--json", CoverageLevel::Explicit),
596                    flag("--output-last-message", CoverageLevel::Explicit),
597                    flag("--skip-git-repo-check", CoverageLevel::Explicit),
598                    flag("--title", CoverageLevel::Explicit),
599                    flag("--uncommitted", CoverageLevel::Explicit),
600                ],
601                vec![arg("PROMPT", CoverageLevel::Explicit)],
602            ),
603            command(
604                &["review"],
605                CoverageLevel::Explicit,
606                None,
607                vec![
608                    flag("--base", CoverageLevel::Explicit),
609                    flag("--commit", CoverageLevel::Explicit),
610                    flag("--title", CoverageLevel::Explicit),
611                    flag("--uncommitted", CoverageLevel::Explicit),
612                ],
613                vec![arg("PROMPT", CoverageLevel::Explicit)],
614            ),
615            command(
616                &["resume"],
617                CoverageLevel::Explicit,
618                None,
619                vec![
620                    flag("--all", CoverageLevel::Explicit),
621                    flag("--include-non-interactive", CoverageLevel::Explicit),
622                    flag("--last", CoverageLevel::Explicit),
623                ],
624                vec![
625                    arg("PROMPT", CoverageLevel::Explicit),
626                    arg("SESSION_ID", CoverageLevel::Explicit),
627                ],
628            ),
629            command(
630                &["fork"],
631                CoverageLevel::Explicit,
632                None,
633                vec![
634                    flag("--all", CoverageLevel::Explicit),
635                    flag("--last", CoverageLevel::Explicit),
636                ],
637                vec![
638                    arg("PROMPT", CoverageLevel::Explicit),
639                    arg("SESSION_ID", CoverageLevel::Explicit),
640                ],
641            ),
642            command(&["plugin"], CoverageLevel::Explicit, None, vec![], vec![]),
643            command(
644                &["plugin", "help"],
645                CoverageLevel::Explicit,
646                None,
647                vec![],
648                vec![arg("COMMAND", CoverageLevel::Explicit)],
649            ),
650            command(
651                &["plugin", "marketplace"],
652                CoverageLevel::Explicit,
653                None,
654                vec![],
655                vec![],
656            ),
657            command(
658                &["plugin", "marketplace", "add"],
659                CoverageLevel::Explicit,
660                None,
661                vec![
662                    flag("--ref", CoverageLevel::Explicit),
663                    flag("--sparse", CoverageLevel::Explicit),
664                ],
665                vec![arg("SOURCE", CoverageLevel::Explicit)],
666            ),
667            command(
668                &["plugin", "marketplace", "help"],
669                CoverageLevel::Explicit,
670                None,
671                vec![],
672                vec![arg("COMMAND", CoverageLevel::Explicit)],
673            ),
674            command(
675                &["plugin", "marketplace", "remove"],
676                CoverageLevel::Explicit,
677                None,
678                vec![],
679                vec![arg("MARKETPLACE_NAME", CoverageLevel::Explicit)],
680            ),
681            command(
682                &["plugin", "marketplace", "upgrade"],
683                CoverageLevel::Explicit,
684                None,
685                vec![],
686                vec![arg("MARKETPLACE_NAME", CoverageLevel::Explicit)],
687            ),
688            WrapperCommandCoverageV1 {
689                path: vec!["completion".to_string()],
690                level: CoverageLevel::IntentionallyUnsupported,
691                note: Some(
692                    "Shell completion generation is out of scope for the wrapper.".to_string(),
693                ),
694                scope: None,
695                flags: None,
696                args: Some(vec![arg_note(
697                    "SHELL",
698                    CoverageLevel::IntentionallyUnsupported,
699                    "Shell completion generation is out of scope for the wrapper.",
700                )]),
701            },
702        ],
703    }
704}