headwater-cli 0.3.0

The headwater binary, and what CI runs. headwater --help is the verb list
Documentation
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
1109
1110
1111
1112
1113
1114
1115
1116
1117
1118
1119
1120
1121
1122
1123
1124
1125
1126
1127
1128
1129
1130
1131
1132
1133
1134
1135
1136
1137
1138
1139
1140
1141
1142
1143
1144
1145
1146
1147
1148
1149
1150
1151
1152
1153
1154
1155
1156
1157
1158
1159
1160
1161
1162
1163
1164
1165
1166
1167
1168
1169
1170
1171
1172
1173
1174
1175
1176
1177
1178
1179
1180
1181
1182
1183
1184
1185
1186
1187
1188
1189
1190
1191
1192
1193
1194
1195
1196
1197
1198
1199
1200
1201
1202
1203
1204
1205
1206
1207
1208
1209
1210
1211
1212
1213
1214
1215
1216
1217
1218
1219
1220
1221
1222
1223
1224
1225
1226
1227
1228
1229
1230
1231
1232
1233
1234
1235
1236
1237
1238
1239
1240
1241
1242
1243
1244
1245
1246
1247
1248
1249
1250
1251
1252
1253
1254
1255
1256
// SPDX-License-Identifier: Apache-2.0
//! What `headwater init` writes, byte for byte.
//!
//! # The defect this target exists for
//!
//! `headwater init` writes two files, and until this target no test of this
//! workspace read one byte of either. One `match` arm of the verb emits two
//! messages under one condition: the comment it writes above `version: 0.0.0`,
//! and the line it prints in its report. #276 found those two drifted apart —
//! the printed one naming two routes and the written one naming a route an
//! adopter cannot take — because the printed one was held and the written one
//! was held by nothing. That arm is still one arm.
//!
//! `.claude/tutorial/drive.py` does hold part of it: three substrings of the
//! declaration, the literal `  version: 0.0.0` line, and the fact that the
//! overlay ends `add: {}`. That guard is real and it sits in the wrong place
//! twice over. It is six claims rather than the file, and its failure message
//! is about a sixteen-step tutorial rather than about `init`. Its own comment
//! named #174 as the missing test's home, and #174 is closed.
//!
//! This target is a characterization of the verb rather than a statement of
//! what the verb should write. It passed on the day it was written, against an
//! unmodified `init`. Its value is that the bytes now have a reader.
//!
//! # Why the two arms are asserted differently
//!
//! The blank-tree arm is asserted as bytes, because every byte of it is a
//! literal in the verb.
//!
//! The package-present arm embeds the version the package declares, so this
//! target reads that version out of the manifest it copied rather than writing
//! it here. A literal would be the defect #427 already caused once: a bump to
//! 4.0.0 moved six mentions in the tutorial, left one behind, and failed every
//! step from 5 to 16 of a tutorial that was right.
//!
//! # What the last line is for
//!
//! `add: {}` is the last line of the overlay, and `.claude/tutorial/drive.py`
//! splices the reader's edit in at exactly that offset. Holding it here makes
//! a break point at `init`, which is where it belongs.
//!
//! # One oddity this target pins rather than repairs
//!
//! The declaration carries one comment at column zero — `# A bundle is an
//! optional part of the package` — where every comment around it carries two
//! spaces. A `\` line continuation in the verb's format string eats the
//! leading whitespace of the line that follows it. YAML reads a comment
//! wherever it sits, so nothing downstream is wrong, and the adopter commits
//! the file all the same. It is pinned here rather than repaired, because this
//! target exists to record what the verb writes today.

use std::path::{Path, PathBuf};
use std::process::Command;

/// The repository this test tree sits in.
fn repository() -> PathBuf {
    Path::new(env!("CARGO_MANIFEST_DIR"))
        .join("../../..")
        .canonicalize()
        .expect("the repository root resolves")
}

/// Everything the declaration carries before the version block.
const DECLARATION_HEAD: &str = r"# The consumer declaration, written by `headwater init`. It says two things,
# and they are different questions: what schema this repository takes, and what
# tree it walks.
#
# `headwater taxonomy resolve` reads this and writes .headwater/taxonomy.lock. Everything after that
# reads the lock and never these sources.

taxonomy:
  package: headwater/standard
";

/// The version block of the arm that found no package, which is an interview
/// stub, nine lines of comment and one commented field.
///
/// This constant is a copy of a string in the verb, and a copy checked against
/// nothing but itself is what #641 cost. The last case in this file runs the
/// route these bytes name, so the bytes have a reader that is not another copy
/// of them.
const NO_PACKAGE_VERSION: &str = r"  # INTERVIEW: no package of this name is under `.headwater/packages/`. Two routes
  # reach a lock, and each one needs a different field below.
  # Copy a package directory into `.headwater/packages/`, and pin `version` at
  # the version that package declares. Or run
  # `headwater taxonomy vendor <dir-or-location>` on a published artifact,
  # unpacked or at the `https://` location of its zip: that verb reads `digest`
  # and refuses until it holds the digest the publisher printed, and
  # `headwater taxonomy resolve` reads `version` after it, so the vendor route
  # needs the digest first and the version as well.
  # digest: sha256:<the digest the publisher printed>
  version: 0.0.0
";

/// Everything the declaration carries after the version block.
///
/// Its first line is the comment at column zero that the module header names.
const DECLARATION_TAIL: &str = r"# A bundle is an optional part of the package, and a selection is add-only.
  # INTERVIEW: which traditions does this corpus already follow?
  bundles: []
  overlay: .headwater/overlay.yml

