{
  "part": 10,
  "title": "source.fullSendPolicy",
  "reset": "none",
  "_comment": "TESTING.md says this Part 'continues directly from Part 9 Step 9.3' with no reset, relying on ds1's no-common-base state persisting across all four policy steps. Tracing ZFS_Utils.pm confirms the source status file is updated unconditionally for any non-skipped send (even a full send that never lands on the target - see Part 7.2a's own note about this) - so after Step 10.1's own 'allow' send, ds1's recorded resume point would already match its only snapshot, and Steps 10.2-10.4 would find nothing new to send at all rather than re-exercising the no-common-base branch. Per project decision, each step below re-establishes a fresh, genuine no-common-base condition for ds1 (the same recipe Part 9 Step 9.3 uses) immediately before testing its own policy value, rather than relying on carry-over.",
  "steps": [
    { "id": "10.1", "title": "allow: full send proceeds quietly",
      "config": { "mutations": [ { "op": "set", "path": "source.fullSendPolicy", "value": "allow" } ], "restore": "none" },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{DST}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "rm_matching", "dir": "{SN}", "pattern": "^sneakernet_target\\.status(\\.[0-9_.-]+)?$" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S1_10_1" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "expect_exit": 0 },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "expect_exit": 0 },
        { "do": "zfs_destroy", "dataset": "{SRC}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "zfs_create", "dataset": "{SRC}/ds1" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S2_10_1" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 4, "log": "part10_1", "expect_exit": 0,
          "capture": { "name": "SEND_COUNT_101", "select": "doSourceReplication: Running command: (zfs send.*)", "reduce": "count" } }
      ],
      "assertions": [
        { "type": "count_equals", "var": "SEND_COUNT_101", "n": 1, "message": "a full-send command for ds1 is generated (it will fail on the target side, same as Part 7 - we're only checking the source's behavior here)" }
      ]
    },
    { "id": "10.2", "title": "warn (the default): full send proceeds, loudly", "depends_on": ["10.1"],
      "config": { "mutations": [ { "op": "set", "path": "source.fullSendPolicy", "value": "warn" } ], "restore": "none" },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{DST}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "rm_matching", "dir": "{SN}", "pattern": "^sneakernet_target\\.status(\\.[0-9_.-]+)?$" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S1_10_2" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "expect_exit": 0 },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "expect_exit": 0 },
        { "do": "zfs_destroy", "dataset": "{SRC}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "zfs_create", "dataset": "{SRC}/ds1" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S2_10_2" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "log": "part10_2", "expect_exit": 0,
          "capture": { "name": "LOG102" } }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG102", "pattern": "(?i)sending a FULL stream", "message": "the warning appears even at low verbosity - would be visible right at the top of an emailed/report OVERVIEW" }
      ]
    },
    { "id": "10.3", "title": "skip: excluded, but other datasets still work", "depends_on": ["10.2"],
      "config": { "mutations": [ { "op": "set", "path": "source.fullSendPolicy", "value": "skip" } ], "restore": "none" },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{DST}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "rm_matching", "dir": "{SN}", "pattern": "^sneakernet_target\\.status(\\.[0-9_.-]+)?$" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S1_10_3" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "expect_exit": 0 },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "expect_exit": 0 },
        { "do": "zfs_destroy", "dataset": "{SRC}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "zfs_create", "dataset": "{SRC}/ds1" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S2_10_3" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds2@S1_10_3" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 4, "log": "part10_3", "expect_exit": 0,
          "capture": { "name": "LOG103" } },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "expect_exit": 0 },
        { "do": "zfs_list_snapshots", "dataset": "{DST}/ds2", "capture": { "name": "DST_DS2_103", "reduce": "join" } },
        { "do": "read_file", "path": "{SN}/sneakernet_target.status", "capture": { "name": "STATUS_TEXT_103" } }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG103", "pattern": "(?i)EXCLUDED", "message": "'EXCLUDED from this run' for ds1" },
        { "type": "regex_absent", "var": "LOG103", "pattern": "doSourceReplication: Running command:.*ds1\\@", "message": "ds1's send command does not appear" },
        { "type": "regex_present", "var": "LOG103", "pattern": "doSourceReplication: Running command:.*ds2\\@", "message": "ds2's send command appears - it has no problem and is unaffected" },
        { "type": "regex_present", "var": "DST_DS2_103", "pattern": "ds2\\@S1_10_3", "message": "ds2 replicates successfully" },
        { "type": "regex_present", "var": "STATUS_TEXT_103", "pattern": "ds1\\@S1_10_3", "message": "ds1's OLD resume point is unchanged - nothing was falsely recorded as sent" }
      ]
    },
    { "id": "10.4", "title": "abort: the whole run fails closed", "depends_on": ["10.3"],
      "config": { "mutations": [ { "op": "set", "path": "source.fullSendPolicy", "value": "abort" } ], "restore": "none" },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{DST}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "rm_matching", "dir": "{SN}", "pattern": "^sneakernet_target\\.status(\\.[0-9_.-]+)?$" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S1_10_4" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "expect_exit": 0 },
        { "do": "run_sneakernet", "servername": "test-target", "verbosity": 1, "expect_exit": 0 },
        { "do": "zfs_destroy", "dataset": "{SRC}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "zfs_create", "dataset": "{SRC}/ds1" },
        { "do": "zfs_snapshot", "dataset": "{SRC}/ds1@S2_10_4" },
        { "do": "empty_dir", "path": "{TRANSPORT}" },
        { "do": "run_sneakernet", "servername": "test-source", "verbosity": 1, "log": "part10_4", "expect_exit": "any",
          "capture": { "name": "LOG104" } },
        { "do": "list_matching", "dir": "{TRANSPORT}/datasets", "pattern": ".*",
          "capture": { "name": "TRANSPORT_FILES_104", "reduce": "count" } }
      ],
      "assertions": [
        { "type": "regex_present", "var": "LOG104", "pattern": "(?i)FATAL ERROR", "message": "FATAL ERROR appears in the output" },
        { "type": "count_equals", "var": "TRANSPORT_FILES_104", "n": 0, "message": "nothing was written to the transport drive's datasets folder - the whole run refused rather than sending anything partial" }
      ]
    },
    { "id": "10.4-cleanup", "title": "Restore fullSendPolicy: warn; recreate a clean ds1 for Part 11", "depends_on": ["10.4"],
      "config": { "restore": "none", "mutations": [
        { "op": "set", "path": "source.fullSendPolicy", "value": "warn" }
      ] },
      "actions": [
        { "do": "zfs_destroy", "dataset": "{SRC}/ds1", "recursive": 1, "missing_ok": 1 },
        { "do": "zfs_create", "dataset": "{SRC}/ds1" }
      ]
    }
  ]
}
