{
  "kind": "editorial offline synthetic calculation; no AI or seller API test",
  "captured_at": "2026-10-30T09:02:51.322579+00:00",
  "input_provenance": "nine fictional examples created by editorial planning; no real orders or personal data",
  "python": "3.14.4",
  "command_actually_run": "python3 normalize.py",
  "working_directory": "fresh temporary directory containing only normalize.py and cases.json",
  "exit_code": 0,
  "stdout": "{\"checked\": 9, \"passed\": 9, \"status_counts\": {\"known\": 4, \"unknown\": 4, \"conflict\": 1}}\n",
  "elapsed_seconds": 0.05323881498770788,
  "output_byte_equal_to_repository": {
    "normalized.json": true,
    "table.md": true
  },
  "files": {
    "normalize.py": {
      "sha256": "3372c381752715ce98f6186cb45471834d528b457f1ce91e3fe25a7ed5628172",
      "content": "#!/usr/bin/env python3\n\"\"\"Offline editorial check of explicitly synthetic getShipment planned intervals.\nNo API calls; no inference about lateness, acceptance or sanctions.\nRun from repository root: python3 data/editorial/ai-market-fbs-shipment-timezone-boundary/normalize.py\n\"\"\"\nimport json\nimport re\nfrom collections import Counter\nfrom datetime import datetime, timezone\nfrom pathlib import Path\n\nROOT = Path(__file__).resolve().parent\nSTAMP = re.compile(r'\\d{4}-\\d{2}-\\d{2}T\\d{2}:\\d{2}:\\d{2}(?:\\.\\d+)?(?:Z|[+-]\\d{2}:\\d{2})?\\Z')\nOFFSET = re.compile(r'(?:Z|[+-]\\d{2}:\\d{2})\\Z')\nFIELDS = ('planIntervalFrom', 'planIntervalTo')\n\n\ndef normalize(source):\n    result = {'input': dict(source), 'start_utc': None, 'end_utc': None,\n              'duration_seconds': None, 'lateness': 'not_assessed'}\n    def unknown(reason):\n        return dict(result, status='unknown', reason=reason)\n    if not any(field in source for field in FIELDS):\n        return unknown('not_getShipment_plan_interval')\n    if any(source.get(field) in (None, '') for field in FIELDS):\n        return unknown('missing_plan_interval_field')\n    times = []\n    for field, output in zip(FIELDS, ('start_utc', 'end_utc')):\n        value = source[field]\n        if not isinstance(value, str):\n            return unknown('invalid_timestamp_type')\n        if re.fullmatch(r'\\d{4}-\\d{2}-\\d{2}', value):\n            return unknown('date_not_timestamp')\n        if not STAMP.fullmatch(value):\n            return unknown('invalid_timestamp')\n        if not OFFSET.search(value) or value.endswith('-00:00'):\n            return unknown('missing_utc_offset')\n        try:\n            parsed = datetime.fromisoformat(value.replace('Z', '+00:00'))\n        except ValueError:\n            return unknown('invalid_timestamp')\n        utc = parsed.astimezone(timezone.utc)\n        times.append(utc)\n        result[output] = utc.isoformat()\n    result['duration_seconds'] = (times[1] - times[0]).total_seconds()\n    if result['duration_seconds'].is_integer():\n        result['duration_seconds'] = int(result['duration_seconds'])\n    if times[1] < times[0]:\n        return dict(result, status='conflict', reason='end_before_start')\n    return dict(result, status='known')\n\n\ndef main():\n    fixtures = json.loads((ROOT / 'cases.json').read_text())\n    rows = []\n    for case in fixtures['cases']:\n        actual = normalize(case['input'])\n        mismatch = {key: {'expected': val, 'actual': actual.get(key)}\n                    for key, val in case['expected'].items() if actual.get(key) != val}\n        rows.append(dict(id=case['id'], **actual, expected_match=not mismatch, mismatches=mismatch))\n    assert len(rows) == 9\n    assert all(row['expected_match'] for row in rows), rows\n    report = {'kind': fixtures['kind'], 'contract': fixtures['contract'],\n              'editorial_policy': fixtures['editorial_policy'], 'cases': rows,\n              'checked': len(rows), 'passed': sum(row['expected_match'] for row in rows),\n              'status_counts': dict(Counter(row['status'] for row in rows)),\n              'not_checked': ['seller API runtime', 'AI extraction accuracy', 'real shipments',\n                              'lateness and sanctions', 'any timestamp formats outside this parser']}\n    (ROOT / 'normalized.json').write_text(json.dumps(report, ensure_ascii=False, indent=2) + '\\n')\n    header = '# Девять синтетических интервалов FBS\\n\\nНе реальные заказы и не результаты теста ИИ. unknown/conflict — редакционные метки, не статусы Маркета.\\n\\n'\n    table = '| Кейс | Исходное начало | Исходный конец | Начало UTC | Конец UTC | Секунды | Результат |\\n|---|---|---|---|---|---|---|\\n'\n    for row in rows:\n        source = row['input']\n        values = [row['id'], source.get(FIELDS[0], 'нет'), source.get(FIELDS[1], 'нет'),\n                  row['start_utc'] or 'неизвестно', row['end_utc'] or 'неизвестно',\n                  str(row['duration_seconds']) if row['duration_seconds'] is not None else 'неизвестно',\n                  row['status'] + (': ' + row['reason'] if 'reason' in row else '')]\n        table += '| ' + ' | '.join(values) + ' |\\n'\n    (ROOT / 'table.md').write_text(header + table + '\\nОтрицательная длительность — конфликт входа; границы не переставляем. Полный исходный объект, включая посторонние поля, сохранён в normalized.json.\\n')\n    print(json.dumps({k: report[k] for k in ('checked', 'passed', 'status_counts')}, ensure_ascii=False))\n\n\nif __name__ == '__main__':\n    main()\n"
    },
    "cases.json": {
      "sha256": "0ad9bb5a63fc0af77441e7b1b8a9530bcd270a996e0eaa0308fbec04db723a8b",
      "content": "{\n  \"kind\": \"editorial_synthetic_cases_not_real_orders_or_AI_results\",\n  \"contract\": \"getShipment.result.planIntervalFrom/planIntervalTo; preserve source text and UTC offset; only normalize a documented planned interval\",\n  \"editorial_policy\": \"unknown and conflict labels are conservative editorial rules, not API statuses; never infer lateness, acceptance or sanctions from planned interval alone\",\n  \"cases\": [\n    {\n      \"id\": \"offset-03\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00+03:00\"\n      },\n      \"expected\": {\n        \"status\": \"known\",\n        \"start_utc\": \"2026-10-30T12:00:00+00:00\",\n        \"end_utc\": \"2026-10-30T13:00:00+00:00\",\n        \"duration_seconds\": 3600\n      }\n    },\n    {\n      \"id\": \"cross-midnight\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T23:30:00+03:00\",\n        \"planIntervalTo\": \"2026-10-31T00:30:00+03:00\"\n      },\n      \"expected\": {\n        \"status\": \"known\",\n        \"start_utc\": \"2026-10-30T20:30:00+00:00\",\n        \"end_utc\": \"2026-10-30T21:30:00+00:00\",\n        \"duration_seconds\": 3600\n      }\n    },\n    {\n      \"id\": \"different-offsets\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T18:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T15:30:00Z\"\n      },\n      \"expected\": {\n        \"status\": \"known\",\n        \"start_utc\": \"2026-10-30T15:00:00+00:00\",\n        \"end_utc\": \"2026-10-30T15:30:00+00:00\",\n        \"duration_seconds\": 1800\n      }\n    },\n    {\n      \"id\": \"offset-05\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+05:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00+05:00\"\n      },\n      \"expected\": {\n        \"status\": \"known\",\n        \"start_utc\": \"2026-10-30T10:00:00+00:00\",\n        \"end_utc\": \"2026-10-30T11:00:00+00:00\",\n        \"duration_seconds\": 3600\n      }\n    },\n    {\n      \"id\": \"missing-offset\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00\"\n      },\n      \"expected\": {\n        \"status\": \"unknown\",\n        \"reason\": \"missing_utc_offset\"\n      }\n    },\n    {\n      \"id\": \"date-only\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30\",\n        \"planIntervalTo\": \"2026-10-31\"\n      },\n      \"expected\": {\n        \"status\": \"unknown\",\n        \"reason\": \"date_not_timestamp\"\n      }\n    },\n    {\n      \"id\": \"missing-end\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+03:00\"\n      },\n      \"expected\": {\n        \"status\": \"unknown\",\n        \"reason\": \"missing_plan_interval_field\"\n      }\n    },\n    {\n      \"id\": \"wrong-field\",\n      \"input\": {\n        \"shipmentTime\": \"16:00:00\",\n        \"shipmentDate\": \"2026-10-30\"\n      },\n      \"expected\": {\n        \"status\": \"unknown\",\n        \"reason\": \"not_getShipment_plan_interval\"\n      }\n    },\n    {\n      \"id\": \"inverted\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T16:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T15:00:00+03:00\"\n      },\n      \"expected\": {\n        \"status\": \"conflict\",\n        \"reason\": \"end_before_start\"\n      }\n    }\n  ]\n}\n"
    },
    "normalized.json": {
      "sha256": "772a7930c442a08a88c9725e42a0679ae7cfb46c6b54f3a0baf96c707ed6cc65",
      "content": "{\n  \"kind\": \"editorial_synthetic_cases_not_real_orders_or_AI_results\",\n  \"contract\": \"getShipment.result.planIntervalFrom/planIntervalTo; preserve source text and UTC offset; only normalize a documented planned interval\",\n  \"editorial_policy\": \"unknown and conflict labels are conservative editorial rules, not API statuses; never infer lateness, acceptance or sanctions from planned interval alone\",\n  \"cases\": [\n    {\n      \"id\": \"offset-03\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00+03:00\"\n      },\n      \"start_utc\": \"2026-10-30T12:00:00+00:00\",\n      \"end_utc\": \"2026-10-30T13:00:00+00:00\",\n      \"duration_seconds\": 3600,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"known\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"cross-midnight\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T23:30:00+03:00\",\n        \"planIntervalTo\": \"2026-10-31T00:30:00+03:00\"\n      },\n      \"start_utc\": \"2026-10-30T20:30:00+00:00\",\n      \"end_utc\": \"2026-10-30T21:30:00+00:00\",\n      \"duration_seconds\": 3600,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"known\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"different-offsets\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T18:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T15:30:00Z\"\n      },\n      \"start_utc\": \"2026-10-30T15:00:00+00:00\",\n      \"end_utc\": \"2026-10-30T15:30:00+00:00\",\n      \"duration_seconds\": 1800,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"known\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"offset-05\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+05:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00+05:00\"\n      },\n      \"start_utc\": \"2026-10-30T10:00:00+00:00\",\n      \"end_utc\": \"2026-10-30T11:00:00+00:00\",\n      \"duration_seconds\": 3600,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"known\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"missing-offset\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00\",\n        \"planIntervalTo\": \"2026-10-30T16:00:00\"\n      },\n      \"start_utc\": null,\n      \"end_utc\": null,\n      \"duration_seconds\": null,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"unknown\",\n      \"reason\": \"missing_utc_offset\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"date-only\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30\",\n        \"planIntervalTo\": \"2026-10-31\"\n      },\n      \"start_utc\": null,\n      \"end_utc\": null,\n      \"duration_seconds\": null,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"unknown\",\n      \"reason\": \"date_not_timestamp\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"missing-end\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T15:00:00+03:00\"\n      },\n      \"start_utc\": null,\n      \"end_utc\": null,\n      \"duration_seconds\": null,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"unknown\",\n      \"reason\": \"missing_plan_interval_field\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"wrong-field\",\n      \"input\": {\n        \"shipmentTime\": \"16:00:00\",\n        \"shipmentDate\": \"2026-10-30\"\n      },\n      \"start_utc\": null,\n      \"end_utc\": null,\n      \"duration_seconds\": null,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"unknown\",\n      \"reason\": \"not_getShipment_plan_interval\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    },\n    {\n      \"id\": \"inverted\",\n      \"input\": {\n        \"planIntervalFrom\": \"2026-10-30T16:00:00+03:00\",\n        \"planIntervalTo\": \"2026-10-30T15:00:00+03:00\"\n      },\n      \"start_utc\": \"2026-10-30T13:00:00+00:00\",\n      \"end_utc\": \"2026-10-30T12:00:00+00:00\",\n      \"duration_seconds\": -3600,\n      \"lateness\": \"not_assessed\",\n      \"status\": \"conflict\",\n      \"reason\": \"end_before_start\",\n      \"expected_match\": true,\n      \"mismatches\": {}\n    }\n  ],\n  \"checked\": 9,\n  \"passed\": 9,\n  \"status_counts\": {\n    \"known\": 4,\n    \"unknown\": 4,\n    \"conflict\": 1\n  },\n  \"not_checked\": [\n    \"seller API runtime\",\n    \"AI extraction accuracy\",\n    \"real shipments\",\n    \"lateness and sanctions\",\n    \"any timestamp formats outside this parser\"\n  ]\n}\n"
    },
    "table.md": {
      "sha256": "fb0102826042afd247276fc4439006a9b969dc8933876b95bbac658f47bc2142",
      "content": "# Девять синтетических интервалов FBS\n\nНе реальные заказы и не результаты теста ИИ. unknown/conflict — редакционные метки, не статусы Маркета.\n\n| Кейс | Исходное начало | Исходный конец | Начало UTC | Конец UTC | Секунды | Результат |\n|---|---|---|---|---|---|---|\n| offset-03 | 2026-10-30T15:00:00+03:00 | 2026-10-30T16:00:00+03:00 | 2026-10-30T12:00:00+00:00 | 2026-10-30T13:00:00+00:00 | 3600 | known |\n| cross-midnight | 2026-10-30T23:30:00+03:00 | 2026-10-31T00:30:00+03:00 | 2026-10-30T20:30:00+00:00 | 2026-10-30T21:30:00+00:00 | 3600 | known |\n| different-offsets | 2026-10-30T18:00:00+03:00 | 2026-10-30T15:30:00Z | 2026-10-30T15:00:00+00:00 | 2026-10-30T15:30:00+00:00 | 1800 | known |\n| offset-05 | 2026-10-30T15:00:00+05:00 | 2026-10-30T16:00:00+05:00 | 2026-10-30T10:00:00+00:00 | 2026-10-30T11:00:00+00:00 | 3600 | known |\n| missing-offset | 2026-10-30T15:00:00 | 2026-10-30T16:00:00 | неизвестно | неизвестно | неизвестно | unknown: missing_utc_offset |\n| date-only | 2026-10-30 | 2026-10-31 | неизвестно | неизвестно | неизвестно | unknown: date_not_timestamp |\n| missing-end | 2026-10-30T15:00:00+03:00 | нет | неизвестно | неизвестно | неизвестно | unknown: missing_plan_interval_field |\n| wrong-field | нет | нет | неизвестно | неизвестно | неизвестно | unknown: not_getShipment_plan_interval |\n| inverted | 2026-10-30T16:00:00+03:00 | 2026-10-30T15:00:00+03:00 | 2026-10-30T13:00:00+00:00 | 2026-10-30T12:00:00+00:00 | -3600 | conflict: end_before_start |\n\nОтрицательная длительность — конфликт входа; границы не переставляем. Полный исходный объект, включая посторонние поля, сохранён в normalized.json.\n"
    }
  },
  "record_id": "fbs-planned-interval-v1",
  "repository_record": "data/tests/fbs-planned-interval-v1/record.json",
  "identity": {
    "record_id": "fbs-planned-interval-v1",
    "repository_record": "data/tests/fbs-planned-interval-v1",
    "scope": "offline synthetic arithmetic, not AI test",
    "verification": {
      "command": "python3 data/tests/fbs-planned-interval-v1/normalize.py",
      "exit_code": 0,
      "stdout": "{\"checked\": 9, \"passed\": 9, \"status_counts\": {\"known\": 4, \"unknown\": 4, \"conflict\": 1}}\n",
      "elapsed_seconds": 0.0480460730032064,
      "outputs_equal_initial_record": {
        "normalized.json": true,
        "table.md": true
      }
    },
    "source_material": "ai-market-fbs-shipment-timezone-boundary",
    "initial_record_sha256": "33c4c2f286078a409090bfa0f753a4a6e21370527df3f97ebd7ceee0918f4a8f"
  }
}