corpus:
  # Proposed from this tree: the directory holding the most Markdown.
  root: docs
  # An exclusion states a reason. A pattern with none is a silent pass with a
  # configuration file in front of it, so the reason is not optional.
  # exclude:
  #   - path: docs/vendor/**
  #     reason: vendored copies of documents another team owns
";

/// The whole overlay. It is one arm, because `init` writes the same overlay
/// whether a package is on the tree or not.
const OVERLAY: &str = r#"# The adopter overlay, written by `headwater init`. It is an overlay and never a
# resolved taxonomy, so nothing here can weaken the package it sits on: a
# bundle selection is add-only, and an add-only overlay carries no operation
# that removes a base rule.
#
# Every block below is a question this engine cannot answer from a tree. It is
# prose about what this corpus is for, and a corpus does not state it.
#
# INTERVIEW 1 --- what does each purpose answer?
#
# A task is matched against declared purposes before it is matched against any
# text, and it is matched on the `answers` phrases first. Two purposes whose
# phrases share every term separate nothing, and every task then matches both
# equally. Read `headwater/standard`'s purposes, and add the phrases a person here would
# actually type.
#
#   add:
#     purposes.rationale.answers: ["why is it this way", "what was rejected"]
#
# INTERVIEW 2 --- what identifies a document, and what does the prefix mean?
#
# A relation names its target by identifier. A corpus whose documents carry none
# has no edges, and no check about an edge can say anything about it.
#
# `add` states a value the package leaves unstated, and `override` replaces one
# the package already states, so the operation follows the package rather than
# the taste of the writer. `headwater/standard` declares `decision_id` with no namespace
# and gives `decision` that scheme, so the namespace below is an `add` on a leaf
# the package leaves empty, and the kind below is an `override` because `add`
# over a value the package already states is refused. Replace ACME with the
# prefix this corpus uses.
#
#   add:
#     identifier_schemes.doc_id: {pattern: "{namespace}-DOC-{slug}", namespace: ACME, allocation: minted-once}
#     identifier_schemes.decision_id.namespace: ACME
#   override:
#     kinds.decision.identifier: {scheme: doc_id}
#
# INTERVIEW 3 --- what does this corpus already write?
#
# Run `headwater infer` once this file resolves. It reports the files that
# classify as nothing, which is the half a payload cannot carry, and the
# documents that state no summary, which nothing will route to.

add: {}
"#;

/// A scratch repository, and what one run of `init` leaves in it.
///
/// Dropping it removes the tree and every directory [`Root::beside`] made, so
/// a case that fails an assertion leaves nothing under the temporary directory
/// (#1158).
struct Root {
    at: PathBuf,
    beside: std::cell::RefCell<Vec<PathBuf>>,
}

impl Drop for Root {
    fn drop(&mut self) {
        let _ = std::fs::remove_dir_all(&self.at);
        for at in self.beside.borrow().iter() {
            let _ = std::fs::remove_dir_all(at);
        }
    }
}

impl Root {
    /// A tree holding one Markdown file under `docs/`, which is the least that
    /// reaches the write path: `busiest_directory` proposes a corpus root from
    /// the directory holding the most Markdown, and a tree with none is
    /// refused.
    ///
    /// `label` names the case rather than the process, for the reason
    /// `wiring.rs` records beside its own helper. Cargo runs the cases of one
    /// target as threads of one process, so a directory keyed on the process
    /// alone is a directory one case removes while another is reading it.
    fn over(label: &str) -> Root {
        let at =
            std::env::temp_dir().join(format!("headwater-cli-init-{}-{label}", std::process::id()));
        let _ = std::fs::remove_dir_all(&at);
        std::fs::create_dir_all(at.join("docs")).expect("the corpus directory is there");
        std::fs::write(at.join("docs/one.md"), "# a document\n").expect("the document writes");
        Root {
            at,
            beside: std::cell::RefCell::default(),
        }
    }

    /// The same tree, plus the manifest of the package this repository
    /// maintains.
    ///
    /// The manifest alone is the whole of the package-present arm, because
    /// `find_version` reads `package.yml` and opens nothing else. Copying the
    /// real one rather than writing a stand-in is what couples this target to
    /// the version the repository actually ships.
    fn with_package(label: &str) -> Root {
        let root = Root::over(label);
        let directory = root.at.join(".headwater/packages/headwater-standard");
        std::fs::create_dir_all(&directory).expect("the package directory is there");
        std::fs::copy(
            repository().join(".headwater/packages/headwater-standard/package.yml"),
            directory.join("package.yml"),
        )
        .expect("the manifest copies");
        root
    }

    /// One run of the verb over this root.
    ///
    /// The exit status is asserted here rather than returned, because every
    /// case below reads a file the verb writes and a refusal writes neither.
    fn init(&self) {
        let output = Command::new(env!("CARGO_BIN_EXE_headwater"))
            .args(["init", "--root"])
            .arg(&self.at)
            .output()
            .expect("the binary runs");
        assert_eq!(
            output.status.code(),
            Some(0),
            "`headwater init` writes both files:\n{}",
            String::from_utf8_lossy(&output.stderr)
        );
    }

    fn read(&self, relative: &str) -> String {
        std::fs::read_to_string(self.at.join(relative)).expect("the written file reads")
    }

    /// The version the copied manifest declares, read rather than written.
    ///
    /// The assertion on the count is the point of the helper. A manifest that
    /// stops declaring a version at the top level, or that declares two, fails
    /// here and names what it found, rather than silently answering with the
    /// wrong line.
    fn declared_version(&self) -> String {
        declared_version_at(
            &self
                .at
                .join(".headwater/packages/headwater-standard/package.yml"),
        )
    }

