diff --git a/packages/python/pyproject.toml b/packages/python/pyproject.toml index 3fad947..c4ed25c 100644 --- a/packages/python/pyproject.toml +++ b/packages/python/pyproject.toml @@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta" [project] name = "synapt-extract" -version = "0.6.0" +version = "0.6.1" description = "SynaptExtraction IL v1 -- schema, validation, and finalization" readme = "README.md" license = "MIT" diff --git a/packages/python/src/synapt/extract/__init__.py b/packages/python/src/synapt/extract/__init__.py index 1394933..747d044 100644 --- a/packages/python/src/synapt/extract/__init__.py +++ b/packages/python/src/synapt/extract/__init__.py @@ -1,5 +1,16 @@ """synapt-extract: SynaptExtraction IL v1 schema, validation, and finalization.""" +#: The version of this package, available at runtime. +#: +#: Consumers that record which extractor produced a document should read this +#: rather than hand-copying a version string, so the recorded value is evidence +#: of what ran instead of a claim about it. +#: +#: Kept in step with ``packages/python/pyproject.toml`` and the TypeScript +#: package; ``scripts/bump-version.sh`` updates all three and +#: ``tests/python/test_version.py`` fails if any one of them drifts. +__version__ = "0.6.1" + from synapt.extract.schema import ( SynaptExtraction, SynaptEntity, @@ -128,4 +139,5 @@ "BatchUnit", "BatchUnitResult", "extract_batch", + "__version__", ] diff --git a/packages/ts/package.json b/packages/ts/package.json index 474977f..d9e120c 100644 --- a/packages/ts/package.json +++ b/packages/ts/package.json @@ -1,6 +1,6 @@ { "name": "@synapt-dev/extract", - "version": "0.6.0", + "version": "0.6.1", "description": "SynaptExtraction IL v1 -- schema, validation, and finalization", "type": "module", "main": "dist/index.js", diff --git a/packages/ts/src/index.ts b/packages/ts/src/index.ts index 6dede24..0ed2da8 100644 --- a/packages/ts/src/index.ts +++ b/packages/ts/src/index.ts @@ -19,6 +19,8 @@ export type { } from "./schema.js"; export { EXTRACTION_CAPABILITIES } from "./schema.js"; +export { VERSION } from "./version.js"; + export { validateExtraction } from "./validate.js"; export type { ValidationResult, ValidationError } from "./validate.js"; diff --git a/packages/ts/src/version.ts b/packages/ts/src/version.ts new file mode 100644 index 0000000..36cdd88 --- /dev/null +++ b/packages/ts/src/version.ts @@ -0,0 +1,18 @@ +/** + * The version of this package, available at runtime. + * + * Consumers that record which extractor produced a document should read this + * rather than hand-copying a version string, so the recorded value is evidence + * of what ran instead of a claim about it. + * + * Deliberately a literal and not a read of `package.json`: the default entry + * has to stay importable in browser and WASM hosts, and + * `scripts/check-ts-universal-entry.mjs` fails the build if it reaches for a + * Node built-in. `tests/test_version.ts` ties this constant to the manifest and + * to the Python distribution, so a one-sided edit fails there rather than + * shipping. + * + * Keep in step with `packages/ts/package.json` and + * `packages/python/pyproject.toml`; `scripts/bump-version.sh` updates all three. + */ +export const VERSION = "0.6.1"; diff --git a/packages/ts/tests/test_version.ts b/packages/ts/tests/test_version.ts new file mode 100644 index 0000000..bff41f2 --- /dev/null +++ b/packages/ts/tests/test_version.ts @@ -0,0 +1,58 @@ +import { readFileSync } from "node:fs"; +import { resolve } from "node:path"; + +import { describe, expect, test } from "vitest"; + +import { VERSION } from "../src/index.js"; + +const REPO_ROOT = resolve(import.meta.dirname, "..", "..", ".."); + +function packageJsonVersion(...parts: string[]): string { + const pkg = JSON.parse(readFileSync(resolve(...parts), "utf-8")) as { version?: string }; + if (typeof pkg.version !== "string") throw new Error(`no version in ${resolve(...parts)}`); + return pkg.version; +} + +/** The `version = "x.y.z"` line from a pyproject, without adding a TOML dependency. */ +function pyprojectVersion(path: string): string { + const match = /^version\s*=\s*"([^"]+)"/m.exec(readFileSync(path, "utf-8")); + if (match === null) throw new Error(`no version line in ${path}`); + return match[1]; +} + +/** + * WHY THIS FILE EXISTS + * + * A consumer that records which extractor produced a document needs to obtain + * the version FROM the package. Before `VERSION` existed there was no way to, + * so the only option we offered was hand-copying a string into a constant -- + * and a hand-copied version is a claim about the runtime, not evidence of it. + * A downstream consumer did exactly that and its copy went stale (declared + * 0.5.0 against a 0.6.0 runtime) with nothing able to detect it. + * + * `VERSION` is a literal rather than a read of package.json on purpose: the + * default entry must stay importable in browser/WASM hosts, and + * `scripts/check-ts-universal-entry.mjs` fails the build if it reaches for a + * Node built-in. That is the same trade the embedded prompt fragments make, so + * it carries the same obligation -- an embedded copy needs a test that fails + * when it drifts from its source. That is what this is. + */ +describe("VERSION", () => { + test("matches the TypeScript package manifest", () => { + expect(VERSION).toBe(packageJsonVersion(REPO_ROOT, "packages", "ts", "package.json")); + }); + + test("matches the Python distribution version", () => { + // The two language surfaces ship as one product at one version. Nothing + // enforced that before this test: they were two hand-edited numbers that + // happened to agree. + expect(VERSION).toBe(pyprojectVersion(resolve(REPO_ROOT, "packages", "python", "pyproject.toml"))); + }); + + test("is a bare semver triple, not a range or a specifier", () => { + // Guards the shape a consumer stamps into provenance. "^0.6.0" or + // "@synapt-dev/extract@0.6.0" would each be a plausible thing to paste + // here and each would corrupt the recorded value. + expect(VERSION).toMatch(/^\d+\.\d+\.\d+$/); + }); +}); diff --git a/scripts/bump-version.sh b/scripts/bump-version.sh new file mode 100755 index 0000000..8662850 --- /dev/null +++ b/scripts/bump-version.sh @@ -0,0 +1,125 @@ +#!/bin/bash +# Bump the synapt-extract version across every place it is written. +# +# Usage: +# ./scripts/bump-version.sh 0.7.0 +# ./scripts/bump-version.sh patch # 0.6.0 -> 0.6.1 +# ./scripts/bump-version.sh minor # 0.6.0 -> 0.7.0 +# ./scripts/bump-version.sh major # 0.6.0 -> 1.0.0 +# +# WHY THIS EXISTS +# +# The version is written in FOUR places: the two package manifests and the two +# runtime constants that let a consumer read the version instead of hand-copying +# it. Four hand-edited numbers is precisely the shape that drifts, and a stale +# version is not a cosmetic problem here -- it is recorded into downstream +# provenance as evidence of what produced a document. +# +# `tests/python/test_version.py` and `packages/ts/tests/test_version.ts` fail +# when any one of the four drifts. This script is how you avoid tripping them. +# +# It deliberately does NOT commit, tag, or push. Under the dev/main branching +# model a tag belongs to the release ceremony on `main`, not to whatever branch +# happens to be checked out when someone bumps a number. + +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "$0")" && pwd)" +REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)" + +PYPROJECT="$REPO_ROOT/packages/python/pyproject.toml" +INIT_PY="$REPO_ROOT/packages/python/src/synapt/extract/__init__.py" +TS_PKG="$REPO_ROOT/packages/ts/package.json" +TS_VERSION="$REPO_ROOT/packages/ts/src/version.ts" + +for f in "$PYPROJECT" "$INIT_PY" "$TS_PKG" "$TS_VERSION"; do + [ -f "$f" ] || { echo "Error: missing $f" >&2; exit 1; } +done + +# --- Read current version (pyproject is the reference) --- +CURRENT=$(grep '^version = ' "$PYPROJECT" | head -1 | sed 's/version = "\(.*\)"/\1/') +if [ -z "$CURRENT" ]; then + echo "Error: could not read version from $PYPROJECT" >&2 + exit 1 +fi + +IFS='.' read -r MAJOR MINOR PATCH <<< "$CURRENT" + +ARG="${1:-}" +if [ -z "$ARG" ]; then + echo "Current version: $CURRENT" + echo "" + echo "Usage: $0 " + echo " $0 patch -> $MAJOR.$MINOR.$((PATCH + 1))" + echo " $0 minor -> $MAJOR.$((MINOR + 1)).0" + echo " $0 major -> $((MAJOR + 1)).0.0" + echo " $0 0.7.0 -> 0.7.0" + exit 0 +fi + +case "$ARG" in + patch) NEW="$MAJOR.$MINOR.$((PATCH + 1))" ;; + minor) NEW="$MAJOR.$((MINOR + 1)).0" ;; + major) NEW="$((MAJOR + 1)).0.0" ;; + *) + if ! echo "$ARG" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+$'; then + echo "Error: '$ARG' is not a bare semver triple (x.y.z) or patch/minor/major." >&2 + echo "A range or a specifier here would be written into consumer provenance." >&2 + exit 1 + fi + NEW="$ARG" + ;; +esac + +echo "Bumping $CURRENT -> $NEW" + +# --- Apply. Each pattern is anchored so it cannot match a dependency's version. +python3 - "$NEW" "$PYPROJECT" "$INIT_PY" "$TS_PKG" "$TS_VERSION" <<'PY' +import re +import sys + +new, pyproject, init_py, ts_pkg, ts_version = sys.argv[1:6] + +def sub(path, pattern, replacement): + text = open(path, encoding="utf-8").read() + updated, n = re.subn(pattern, replacement, text, count=1, flags=re.MULTILINE) + if n != 1: + raise SystemExit(f"Error: expected exactly 1 version match in {path}, found {n}") + open(path, "w", encoding="utf-8").write(updated) + +sub(pyproject, r'^version = "[^"]+"', f'version = "{new}"') +sub(init_py, r'^__version__ = "[^"]+"', f'__version__ = "{new}"') +sub(ts_pkg, r'^( "version": )"[^"]+"', rf'\g<1>"{new}"') +sub(ts_version, r'^export const VERSION = "[^"]+"', f'export const VERSION = "{new}"') +PY + +# --- Verify by fruit. The point of this script is that four numbers agree, so +# --- it checks that they do rather than reporting success for having run. +echo "" +echo "Verifying all four locations:" +FAIL=0 +check() { + local label="$1" actual="$2" + printf " %-34s %s" "$label" "$actual" + if [ "$actual" = "$NEW" ]; then echo " ok"; else echo " MISMATCH (expected $NEW)"; FAIL=1; fi +} +check "pyproject.toml" "$(grep '^version = ' "$PYPROJECT" | head -1 | sed 's/version = "\(.*\)"/\1/')" +check "python __init__.py" "$(grep '^__version__ = ' "$INIT_PY" | head -1 | sed 's/__version__ = "\(.*\)"/\1/')" +check "ts package.json" "$(grep '^ "version": ' "$TS_PKG" | head -1 | sed 's/.*: "\(.*\)".*/\1/')" +check "ts src/version.ts" "$(grep '^export const VERSION = ' "$TS_VERSION" | sed 's/.*"\(.*\)".*/\1/')" + +if [ "$FAIL" -ne 0 ]; then + echo "" + echo "Error: the four locations do not agree. Nothing was committed; fix before proceeding." >&2 + exit 1 +fi + +echo "" +echo "All four agree at $NEW. Next:" +echo " 1. REINSTALL the python package first: pip install -e packages/python" +echo " (test_installed_distribution_agrees... compares importlib.metadata against" +echo " the source constant, so it correctly reports RED until the installed" +echo " distribution is rebuilt at $NEW. That is the check doing its job, not a bug.)" +echo " 2. run the suites (pytest tests/python && cd packages/ts && npm test)" +echo " 3. PR the bump into dev" +echo " 4. release ceremony merges dev -> main, then 'gh release create v$NEW' cuts the tag" diff --git a/tests/python/fixtures/extract-batch-real-failures-v1.json b/tests/python/fixtures/extract-batch-real-failures-v1.json index 9df5398..320895c 100644 --- a/tests/python/fixtures/extract-batch-real-failures-v1.json +++ b/tests/python/fixtures/extract-batch-real-failures-v1.json @@ -6,7 +6,7 @@ }, "boundary": { "target": "OSS synapt-extract tests", - "review": "The selected sensitivity content contains public product/process facts only. No unpublished scores, private implementation, secrets, or user data are included." + "review": "The sensitivity content is synthetic, invented subject matter (a fictional company's operations). No real product internals, unpublished scores, private implementation, secrets, or user data are included." }, "interpretation": { "normalization_scope": "Repair envelope and leaf shape only. Preserve text and semantic placement; do not silently rewrite wrong-category or incomplete content.", @@ -52,12 +52,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -77,7 +77,7 @@ ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" } ], "temporal_refs": [] @@ -134,12 +134,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"entity_refs\": null,\n \"decided_at\": null\n },\n {\n \"text\": \"because one extract call produces one envelope rather than one packet per input unit\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"entity_refs\": null,\n \"decided_at\": null\n },\n {\n \"text\": \"because one harbor sweep clears one manifest rather than one ticket per boarding group\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -162,10 +162,10 @@ ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, { - "text": "because one extract call produces one envelope rather than one packet per input unit" + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group" } ], "temporal_refs": [] @@ -223,12 +223,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"entity_refs\": null,\n \"decided_at\": null\n },\n {\n \"text\": \"because one extract call produces one envelope rather than one packet per input unit\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"entity_refs\": null,\n \"decided_at\": null\n },\n {\n \"text\": \"because one harbor sweep clears one manifest rather than one ticket per boarding group\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -250,10 +250,10 @@ ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, { - "text": "because one extract call produces one envelope rather than one packet per input unit" + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group" } ], "temporal_refs": [] @@ -311,26 +311,26 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M09", "pool": "multi", - "text": "On June 22, 2026, the team established that independent Modal variants should run concurrently with spawn.", + "text": "On June 22, 2026, the team established that standby ferry routes should run concurrently with the peak schedule.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.\",\n \"decided_at\": null,\n \"entity_refs\": null\n },\n {\n \"text\": \"independent Modal variants should run concurrently with spawn.\",\n \"decided_at\": \"2026-06-22T00:00:00Z\",\n \"entity_refs\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22T00:00:00Z\",\n \"context\": \"decision reference\"\n }\n ]\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.\",\n \"decided_at\": null,\n \"entity_refs\": null\n },\n {\n \"text\": \"standby ferry routes should run concurrently with the peak schedule.\",\n \"decided_at\": \"2026-06-22T00:00:00Z\",\n \"entity_refs\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22T00:00:00Z\",\n \"context\": \"decision reference\"\n }\n ]\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -353,10 +353,10 @@ ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit." + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group." }, { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z" } ], @@ -447,26 +447,26 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M09", "pool": "multi", - "text": "On June 22, 2026, the team established that independent Modal variants should run concurrently with spawn.", + "text": "On June 22, 2026, the team established that standby ferry routes should run concurrently with the peak schedule.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.\",\n \"decided_at\": null,\n \"entity_refs\": null\n },\n {\n \"text\": \"independent Modal variants should run concurrently with spawn.\",\n \"decided_at\": \"2026-06-22T00:00:00Z\",\n \"entity_refs\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22T00:00:00Z\",\n \"context\": \"decision reference\"\n }\n ]\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.\",\n \"decided_at\": null,\n \"entity_refs\": null\n },\n {\n \"text\": \"standby ferry routes should run concurrently with the peak schedule.\",\n \"decided_at\": \"2026-06-22T00:00:00Z\",\n \"entity_refs\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22T00:00:00Z\",\n \"context\": \"decision reference\"\n }\n ]\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -489,10 +489,10 @@ ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit." + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group." }, { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z" } ], @@ -583,26 +583,26 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M09", "pool": "multi", - "text": "On June 22, 2026, the team established that independent Modal variants should run concurrently with spawn.", + "text": "On June 22, 2026, the team established that standby ferry routes should run concurrently with the peak schedule.", "expected_leaf_count": 1, "expected_type": "decision" }, { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n },\n {\n \"text\": \"The team established that independent Modal variants should run concurrently with spawn on June 22, 2026.\",\n \"category\": \"temporal_decision\"\n }\n ],\n \"decisions\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22\",\n \"context\": \"Establishment of concurrent spawn for Modal variants\"\n }\n ]\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\",\n \"facts\": [\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n },\n {\n \"text\": \"The team established that standby ferry routes should run concurrently with the peak schedule on June 22, 2026.\",\n \"category\": \"temporal_decision\"\n }\n ],\n \"decisions\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"entity_refs\": null,\n \"decided_at\": null\n }\n ],\n \"temporal_refs\": [\n {\n \"raw\": \"June 22, 2026\",\n \"type\": \"point\",\n \"resolved\": \"2026-06-22\",\n \"context\": \"Establishment of concurrent standby routes for the peak schedule\"\n }\n ]\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -622,13 +622,13 @@ "text": "The premium package is proprietary." }, { - "text": "The team established that independent Modal variants should run concurrently with spawn on June 22, 2026.", + "text": "The team established that standby ferry routes should run concurrently with the peak schedule on June 22, 2026.", "category": "temporal_decision" } ], "decisions": [ { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" } ], "temporal_refs": [ @@ -711,7 +711,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -723,7 +723,7 @@ "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO timestamp\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO timestamp\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -738,7 +738,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, { "text": "The recall package is open source." @@ -792,7 +792,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -804,7 +804,7 @@ "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current timestamp\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current timestamp\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -819,7 +819,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, { "text": "The recall package is open source." @@ -873,7 +873,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -885,7 +885,7 @@ "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -900,7 +900,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, { "text": "The recall package is open source." @@ -954,7 +954,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -975,12 +975,12 @@ { "unit_id": "S04", "pool": "simple", - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for current ISO 8601 timestamp\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": null\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": null\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": null\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -996,7 +996,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, { "text": "The recall package is open source." @@ -1081,7 +1081,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -1102,12 +1102,12 @@ { "unit_id": "S04", "pool": "simple", - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": \"coordination\"\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The extract library emits SynaptExtraction documents.\",\n \"category\": \"output_format\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": \"coordination\"\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The loading crew emits standardized cargo manifests.\",\n \"category\": \"output_format\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -1119,7 +1119,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": "coordination" }, { @@ -1131,7 +1131,7 @@ "category": "licensing" }, { - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "category": "output_format" } ], @@ -1211,7 +1211,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" }, @@ -1232,12 +1232,12 @@ { "unit_id": "S04", "pool": "simple", - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "expected_leaf_count": 1, "expected_type": "fact" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"Synapt uses gitgrip to coordinate multiple repositories.\",\n \"category\": \"coordination\"\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The extract library emits SynaptExtraction documents.\",\n \"category\": \"output_format\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"Harborline uses a shared dispatch board to coordinate multiple depots.\",\n \"category\": \"coordination\"\n },\n {\n \"text\": \"The recall package is open source.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The premium package is proprietary.\",\n \"category\": \"licensing\"\n },\n {\n \"text\": \"The loading crew emits standardized cargo manifests.\",\n \"category\": \"output_format\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -1249,7 +1249,7 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": "coordination" }, { @@ -1261,7 +1261,7 @@ "category": "licensing" }, { - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "category": "output_format" } ], @@ -1341,12 +1341,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one extract call produces one envelope rather than one packet per input unit\",\n \"category\": \"implementation_detail\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction time\n \"facts\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one harbor sweep clears one manifest rather than one ticket per boarding group\",\n \"category\": \"implementation_detail\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -1358,11 +1358,11 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "category": "technical" }, { - "text": "one extract call produces one envelope rather than one packet per input unit", + "text": "one harbor sweep clears one manifest rather than one ticket per boarding group", "category": "implementation_detail" } ], @@ -1401,12 +1401,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction timestamp\n \"facts\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one extract call produces one envelope rather than one packet per input unit\",\n \"category\": \"implementation\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction timestamp\n \"facts\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one harbor sweep clears one manifest rather than one ticket per boarding group\",\n \"category\": \"implementation\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -1418,11 +1418,11 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "category": "technical" }, { - "text": "one extract call produces one envelope rather than one packet per input unit", + "text": "one harbor sweep clears one manifest rather than one ticket per boarding group", "category": "implementation" } ], @@ -1461,12 +1461,12 @@ { "unit_id": "M05", "pool": "multi", - "text": "The team decided to measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "The team decided to track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "expected_leaf_count": 1, "expected_type": "decision" } ], - "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction timestamp\n \"facts\": [\n {\n \"text\": \"measure fidelity at the facts-and-decisions leaf level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one extract call produces one envelope rather than one packet per input unit\",\n \"category\": \"implementation_detail\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", + "raw_model_output": "```json\n{\n \"extracted_at\": \"2023-11-15T00:00:00Z\", // Placeholder for actual extraction timestamp\n \"facts\": [\n {\n \"text\": \"track punctuality at the per-berth arrival level\",\n \"category\": \"technical\"\n },\n {\n \"text\": \"one harbor sweep clears one manifest rather than one ticket per boarding group\",\n \"category\": \"implementation_detail\"\n }\n ],\n \"decisions\": [],\n \"temporal_refs\": []\n}\n```", "observed_strict_json_parse_success": false, "observed_finish_reason": "stop", "expected_unwrap_actions": [ @@ -1478,11 +1478,11 @@ "extracted_at": "2023-11-15T00:00:00Z", "facts": [ { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "category": "technical" }, { - "text": "one extract call produces one envelope rather than one packet per input unit", + "text": "one harbor sweep clears one manifest rather than one ticket per boarding group", "category": "implementation_detail" } ], @@ -1521,7 +1521,7 @@ { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1571,7 +1571,7 @@ { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1621,7 +1621,7 @@ { "unit_id": "M01", "pool": "multi", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1671,7 +1671,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1720,7 +1720,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1770,7 +1770,7 @@ { "unit_id": "S01", "pool": "simple", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_leaf_count": 1, "expected_type": "fact" } @@ -1821,7 +1821,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "entity_refs": null, "decided_at": null }, @@ -1838,7 +1838,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, "semantic_reclassification_allowed": false }, @@ -1879,7 +1879,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "entity_refs": null, "decided_at": null }, @@ -1896,7 +1896,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, "semantic_reclassification_allowed": false }, @@ -1910,7 +1910,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "because one extract call produces one envelope rather than one packet per input unit", + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group", "entity_refs": null, "decided_at": null }, @@ -1927,7 +1927,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "because one extract call produces one envelope rather than one packet per input unit" + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group" }, "semantic_reclassification_allowed": false }, @@ -1968,7 +1968,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "entity_refs": null, "decided_at": null }, @@ -1985,7 +1985,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, "semantic_reclassification_allowed": false }, @@ -1999,7 +1999,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "because one extract call produces one envelope rather than one packet per input unit", + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group", "entity_refs": null, "decided_at": null }, @@ -2016,7 +2016,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "because one extract call produces one envelope rather than one packet per input unit" + "text": "because one harbor sweep clears one manifest rather than one ticket per boarding group" }, "semantic_reclassification_allowed": false }, @@ -2057,7 +2057,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "decided_at": null, "entity_refs": null }, @@ -2073,7 +2073,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit." + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group." }, "semantic_reclassification_allowed": false }, @@ -2087,7 +2087,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z", "entity_refs": null }, @@ -2101,7 +2101,7 @@ "omit_null_or_invalid_entity_refs" ], "expected_normalized_leaf": { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z" }, "semantic_reclassification_allowed": false @@ -2143,7 +2143,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit.", + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group.", "decided_at": null, "entity_refs": null }, @@ -2159,7 +2159,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level because one extract call produces one envelope rather than one packet per input unit." + "text": "track punctuality at the per-berth arrival level because one harbor sweep clears one manifest rather than one ticket per boarding group." }, "semantic_reclassification_allowed": false }, @@ -2173,7 +2173,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z", "entity_refs": null }, @@ -2187,7 +2187,7 @@ "omit_null_or_invalid_entity_refs" ], "expected_normalized_leaf": { - "text": "independent Modal variants should run concurrently with spawn.", + "text": "standby ferry routes should run concurrently with the peak schedule.", "decided_at": "2026-06-22T00:00:00Z" }, "semantic_reclassification_allowed": false @@ -2229,7 +2229,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "entity_refs": null, "decided_at": null }, @@ -2246,7 +2246,7 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level" + "text": "track punctuality at the per-berth arrival level" }, "semantic_reclassification_allowed": false }, @@ -2287,7 +2287,7 @@ ], "field": "facts", "raw_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": null }, "observed_schema_errors": [ @@ -2300,7 +2300,7 @@ "omit_null_or_non_string_category" ], "expected_normalized_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, "semantic_reclassification_allowed": false }, @@ -2341,7 +2341,7 @@ ], "field": "facts", "raw_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": null }, "observed_schema_errors": [ @@ -2354,7 +2354,7 @@ "omit_null_or_non_string_category" ], "expected_normalized_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, "semantic_reclassification_allowed": false }, @@ -2395,7 +2395,7 @@ ], "field": "facts", "raw_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": null }, "observed_schema_errors": [ @@ -2408,7 +2408,7 @@ "omit_null_or_non_string_category" ], "expected_normalized_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, "semantic_reclassification_allowed": false }, @@ -2449,7 +2449,7 @@ ], "field": "facts", "raw_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "category": null }, "observed_schema_errors": [ @@ -2462,7 +2462,7 @@ "omit_null_or_non_string_category" ], "expected_normalized_leaf": { - "text": "Synapt uses gitgrip to coordinate multiple repositories." + "text": "Harborline uses a shared dispatch board to coordinate multiple depots." }, "semantic_reclassification_allowed": false }, @@ -2537,7 +2537,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2566,7 +2566,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2595,7 +2595,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2624,7 +2624,7 @@ ], "dropped_source_unit": { "source_unit_id": "S04", - "text": "The extract library emits SynaptExtraction documents.", + "text": "The loading crew emits standardized cargo manifests.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2650,7 +2650,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2676,7 +2676,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2702,7 +2702,7 @@ ], "dropped_source_unit": { "source_unit_id": "M01", - "text": "The extract builder receives clean knowledge units, while recall remains responsible for identifying those units and reconciling them afterward.", + "text": "The dispatch desk receives sorted cargo pallets, while the harbor ledger remains responsible for identifying those pallets and reconciling them afterward.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2728,7 +2728,7 @@ ], "dropped_source_unit": { "source_unit_id": "S01", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2754,7 +2754,7 @@ ], "dropped_source_unit": { "source_unit_id": "S01", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2780,7 +2780,7 @@ ], "dropped_source_unit": { "source_unit_id": "S01", - "text": "Synapt uses gitgrip to coordinate multiple repositories.", + "text": "Harborline uses a shared dispatch board to coordinate multiple depots.", "expected_type": "fact" }, "observed_mapped_leaf_ids": [], @@ -2806,7 +2806,7 @@ ], "field": "decisions", "raw_leaf": { - "text": "On July 1, 2026, per-epoch checkpointing became mandatory for long Modal jobs after repeated preemptions.", + "text": "On July 1, 2026, per-voyage logging became mandatory for long freight runs after repeated cancellations.", "decided_at": "2026-07-01", "context": "mandatory rule" }, @@ -2817,7 +2817,7 @@ "drop_unknown_key:context" ], "expected_normalized_leaf": { - "text": "On July 1, 2026, per-epoch checkpointing became mandatory for long Modal jobs after repeated preemptions.", + "text": "On July 1, 2026, per-voyage logging became mandatory for long freight runs after repeated cancellations.", "decided_at": "2026-07-01" } }, @@ -2831,11 +2831,11 @@ ], "field": "facts", "raw_leaf": { - "text": "On July 1, 2026, per-epoch checkpointing became mandatory for long Modal jobs after repeated preemptions.", + "text": "On July 1, 2026, per-voyage logging became mandatory for long freight runs after repeated cancellations.", "type": "range", "resolved": "2026-07-01T00:00:00Z", "resolved_end": "2026-07-13T00:00:00Z", - "context": "Mandatory after preemptions" + "context": "Mandatory after cancellations" }, "observed_schema_errors": [ "extra_keys:context,resolved,resolved_end,type" @@ -2847,7 +2847,7 @@ "drop_unknown_key:type" ], "expected_normalized_leaf": { - "text": "On July 1, 2026, per-epoch checkpointing became mandatory for long Modal jobs after repeated preemptions." + "text": "On July 1, 2026, per-voyage logging became mandatory for long freight runs after repeated cancellations." } } ], @@ -2903,7 +2903,7 @@ "raw": "June 22, 2026", "type": "point", "resolved": "2026-06-22", - "context": "Establishment of concurrent spawn for Modal variants" + "context": "Establishment of concurrent standby routes for the peak schedule" }, "observed_prompt_schema_conflict": "The builder prompt requests type/context/resolved_end, but the Stage-1 schema permits only raw and optional resolved.", "expected_actions": [ @@ -2924,8 +2924,8 @@ "empirical_difference": "The run emitted entity_refs=null, never a scalar. This fixture changes only that value to a string because scalar-to-array is in the shared contract.", "field": "decisions", "raw_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", - "entity_refs": "facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", + "entity_refs": "per-berth arrival level", "decided_at": null }, "expected_actions": [ @@ -2933,9 +2933,9 @@ "omit_null_or_non_string_decided_at" ], "expected_normalized_leaf": { - "text": "measure fidelity at the facts-and-decisions leaf level", + "text": "track punctuality at the per-berth arrival level", "entity_refs": [ - "facts-and-decisions leaf level" + "per-berth arrival level" ] }, "must_not_be_reported_as_empirically_observed": true diff --git a/tests/python/test_extract_batch.py b/tests/python/test_extract_batch.py index ff51352..f20e6f6 100644 --- a/tests/python/test_extract_batch.py +++ b/tests/python/test_extract_batch.py @@ -27,7 +27,7 @@ RECALL_CAPABILITIES = ["facts", "decisions", "temporal_refs"] PRODUCED_BY = "mlx://mlx-community/Ministral-3-3B-Instruct-2512-4bit" EXTRACTED_AT = "2026-07-13T10:00:00Z" -FIXTURE_SHA256 = "d01bda9b4369c56a681cd9861bc9ed78293e32cb7fd1ed0310535758fe3adf2c" +FIXTURE_SHA256 = "c0451311c5706750dda8d2ee2ed9cec8f2208dc46f298b6be00f75d893947fbf" FIXTURE_PATH = Path(__file__).parent / "fixtures" / "extract-batch-real-failures-v1.json" FIXTURE_BYTES = FIXTURE_PATH.read_bytes() FIXTURES = json.loads(FIXTURE_BYTES) diff --git a/tests/python/test_version.py b/tests/python/test_version.py new file mode 100644 index 0000000..09d8534 --- /dev/null +++ b/tests/python/test_version.py @@ -0,0 +1,86 @@ +"""The runtime version is evidence, not a claim. + +WHY THIS FILE EXISTS + +A consumer that records which extractor produced a document needs to obtain the +version FROM the package. Before ``__version__`` existed there was no way to, so +the only option we offered was hand-copying a string -- and a hand-copied +version is a claim about the runtime rather than evidence of it. A downstream +consumer did exactly that and its copy went stale (declared 0.5.0 against a +0.6.0 runtime) with nothing able to detect the drift. + +These tests tie every place the version is written to every other place, so any +single-sided edit fails here instead of shipping. +""" +from __future__ import annotations + +import re +import sys +from pathlib import Path + +import pytest + +sys.path.insert(0, str(Path(__file__).resolve().parents[2] / "packages" / "python" / "src")) + +import synapt.extract +from synapt.extract import __version__ + +REPO_ROOT = Path(__file__).resolve().parents[2] +PYPROJECT = REPO_ROOT / "packages" / "python" / "pyproject.toml" +TS_PACKAGE_JSON = REPO_ROOT / "packages" / "ts" / "package.json" + +_SEMVER = re.compile(r"^\d+\.\d+\.\d+$") + + +def _pyproject_version() -> str: + match = re.search(r'^version\s*=\s*"([^"]+)"', PYPROJECT.read_text(), re.MULTILINE) + assert match is not None, f"no version line in {PYPROJECT}" + return match.group(1) + + +def _ts_package_version() -> str: + import json + + return json.loads(TS_PACKAGE_JSON.read_text())["version"] + + +def test_version_matches_the_distribution_metadata(): + assert __version__ == _pyproject_version() + + +def test_version_matches_the_typescript_package(): + """The two language surfaces ship as one product at one version. + + Nothing enforced this before: they were two hand-edited numbers that + happened to agree.""" + assert __version__ == _ts_package_version() + + +def test_version_is_exported_from_the_package_root(): + """A consumer must reach it without knowing the internal module layout.""" + assert synapt.extract.__version__ == __version__ + + +def test_version_is_a_bare_semver_triple(): + """Guards the shape stamped into provenance. A range (">=0.6.0") or a full + specifier ("@synapt-dev/extract@0.6.0") are each a plausible thing to paste + into this constant, and each would corrupt the recorded value.""" + assert _SEMVER.match(__version__), f"not a bare semver triple: {__version__!r}" + + +def test_installed_distribution_agrees_when_the_package_is_installed(): + """importlib.metadata reads what pip actually installed, which is a + genuinely different source than the literal in __init__.py -- so this + catches a stale editable install or a version-bump that never got reinstalled, + which the other assertions here cannot see. + + Skipped rather than failed when the package is imported from a source tree + with no installed distribution, since that is a legitimate way to use it. + """ + from importlib.metadata import PackageNotFoundError, version + + try: + installed = version("synapt-extract") + except PackageNotFoundError: + pytest.skip("synapt-extract is not installed as a distribution") + assert installed == __version__