    /// A scratch directory beside this root, for something that is not part of
    /// the tree `init` reads.
    ///
    /// A published artifact under the root would join the tree that
    /// `busiest_directory` walks, so it goes outside rather than inside.
    fn beside(&self, label: &str) -> PathBuf {
        let at = self.at.with_file_name(format!(
            "{}-{label}",
            self.at
                .file_name()
                .expect("the scratch root is named")
                .to_string_lossy()
        ));
        let _ = std::fs::remove_dir_all(&at);
        std::fs::create_dir_all(&at).expect("the scratch directory is made");
        self.beside.borrow_mut().push(at.clone());
        at
    }

    fn write(&self, relative: &str, body: &str) {
        std::fs::write(self.at.join(relative), body).expect("the file writes");
    }

    /// One run of the built binary over this root, with an optional path
    /// argument, returning the status and standard error.
    ///
    /// The two streams are kept apart, because a verb of this engine writes its
    /// report to one and its account of a refusal to the other.
    fn run(&self, verb: &[&str], argument: Option<&Path>) -> (Option<i32>, String) {
        let mut command = Command::new(env!("CARGO_BIN_EXE_headwater"));
        command.args(verb);
        if let Some(path) = argument {
            command.arg(path);
        }
        let output = command
            .arg("--root")
            .arg(&self.at)
            .output()
            .expect("the binary runs");
        (
            output.status.code(),
            String::from_utf8_lossy(&output.stderr).into_owned(),
        )
    }
}

/// The version a package manifest declares at the top level, read rather than
/// written.
///
/// The assertion on the count is the point of the helper. A manifest that stops
/// declaring a version at the top level, or that declares two, fails here and
/// names what it found, rather than silently answering with the wrong line.
fn declared_version_at(manifest: &Path) -> String {
    let body = std::fs::read_to_string(manifest)
        .unwrap_or_else(|error| panic!("{} reads: {error}", manifest.display()));
    let lines: Vec<&str> = body
        .lines()
        .filter(|line| line.starts_with("version:"))
        .collect();
    assert_eq!(
        lines.len(),
        1,
        "{} declares one version at the top level, and it declares {}",
        manifest.display(),
        lines.len()
    );
    lines[0].trim_start_matches("version:").trim().to_string()
}

/// The version this repository's maintained source of `headwater/standard`
/// declares.
fn maintained_version() -> String {
    declared_version_at(&repository().join("taxonomy-source/headwater-standard/package.yml"))
}

/// `taxonomy publish --from` over this repository's maintained source, giving
/// back the digest the report printed.
///
/// The digest is read out of the report rather than written here, so a change to
/// the package moves this case with it. `publish.rs` holds what the verb writes
/// to disk; this reads the one line the report tells a consumer to pin, and it
/// names `taxonomy-source/headwater-standard/` for the reason that target
/// states: it is the maintained source, and `.headwater/packages/headwater-standard/` is a
/// vendored artifact that `publish` refuses.
fn publish_maintained_source_into(out: &Path) -> String {
    let output = Command::new(env!("CARGO_BIN_EXE_headwater"))
        .arg("taxonomy")
        .arg("publish")
        .arg("--from")
        .arg(repository().join("taxonomy-source/headwater-standard"))
        .arg("--out")
        .arg(out)
        .arg("--root")
        .arg(repository())
        .output()
        .expect("the binary runs");
    assert_eq!(
        output.status.code(),
        Some(0),
        "the artifact publishes:\n{}",
        String::from_utf8_lossy(&output.stderr)
    );
    let report = String::from_utf8_lossy(&output.stdout).into_owned();
    let stated: Vec<&str> = report
        .lines()
        .filter_map(|line| line.trim().strip_prefix("digest "))
        .collect();
    assert_eq!(
        stated.len(),
        1,
        "the publish report states one digest, and it states {}:\n{report}",
        stated.len()
    );
    stated[0].to_string()
}

/// The declaration of a tree with no package under `.headwater/packages/`.
///
/// This is the first-run state that spec 7 calls the surface `init` is for,
/// and it is the state the tutorial runs its step 2 in. Every byte of it is a
/// literal in the verb, so every byte of it is asserted.
#[test]
fn the_declaration_of_a_tree_with_no_package_is_written_byte_for_byte() {
    let root = Root::over("declaration-no-package");
    root.init();
    let expected = format!("{DECLARATION_HEAD}{NO_PACKAGE_VERSION}{DECLARATION_TAIL}");
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        expected,
        "the declaration `headwater init` writes on a blank tree"
    );
}

/// The overlay, which is one arm and carries no package-dependent byte.
#[test]
fn the_overlay_is_written_byte_for_byte() {
    let root = Root::over("overlay");
    root.init();
    assert_eq!(
        root.read(".headwater/overlay.yml"),
        OVERLAY,
        "the overlay `headwater init` writes"
    );
}

/// The declaration of a tree that carries the package, which pins the version
/// the package declares.
///
/// The version is read out of the manifest this case copied. A literal here
/// would go stale on the next bump of the package, which is what #427 did to
/// the tutorial: six mentions moved, one stayed, and every step from 5 to 16
/// failed on a document that was right.
#[test]
fn the_declaration_of_a_tree_with_a_package_pins_the_version_that_package_declares() {
    let root = Root::with_package("declaration-with-package");
    let version = root.declared_version();
    root.init();
    let expected = format!("{DECLARATION_HEAD}  version: {version}\n{DECLARATION_TAIL}");
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        expected,
        "the declaration `headwater init` writes over a package it found"
    );
    assert!(
        !expected.contains("0.0.0"),
        "a package on the tree replaces the interview stub rather than joining it"
    );
}

/// `add: {}` is the last line of the overlay.
///
/// `.claude/tutorial/drive.py` asserts the same thing and then splices the
/// reader's edit in at that offset, so anything appended after it breaks a
/// driver assertion rather than an output block, and re-recording the block
/// does not repair it. Held here as well, so that a break points at the verb.
#[test]
fn the_last_line_of_the_overlay_is_the_line_the_tutorial_replaces() {
    let root = Root::over("overlay-last-line");
    root.init();
    let overlay = root.read(".headwater/overlay.yml");
    assert!(
        overlay.trim_end_matches('\n').ends_with("add: {}"),
        "the last line of the overlay is `add: {{}}`, and the tutorial replaces it in place:\n{}",
        overlay
            .lines()
            .rev()
            .take(3)
            .collect::<Vec<&str>>()
            .join("\n")
    );
}

/// The route the declaration names, run rather than read.
///
/// # Why this case exists
///
/// Every other case in this target asserts that the declaration *says*
/// something. None of them runs a command it names, so the comment `init`
/// writes was a claim about this engine sitting in a string literal, checked
/// only against a copy of itself in this file. [#641] is what that costs: the
/// comment stated one requirement for two routes that need different fields,
/// an adopter who did exactly what it said met `nothing pins this artifact`,
/// and both copies agreed with each other the whole time.
///
/// `.claude/tutorial/fixtures.sh` does not cover it either. The tutorial takes
/// the copy route, so `taxonomy vendor` is outside the population that suite
/// runs.
///
/// So this case reads the instruction out of the file the verb just wrote,
/// performs it, and runs the command the file names. It publishes a real
/// artifact from this repository's own maintained source rather than a
/// synthetic one, for the reason `publish.rs` states beside the same source:
/// the manifest of a small stand-in does not reach the paths a real one does.
///
/// # What it asserts, and in the order an adopter meets it
///
/// The declaration names a `digest` field. `taxonomy vendor` accepts the
/// artifact once that field carries the digest the publisher printed.
/// `taxonomy resolve` then gets past the version, which is the second field
/// the vendor route needs and the one the comment used to name alone.
///
/// `resolve` still refuses after all of that, and that refusal is by design:
/// it is the interview asking this corpus for an identifier namespace, which
/// no engine can answer off a tree. This case asserts the refusal is no longer
/// the version one.
///
/// [#641]: https://github.com/headwater-ai/headwater/issues/641
#[test]
fn the_vendor_route_the_declaration_names_reaches_a_resolved_version() {
    let root = a_root_the_vendor_route_reached("vendor-route");

    let (_, stderr) = root.run(&["taxonomy", "resolve"], None);
    assert!(
        !stderr.contains("this takes headwater/standard"),
        "`headwater taxonomy resolve` is past the version the vendor route also needs:\n{stderr}"
    );
}

/// A root that `init` wrote and that the vendor route the declaration names has
/// carried as far as a package on disk at the pinned version.
///
/// This is the setup of the two cases below it, extracted rather than typed
/// twice. Every edit in it is read out of a file the verb wrote, so a verb that
/// stops writing one of those lines fails here.
fn a_root_the_vendor_route_reached(label: &str) -> Root {
    let root = Root::over(label);
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();

    // The edit the comment instructs, taken from the file rather than written
    // here: the commented `digest` line the declaration carries is the only
    // hint an adopter has that the field exists, so a declaration that stops
    // carrying it fails here rather than one command later in their tree.
    let declaration = root.read(".headwater/taxonomy.yml");
    let commented = declaration
        .lines()
        .find(|line| line.trim_start().starts_with("# digest:"))
        .unwrap_or_else(|| {
            panic!(
                "the declaration names the field `taxonomy vendor` reads, and it names none of \
                 these:\n{declaration}"
            )
        });
    let version = maintained_version();
    let pinned = declaration
        .replace(commented, &format!("  digest: {digest}"))
        .replace("  version: 0.0.0\n", &format!("  version: {version}\n"));
    assert!(
        pinned.contains(&format!("  digest: {digest}\n")),
        "the commented line is replaced by the pin:\n{pinned}"
    );
    root.write(".headwater/taxonomy.yml", &pinned);

    let (code, stderr) = root.run(&["taxonomy", "vendor"], Some(&artifact));
    assert_eq!(
        code,
        Some(0),
        "`headwater taxonomy vendor` accepts the artifact the declaration pins:\n{stderr}"
    );
    assert_eq!(
        declared_version_at(
            &root
                .at
                .join(".headwater/packages/headwater-standard/package.yml")
        ),
        version,
        "the vendored package is the one the declaration pins"
    );
    root
}

/// The example the overlay prints under `INTERVIEW 2`, lifted out of the file
/// `init` wrote and given back with the comment prefix and the common indent
/// removed.
///
/// # Why it is read rather than typed
///
/// The case below runs these lines. A case that retyped them would pass forever
/// whatever the template said, which is the defect `OVERLAY` above has on its
/// own: a string literal held against a copy of itself. Reading them here makes
/// the template the thing under test.
///
/// The example lines are the ones this block indents past its prose: a comment
/// line of the block carries one space after the `#`, and an example line
/// carries three or more. The panic is the second half of the mechanism —
/// a template that stopped printing an example would otherwise hand back an
/// empty patch, and the case would then be asserting something else.
fn the_interview_2_example(overlay: &str) -> String {
    let block: Vec<&str> = overlay
        .lines()
        .skip_while(|line| !line.starts_with("# INTERVIEW 2"))
        .skip(1)
        .take_while(|line| !line.starts_with("# INTERVIEW"))
        .collect();
    let example: Vec<&str> = block
        .iter()
        .filter_map(|line| line.strip_prefix('#'))
        .filter(|rest| rest.starts_with("   "))
        .collect();
    assert!(
        !example.is_empty(),
        "the overlay prints an example under `INTERVIEW 2`, and it prints none of these:\n{overlay}"
    );
    let indent = example
        .iter()
        .map(|line| line.len() - line.trim_start().len())
        .min()
        .expect("the example is not empty");
    example
        .iter()
        .map(|line| format!("{}\n", &line[indent..]))
        .collect()
}

/// The example the overlay hands an adopter resolves.
///
/// # The defect this case exists for
///
/// [#659]. `INTERVIEW 2` asks the corpus what identifies a document, and the
/// example under it was the only answer an adopter reading that file ever saw.
/// It answered nothing twice over: it declared a scheme of its own and never
/// the namespace that `headwater/standard` leaves to the corpus, so `resolve`
/// refused with `carries no namespace after resolution`; and it stated
/// `kinds.<kind>.identifier` under `add` over a kind the package already gives
/// an identifier, which `resolve` refuses on its own terms. Neither refusal
/// moves the other, so the example had to be repaired in both places at once.
///
/// The case that precedes this one is [#641]'s, and it stops one refusal short:
/// it asserts `resolve` is past the version and lets the namespace refusal
/// stand, because that refusal is by design. This case is the continuation. It
/// performs the overlay's own instruction and asserts `resolve` writes a lock.
///
/// # Why it extracts rather than retypes
///
/// See `the_interview_2_example`. Reverting the template to the example this
/// case was written against fails here with the refusal above, which is the
/// discriminator a retyped copy would not have.
///
/// [#641]: https://github.com/headwater-ai/headwater/issues/641
/// [#659]: https://github.com/headwater-ai/headwater/issues/659
#[test]
fn the_overlay_example_the_interview_prints_reaches_a_lock() {
    let root = a_root_the_vendor_route_reached("interview-example");

    let overlay = root.read(".headwater/overlay.yml");
    assert!(
        overlay.trim_end_matches('\n').ends_with("add: {}"),
        "the overlay ends with the line the example replaces:\n{overlay}"
    );
    let patched = format!(
        "{}{}",
        overlay.trim_end_matches('\n').trim_end_matches("add: {}"),
        the_interview_2_example(&overlay)
    );
    root.write(".headwater/overlay.yml", &patched);

    let (code, stderr) = root.run(&["taxonomy", "resolve"], None);
    assert_eq!(
        code,
        Some(0),
        "the example `INTERVIEW 2` prints resolves:\n{stderr}\nthe overlay it was run \
         against:\n{patched}"
    );
}

/// The argument `taxonomy vendor` takes, as the interface contract states it.
///
/// `docs/interfaces/headwater-taxonomy.md` is the contract for the verb, so the
/// placeholder `init` prints is held to that row rather than to a copy of it in
/// this file. A contract that renames the argument moves this case with it.
fn the_vendor_argument_the_contract_states() -> String {
    let contract =
        std::fs::read_to_string(repository().join("docs/interfaces/headwater-taxonomy.md"))
            .expect("the interface contract reads");
    let rows: Vec<String> = contract
        .lines()
        .filter_map(|line| line.strip_prefix("| `vendor <"))
        .filter_map(|rest| {
            rest.split_once('>')
                .map(|(argument, _)| argument.to_string())
        })
        .collect();
    assert_eq!(
        rows.len(),
        1,
        "the contract states one `vendor` row, and it states {}",
        rows.len()
    );
    rows[0].clone()
}

/// Every argument placeholder that follows `headwater taxonomy vendor` in `text`.
fn vendor_placeholders(text: &str) -> Vec<String> {
    let squeezed = text
        .lines()
        .map(|line| line.trim_start().trim_start_matches('#').trim())
        .collect::<Vec<_>>()
        .join(" ");
    squeezed
        .match_indices("headwater taxonomy vendor <")
        .filter_map(|(at, marker)| {
            squeezed[at + marker.len()..]
                .split_once('>')
                .map(|(argument, _)| argument.to_string())
        })
        .collect()
}

/// Pack a published artifact directory into the zip `release-taxonomy.yml`
/// uploads: every member at the root of the archive, deflated.
///
/// A copy of the helper of the same name in `publish.rs`. Two targets of one
/// crate share no module unless one is written for them, and `tests/common`
/// carries nothing about a published artifact.
#[cfg(feature = "fetch")]
fn zipped(artifact: &Path) -> Vec<u8> {
    use std::io::Write;
    let mut members = Vec::new();
    let mut pending = vec![artifact.to_path_buf()];
    while let Some(dir) = pending.pop() {
        for entry in std::fs::read_dir(&dir).expect("the artifact directory reads") {
            let path = entry.expect("the entry reads").path();
            if path.is_dir() {
                pending.push(path);
            } else {
                members.push(path);
            }
        }
    }
    members.sort();
    let mut writer = zip::ZipWriter::new(std::io::Cursor::new(Vec::new()));
    let options = zip::write::SimpleFileOptions::default()
        .compression_method(zip::CompressionMethod::Deflated);
    for path in members {
        let name = path
            .strip_prefix(artifact)
            .expect("under the artifact")
            .to_str()
            .expect("the path is UTF-8")
            .replace('\\', "/");
        writer.start_file(name, options).expect("the member starts");
        writer
            .write_all(&std::fs::read(&path).expect("the member reads"))
            .expect("the member writes");
    }
    writer.finish().expect("the archive closes").into_inner()
}

/// Serve `body` at `/<name>` on 127.0.0.1 for as long as the test process
/// lives, and give back the location. `headwater-fetch` takes plain `http`
/// to a loopback host alone, so the suite reaches no other host.
#[cfg(feature = "fetch")]
fn serve(name: &str, body: Vec<u8>) -> String {
    use std::io::{Read, Write};
    let listener = std::net::TcpListener::bind("127.0.0.1:0").expect("a loopback port binds");
    let location = format!(
        "http://{}/{name}",
        listener.local_addr().expect("the port reads")
    );
    let wanted = format!("/{name}");
    std::thread::spawn(move || {
        for stream in listener.incoming() {
            let Ok(mut stream) = stream else { continue };
            let mut request = [0_u8; 4096];
            let read = stream.read(&mut request).unwrap_or(0);
            let line = String::from_utf8_lossy(&request[..read]).to_string();
            let found = line.split_whitespace().nth(1) == Some(wanted.as_str());
            let (head, payload): (String, &[u8]) = if found {
                (
                    format!(
                        "HTTP/1.1 200 OK\r\nContent-Length: {}\r\nConnection: close\r\n\r\n",
                        body.len()
                    ),
                    &body,
                )
            } else {
                (
                    "HTTP/1.1 404 Not Found\r\nContent-Length: 0\r\nConnection: close\r\n\r\n"
                        .to_string(),
                    &[],
                )
            };
            let _ = stream.write_all(head.as_bytes());
            let _ = stream.write_all(payload);
        }
    });
    location
}

/// The vendor route `init` names takes the location a release publishes.
///
/// # Why this case exists
///
/// `HW-OBL-0085` recorded that this engine fetched no package, and `init`
/// said so in both messages of its no-package arm. [#959] gave
/// `taxonomy vendor` a location, so that sentence became false and the remedy
/// named `<dir>` alone. An adopter with no copy of the artifact on disk read
/// that they had to fetch and unpack it by other means. This is the #271 and
/// [#641] failure again: the first verb an adopter runs states a remedy that
/// does not match the binary in front of them.
///
/// # What it asserts
///
/// Both messages name the argument the interface contract states for the
/// verb. The case then takes that route as a reader would. It pins the digest
/// in the field the declaration names, and it vendors from a zip served on
/// 127.0.0.1 in the place of the placeholder. `taxonomy resolve` then gets
/// past the version, as the case above asserts for the directory form.
///
/// [#641]: https://github.com/headwater-ai/headwater/issues/641
/// [#959]: https://github.com/headwater-ai/headwater/issues/959
#[cfg(feature = "fetch")]
#[test]
fn the_vendor_route_init_names_takes_the_location_a_release_publishes() {
    let root = Root::over("vendor-location");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);

    let output = Command::new(env!("CARGO_BIN_EXE_headwater"))
        .args(["init", "--root"])
        .arg(&root.at)
        .output()
        .expect("the binary runs");
    assert_eq!(
        output.status.code(),
        Some(0),
        "`headwater init` writes both files"
    );
    let report = String::from_utf8_lossy(&output.stdout).into_owned();
    let declaration = root.read(".headwater/taxonomy.yml");

    let argument = the_vendor_argument_the_contract_states();
    for (name, text) in [
        ("the printed report", &report),
        ("the declaration", &declaration),
    ] {
        assert_eq!(
            vendor_placeholders(text),
            vec![argument.clone()],
            "{name} names `headwater taxonomy vendor <{argument}>`, the argument the contract \
             states:\n{text}"
        );
        assert!(
            !text.contains("fetches one"),
            "{name} no longer says that nothing fetches a package:\n{text}"
        );
    }

    let version = maintained_version();
    let commented = declaration
        .lines()
        .find(|line| line.trim_start().starts_with("# digest:"))
        .expect("the declaration names the field `taxonomy vendor` reads");
    let pinned = declaration
        .replace(commented, &format!("  digest: {digest}"))
        .replace("  version: 0.0.0\n", &format!("  version: {version}\n"));
    root.write(".headwater/taxonomy.yml", &pinned);

    let location = serve(
        &format!("headwater-standard-{version}.zip"),
        zipped(&artifact),
    );
    let (code, stderr) = root.run(&["taxonomy", "vendor", location.as_str()], None);
    assert_eq!(
        code,
        Some(0),
        "`headwater taxonomy vendor <{argument}>` accepts the location a release \
         publishes:\n{stderr}"
    );
    assert_eq!(
        declared_version_at(
            &root
                .at
                .join(".headwater/packages/headwater-standard/package.yml")
        ),
        version,
        "the fetched package is the one the declaration pins"
    );

    let (_, stderr) = root.run(&["taxonomy", "resolve"], None);
    assert!(
        !stderr.contains("this takes headwater/standard"),
        "`headwater taxonomy resolve` is past the version after a fetch:\n{stderr}"
    );
}

/// The digest the declaration under `root` pins, read through the parser every
/// verb reads it with rather than grepped out of the text.
fn pinned_digest(root: &Root) -> Option<String> {
    headwater_resolve::package::consumer(&root.at)
        .unwrap_or_else(|errors| panic!("the declaration reads: {errors:?}"))
        .digest
}

/// `taxonomy vendor --expect` records the pin it verified, so the next clone
/// does not refuse.
///
/// # Why this case exists
///
/// [#641] was an adopter who named the one command a guide gave them and met
/// `nothing pins this artifact` on the next run. `--expect` verified the
/// artifact, installed it and exited 0, and left the declaration without a
/// `digest`, so the identical next run with no flag refused. The owner ruled
/// in [#1063] that `vendor --expect` writes the value it verified into
/// `.headwater/taxonomy.yml` when no pin is declared there.
///
/// # What it asserts, in the order an adopter meets it
///
/// `init`, then `vendor <artifact> --expect <digest>`, leaves a declaration
/// that pins that digest. A second `vendor` with no flag accepts the artifact.
/// `resolve`, once the version is set, gets past the pin and the version.
///
/// [#641]: https://github.com/headwater-ai/headwater/issues/641
/// [#1063]: https://github.com/headwater-ai/headwater/issues/1063
#[test]
fn vendor_with_expect_writes_the_pin_the_next_run_reads() {
    let root = Root::over("vendor-expect-pins");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    assert_eq!(pinned_digest(&root), None, "`init` pins nothing");

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor --expect` accepts the artifact:\n{stderr}"
    );
    assert_eq!(
        pinned_digest(&root),
        Some(digest.clone()),
        "`vendor --expect` wrote the pin it verified into the declaration:\n{}",
        root.read(".headwater/taxonomy.yml")
    );
    let declaration = root.read(".headwater/taxonomy.yml");
    assert!(
        !declaration.contains("# digest:"),
        "the commented `digest` line is the one the pin replaced:\n{declaration}"
    );

    let (code, stderr) = root.run(&["taxonomy", "vendor"], Some(&artifact));
    assert_eq!(
        code,
        Some(0),
        "the next `vendor`, with no flag, reads the pin the first one wrote:\n{stderr}"
    );
    assert!(
        !stderr.contains("nothing pins this artifact"),
        "the next `vendor` finds a pin:\n{stderr}"
    );

    let version = maintained_version();
    root.write(
        ".headwater/taxonomy.yml",
        &declaration.replace("  version: 0.0.0\n", &format!("  version: {version}\n")),
    );
    let (_, stderr) = root.run(&["taxonomy", "resolve"], None);
    assert!(
        !stderr.contains("digest") && !stderr.contains("this takes headwater/standard"),
        "`taxonomy resolve` is past the pin and the version:\n{stderr}"
    );
}

/// A digest the artifact does not match writes nothing into the declaration.
#[test]
fn vendor_with_a_wrong_expect_leaves_the_declaration_byte_identical() {
    let root = Root::over("vendor-expect-wrong");
    let artifact = root.beside("artifact");
    publish_maintained_source_into(&artifact);
    root.init();
    let before = root.read(".headwater/taxonomy.yml");

    let wrong = format!("sha256:{}", "0".repeat(64));
    let (code, _) = root.run(
        &["taxonomy", "vendor", "--expect", wrong.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(1),
        "an artifact the digest does not name is refused"
    );
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        before,
        "a refused `vendor` writes nothing into the declaration"
    );
}

/// A declared pin is never overwritten, whether `--expect` agrees with it or
/// not.
///
/// A different `--expect` still vendors the artifact that value names, as it
/// did before #1063, and `pin.current` reports the stale declared pin. This
/// case holds the declaration byte-identical under both values.
#[test]
fn vendor_with_expect_never_overwrites_a_declared_pin() {
    let root = Root::over("vendor-expect-declared");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    let declaration = root.read(".headwater/taxonomy.yml");
    let commented = declaration
        .lines()
        .find(|line| line.trim_start().starts_with("# digest:"))
        .expect("the declaration names the field `taxonomy vendor` reads");

    for (label, declared) in [
        ("an equal", digest.clone()),
        ("a different", format!("sha256:{}", "a".repeat(64))),
    ] {
        let pinned = declaration.replace(commented, &format!("  digest: {declared}"));
        root.write(".headwater/taxonomy.yml", &pinned);
        let (code, stderr) = root.run(
            &["taxonomy", "vendor", "--expect", digest.as_str()],
            Some(&artifact),
        );
        assert_eq!(
            code,
            Some(0),
            "`vendor --expect` installs the artifact:\n{stderr}"
        );
        assert_eq!(
            root.read(".headwater/taxonomy.yml"),
            pinned,
            "{label} `--expect` leaves a declared pin byte-identical"
        );
    }
}

/// The fetch path records the pin as the directory path does.
#[cfg(feature = "fetch")]
#[test]
fn vendor_with_expect_from_a_location_writes_the_pin() {
    let root = Root::over("vendor-expect-location");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();

    let location = serve(
        &format!("headwater-standard-{}.zip", maintained_version()),
        zipped(&artifact),
    );
    let (code, stderr) = root.run(
        &[
            "taxonomy",
            "vendor",
            location.as_str(),
            "--expect",
            digest.as_str(),
        ],
        None,
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor <location> --expect` accepts the artifact:\n{stderr}"
    );
    assert_eq!(
        pinned_digest(&root),
        Some(digest),
        "`vendor <location> --expect` wrote the pin it verified:\n{}",
        root.read(".headwater/taxonomy.yml")
    );
}

/// On a declaration with no commented `digest` line, the pin is one line added
/// after `version:` at the indentation of its siblings, and every other byte
/// stays.
#[test]
fn vendor_with_expect_adds_one_line_to_a_declaration_with_no_commented_digest() {
    let root = Root::over("vendor-expect-one-line");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    // An authored declaration: the commented line removed, the version set.
    let written = root.read(".headwater/taxonomy.yml");
    let commented = written
        .lines()
        .find(|line| line.trim_start().starts_with("# digest:"))
        .expect("the declaration names the field `taxonomy vendor` reads");
    let before = written.replace(&format!("{commented}\n"), "").replace(
        "  version: 0.0.0\n",
        &format!("  version: {}\n", maintained_version()),
    );
    root.write(".headwater/taxonomy.yml", &before);

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor --expect` accepts the artifact:\n{stderr}"
    );
    assert_eq!(pinned_digest(&root), Some(digest.clone()));

    let version_line = before
        .lines()
        .find(|line| line.starts_with("  version:"))
        .expect("the declaration pins a version");
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        before.replacen(
            &format!("{version_line}\n"),
            &format!("{version_line}\n  digest: {digest}\n"),
            1
        ),
        "the pin is the one line that moved"
    );
}

/// Where the value of `version:` continues on a deeper line, the pin lands
/// after that line and never inside the value.
///
/// Held for #1063: without the count of deeper lines in `with_pin`, the pin
/// sits between the key and its value, the value then continues the pin, the
/// pin does not read back, and the verb restores the file and exits 1.
#[test]
fn vendor_with_expect_puts_the_pin_after_a_version_value_on_a_deeper_line() {
    let root = Root::over("vendor-expect-deeper-version");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    let written = root.read(".headwater/taxonomy.yml");
    let commented = written
        .lines()
        .find(|line| line.trim_start().starts_with("# digest:"))
        .expect("the declaration names the field `taxonomy vendor` reads");
    let version_block = format!("  version:\n    {}\n", maintained_version());
    let before = written
        .replace(&format!("{commented}\n"), "")
        .replace("  version: 0.0.0\n", &version_block);
    assert!(
        before.contains(&version_block),
        "the declaration carries the value on its own line:\n{before}"
    );
    root.write(".headwater/taxonomy.yml", &before);

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor --expect` pins a declaration whose version value is on a deeper line:\n{stderr}"
    );
    assert_eq!(pinned_digest(&root), Some(digest.clone()));
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        before.replacen(
            &version_block,
            &format!("{version_block}  digest: {digest}\n"),
            1
        ),
        "the pin is one line after the value, and nothing else moved"
    );
}

/// Where the `taxonomy:` block is last in the file and its last line has no
/// newline, the pin is a line of its own.
///
/// Held for #1063: without the newline `with_pin` adds to that last line, the
/// pin joins it, the pin does not read back, and the verb restores the file
/// and exits 1.
#[test]
fn vendor_with_expect_pins_a_last_line_with_no_trailing_newline() {
    let root = Root::over("vendor-expect-no-trailing-newline");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    let version = maintained_version();
    let before = format!(
        "corpus:\n  root: docs\n\ntaxonomy:\n  package: headwater/standard\n  bundles: []\n  overlay: .headwater/overlay.yml\n  version: {version}"
    );
    root.write(".headwater/taxonomy.yml", &before);

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor --expect` pins a declaration whose last line has no newline:\n{stderr}"
    );
    assert_eq!(pinned_digest(&root), Some(digest.clone()));
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        format!("{before}\n  digest: {digest}\n"),
        "the last line gains its newline, and the pin follows it"
    );
}

/// Every comment the adopter wrote keeps its bytes, whatever it says about a
/// digest.
///
/// Only the placeholder line `init` writes is the verb's to replace. The three
/// comments below each begin `# digest:`, and a verify run found a build that
/// replaced the first of them it met: an audit note in the block, a note at
/// column zero above the next key, and a note inside `bundles`, where the edit
/// then did not parse and the verb refused a valid file.
#[test]
fn vendor_with_expect_keeps_every_comment_it_does_not_own() {
    let root = Root::over("vendor-expect-comments");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    let before = format!(
        "taxonomy:\n  package: headwater/standard\n  # digest: sha256:0000 was the old pin, \
         keep for audit\n  version: {}\n  bundles:\n    # digest: see the release notes\n    \
         []\n  overlay: .headwater/overlay.yml\n# digest: the upstream pin, see below\ncorpus:\n  \
         root: docs\n",
        maintained_version()
    );
    root.write(".headwater/taxonomy.yml", &before);

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(0),
        "`vendor --expect` pins a valid file:\n{stderr}"
    );
    let version_line = format!("  version: {}\n", maintained_version());
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        before.replacen(
            &version_line,
            &format!("{version_line}  digest: {digest}\n"),
            1
        ),
        "the pin is one line after `version:`, and every comment stays"
    );
}

/// An edit that does not read back as this pin is undone, byte for byte.
///
/// `digest: []` declares no pin that `vendor` can read, so the run goes ahead
/// on `--expect` and adds a second `digest` key. The read-back refuses that
/// file, and the verb puts the original bytes back rather than leave it.
#[test]
fn a_pin_that_does_not_read_back_leaves_the_declaration_byte_identical() {
    let root = Root::over("vendor-expect-restore");
    let artifact = root.beside("artifact");
    let digest = publish_maintained_source_into(&artifact);
    root.init();
    let before = root
        .read(".headwater/taxonomy.yml")
        .replace("  version: 0.0.0\n", "  version: 0.0.0\n  digest: []\n");
    root.write(".headwater/taxonomy.yml", &before);

    let (code, stderr) = root.run(
        &["taxonomy", "vendor", "--expect", digest.as_str()],
        Some(&artifact),
    );
    assert_eq!(
        code,
        Some(1),
        "the verb refuses a pin it could not write:\n{stderr}"
    );
    assert!(
        stderr.contains("restored"),
        "the refusal says the file was put back:\n{stderr}"
    );
    assert_eq!(
        root.read(".headwater/taxonomy.yml"),
        before,
        "the declaration is byte for byte what it was"
    );
}