notion-md: $EDITOR-based editing — VRS + implementation epic#786
Conversation
…gine Design the plugin-free, Unix-style $EDITOR two-way Notion<->Markdown editing surface and its notion-cli umbrella integration, captured as VRS. - cat/put: stateless stdin/stdout body pipes (gateway-only; cat --frontmatter is a read-only envelope dump; no stateless property write). - edit: an ephemeral file-engine session — pull into a $TMPDIR .nmd, body-only splice, push via the existing sync engine, clean up. Not a second push path. - Refuse-lossy is uniform across cat/put/edit and file sync: a page whose body contains a block that does not round-trip as Markdown (child_database, toc, synced_block, child_page-in-body, unsupported) is refused at the pull. R38 (sound fidelity classifier) is the blocking prerequisite — and a fix for a pre-existing file-path silent-data-loss defect proven by live testing. - Decisions 0001-0017 record the path: the block-reconciler/converter edifice (0005/0010/0011/0014/0015) and the stateless schema fingerprint (0013) were superseded once the platform ceiling and engine reuse were understood. Adds glossary, impl-delta, decision records; updates vision/requirements/spec/ experiments and the notion-cli docs. Design only; not yet implemented. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Storybook PreviewsNo storybooks were deployed. |
CI Measurementspartial - advisory gate - readiness
nix / closures / packages
Unchanged / 0-impact measurements (17)These rows had compatible baseline data, but their semantic impact rounded to 0.00x because the movement was below the configured budget, below the noise floor, or inside the robust noise band. devenv / devenv cli
devenv / devenv shell
devenv / genie
devenv / quality gates
devenv / workspace setup
nix / closures / packages
Diagnostic / ungated measurements (7)source / effect-utils
devenv / devenv shell
All measurements
Previous runs
Source-of-truth JSON{
"schemaVersion": 1,
"title": "CI Measurements",
"status": "partial",
"gate": "advisory",
"readiness": "partial (17/26 enabled observations gateable)",
"commit": {
"shortSha": "b9b23a5",
"sha": "b9b23a5a5512f46f499068b5f94cebe7bbfae20c"
},
"run": {
"id": "27650110831",
"attempt": "1",
"url": "https://github.com/overengineeringstudio/effect-utils/actions/runs/27650110831"
},
"baseline": null,
"protocol": "devenv-perf-warm-median-v2",
"chart": {
"meaning": "semantic-impact",
"zeroImpactMeaning": "no actionable PR impact after budgets, noise floor, and robust evidence checks",
"svg": "https://raw.githubusercontent.com/overengineeringstudio/effect-utils/ci-measurement-assets/ci-measurements/pr-786/b9b23a5a5512f46f499068b5f94cebe7bbfae20c/run-27650110831-attempt-1/ci-measurements.svg",
"lightPng": "https://raw.githubusercontent.com/overengineeringstudio/effect-utils/ci-measurement-assets/ci-measurements/pr-786/b9b23a5a5512f46f499068b5f94cebe7bbfae20c/run-27650110831-attempt-1/ci-measurements.png",
"darkPng": "https://raw.githubusercontent.com/overengineeringstudio/effect-utils/ci-measurement-assets/ci-measurements/pr-786/b9b23a5a5512f46f499068b5f94cebe7bbfae20c/run-27650110831-attempt-1/ci-measurements-dark.png"
},
"measurements": [
{
"id": "nix.closure.nar_size",
"label": "Total closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"total",
"closure-size",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 561623408,
"current": 561922280,
"delta": 298872,
"ratio": 1.000532157306378,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10933596.16,
"evidenceDeltaUpper": 11531340.16,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "nix.closure.nar_size",
"label": "Total closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"total",
"closure-size",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 151364760,
"current": 151426200,
"delta": 61440,
"ratio": 1.000405906896691,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10424320,
"evidenceDeltaUpper": 10547200,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "nix.closure.serialized_nar_size",
"label": "Total serialized NAR size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"total",
"serialized-nar-size",
"nix closure diagnostics"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "regressed",
"gateable": true,
"gateReason": "eligible",
"confidence": "within_budget",
"comparisonMode": "diagnostic",
"unit": "bytes",
"baseline": 151364760,
"current": 151426200,
"delta": 61440,
"ratio": 1.000405906896691,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 17,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -15075036,
"evidenceDeltaUpper": 15197916,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total",
"sizeKind": "nar"
}
},
{
"id": "nix.closure.serialized_nar_size",
"label": "Total serialized NAR size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"total",
"serialized-nar-size",
"nix closure diagnostics"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "regressed",
"gateable": true,
"gateReason": "eligible",
"confidence": "within_budget",
"comparisonMode": "diagnostic",
"unit": "bytes",
"baseline": 561873128,
"current": 561922280,
"delta": 49152,
"ratio": 1.0000874788231553,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 17,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -56138160.800000004,
"evidenceDeltaUpper": 56236464.800000004,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total",
"sizeKind": "nar"
}
},
{
"id": "nix.closure.nar_size",
"label": "Total closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"total",
"closure-size",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 163874192,
"current": 163884208,
"delta": 10016,
"ratio": 1.0000611200572693,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10475744,
"evidenceDeltaUpper": 10495776,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "nix.closure.serialized_nar_size",
"label": "Total serialized NAR size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"total",
"serialized-nar-size",
"nix closure diagnostics"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "regressed",
"gateable": true,
"gateReason": "eligible",
"confidence": "within_budget",
"comparisonMode": "diagnostic",
"unit": "bytes",
"baseline": 163874192,
"current": 163884208,
"delta": 10016,
"ratio": 1.0000611200572693,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 17,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -16377403.200000001,
"evidenceDeltaUpper": 16397435.200000001,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total",
"sizeKind": "nar"
}
},
{
"id": "source.lines",
"label": "Genie runtime lines",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"packages",
"genie",
"source / genie"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "regressed",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "lines",
"baseline": 19809,
"current": 20266,
"delta": 457,
"ratio": 1.023070321571003,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 67,
"pairedSamples": 0,
"evidenceDeltaLower": -1523.9,
"evidenceDeltaUpper": 2437.9,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "genie_runtime"
}
},
{
"id": "source.lines",
"label": "Genie CI workflow helpers lines",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"genie",
"ci-workflow",
"source / ci"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "regressed",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "lines",
"baseline": 7318,
"current": 7418,
"delta": 100,
"ratio": 1.0136649357748018,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 9,
"pairedSamples": 0,
"evidenceDeltaLower": -631.8000000000001,
"evidenceDeltaUpper": 831.8000000000001,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "genie_ci_workflow"
}
},
{
"id": "source.files",
"label": "Genie runtime files",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"packages",
"genie",
"source / genie"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "regressed",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "count",
"baseline": 66,
"current": 67,
"delta": 1,
"ratio": 1.0151515151515151,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 67,
"pairedSamples": 0,
"evidenceDeltaLower": -5.6000000000000005,
"evidenceDeltaUpper": 7.6000000000000005,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "genie_runtime"
}
},
{
"id": "genie_check_direct",
"label": "Genie check direct",
"group": "devenv / genie",
"path": [
"devenv",
"genie"
],
"groupPath": [
"devenv",
"genie"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 5.46,
"current": 5.401,
"delta": -0.05900000000000016,
"ratio": 0.9891941391941391,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 5,
"currentSamples": 5,
"pairedSamples": 5,
"evidenceDeltaLower": -0.072,
"evidenceDeltaUpper": 0.009,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "genie_check_direct",
"probeLabel": "Genie check direct",
"status": 0,
"sampleCount": 11,
"warmupCount": 1,
"measuredSampleCount": 5,
"pairedSampleCount": 5,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "task_check_quick_forced",
"label": "Forced check:quick",
"group": "devenv / quality gates",
"path": [
"devenv",
"quality gates",
"check:quick"
],
"groupPath": [
"devenv",
"quality gates"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 11.547,
"current": 11.592,
"delta": 0.04499999999999993,
"ratio": 1.0038971161340609,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 3,
"currentSamples": 3,
"pairedSamples": 3,
"evidenceDeltaLower": -0.106,
"evidenceDeltaUpper": 0.045,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"workload": "forced-task-cache",
"taskCacheMode": "refresh",
"probe": "task_check_quick_forced",
"probeLabel": "Forced check:quick",
"status": 0,
"sampleCount": 6,
"warmupCount": 0,
"measuredSampleCount": 3,
"pairedSampleCount": 3,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "tasks_list",
"label": "devenv tasks list",
"group": "devenv / devenv cli",
"path": [
"devenv",
"devenv cli"
],
"groupPath": [
"devenv",
"devenv cli"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 0.04,
"current": 0.063,
"delta": 0.023,
"ratio": 1.575,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 9,
"currentSamples": 9,
"pairedSamples": 9,
"evidenceDeltaLower": 0.001,
"evidenceDeltaUpper": 0.025,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "tasks_list",
"probeLabel": "devenv tasks list",
"status": 0,
"sampleCount": 19,
"warmupCount": 1,
"measuredSampleCount": 9,
"pairedSampleCount": 9,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "task_genie_run",
"label": "Genie run task",
"group": "devenv / genie",
"path": [
"devenv",
"genie"
],
"groupPath": [
"devenv",
"genie"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 1.256,
"current": 1.236,
"delta": -0.020000000000000018,
"ratio": 0.9840764331210191,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 5,
"currentSamples": 5,
"pairedSamples": 5,
"evidenceDeltaLower": 0.004,
"evidenceDeltaUpper": 0.089,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "task_genie_run",
"probeLabel": "Genie run task",
"status": 0,
"sampleCount": 11,
"warmupCount": 1,
"measuredSampleCount": 5,
"pairedSampleCount": 5,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "shell_eval_warm",
"label": "Warm shell eval",
"group": "devenv / devenv shell",
"path": [
"devenv",
"devenv shell"
],
"groupPath": [
"devenv",
"devenv shell"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 2.219,
"current": 2.236,
"delta": 0.017000000000000348,
"ratio": 1.007661108607481,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 5,
"currentSamples": 5,
"pairedSamples": 5,
"evidenceDeltaLower": -0.081,
"evidenceDeltaUpper": 0.25,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "shell_eval_warm",
"probeLabel": "Warm shell eval",
"status": 0,
"sampleCount": 11,
"warmupCount": 1,
"measuredSampleCount": 5,
"pairedSampleCount": 5,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "task_pnpm_install",
"label": "pnpm install task",
"group": "devenv / workspace setup",
"path": [
"devenv",
"workspace setup"
],
"groupPath": [
"devenv",
"workspace setup"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 0.643,
"current": 0.626,
"delta": -0.017000000000000015,
"ratio": 0.973561430793157,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 5,
"currentSamples": 5,
"pairedSamples": 5,
"evidenceDeltaLower": -0.06,
"evidenceDeltaUpper": -0.014,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "task_pnpm_install",
"probeLabel": "pnpm install task",
"status": 0,
"sampleCount": 11,
"warmupCount": 1,
"measuredSampleCount": 5,
"pairedSampleCount": 5,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "task_check_quick_warm",
"label": "Warm cached check:quick",
"group": "devenv / quality gates",
"path": [
"devenv",
"quality gates",
"check:quick"
],
"groupPath": [
"devenv",
"quality gates"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 3.019,
"current": 3.018,
"delta": -0.001000000000000334,
"ratio": 0.9996687644915534,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 5,
"currentSamples": 5,
"pairedSamples": 5,
"evidenceDeltaLower": -0.06,
"evidenceDeltaUpper": -0.001,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"workload": "cached-no-op",
"taskCacheMode": "warm",
"probe": "task_check_quick_warm",
"probeLabel": "Warm cached check:quick",
"status": 0,
"sampleCount": 11,
"warmupCount": 1,
"measuredSampleCount": 5,
"pairedSampleCount": 5,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "processes_help",
"label": "devenv processes --help",
"group": "devenv / devenv cli",
"path": [
"devenv",
"devenv cli"
],
"groupPath": [
"devenv",
"devenv cli"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "paired",
"unit": "seconds",
"baseline": 0.02,
"current": 0.02,
"delta": 0,
"ratio": 1,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 9,
"currentSamples": 9,
"pairedSamples": 9,
"evidenceDeltaLower": 0,
"evidenceDeltaUpper": 0.002,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"probe": "processes_help",
"probeLabel": "devenv processes --help",
"status": 0,
"sampleCount": 19,
"warmupCount": 1,
"measuredSampleCount": 9,
"pairedSampleCount": 9,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
},
{
"id": "source.files",
"label": "Genie CI workflow helpers files",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"genie",
"ci-workflow",
"source / ci"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "unchanged",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "count",
"baseline": 9,
"current": 9,
"delta": 0,
"ratio": 1,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 9,
"pairedSamples": 0,
"evidenceDeltaLower": -1,
"evidenceDeltaUpper": 1,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "genie_ci_workflow"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Nix sources closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"buckets",
"nix-sources",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "nix-sources"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Nix sources closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"buckets",
"nix-sources",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "nix-sources"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Nix sources closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"buckets",
"nix-sources",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "nix-sources"
}
},
{
"id": "source.files",
"label": "Nix workspace tools files",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"nix",
"workspace-tools",
"source / nix"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "unchanged",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "count",
"baseline": 14,
"current": 14,
"delta": 0,
"ratio": 1,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 14,
"pairedSamples": 0,
"evidenceDeltaLower": -1.4000000000000001,
"evidenceDeltaUpper": 1.4000000000000001,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "nix_workspace_tools"
}
},
{
"id": "source.lines",
"label": "Nix workspace tools lines",
"group": "source / effect-utils",
"path": [
"source",
"effect-utils",
"nix",
"workspace-tools",
"source / nix"
],
"groupPath": [
"source",
"effect-utils"
],
"status": "pass",
"direction": "unchanged",
"gateable": false,
"gateReason": "disabled",
"confidence": "diagnostic",
"comparisonMode": "budget",
"unit": "lines",
"baseline": 3632,
"current": 3632,
"delta": 0,
"ratio": 1,
"semanticImpactScore": null,
"semanticImpactKind": "diagnostic",
"baselineSources": 19,
"currentSamples": 14,
"pairedSamples": 0,
"evidenceDeltaLower": -363.20000000000005,
"evidenceDeltaUpper": 363.20000000000005,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"scope": "nix_workspace_tools"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Node / pnpm closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"buckets",
"node",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "node"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Node / pnpm closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"buckets",
"node",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "node"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Node / pnpm closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"buckets",
"node",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "node"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Rust closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"buckets",
"rust",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "rust"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Rust closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"buckets",
"rust",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "rust"
}
},
{
"id": "nix.closure.bucket.nar_size",
"label": "Rust closure size",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"buckets",
"rust",
"nix closure buckets"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "unknown",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "unknown",
"comparisonMode": "budget",
"unit": "bytes",
"baseline": 0,
"current": 0,
"delta": 0,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": "unknown",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10485760,
"evidenceDeltaUpper": 10485760,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "rust"
}
},
{
"id": "nix.closure.path_count",
"label": "Total closure path count",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"genie",
"total",
"path-count",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "count",
"baseline": 79,
"current": 79,
"delta": 0,
"ratio": 1,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10,
"evidenceDeltaUpper": 10,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "nix.closure.path_count",
"label": "Total closure path count",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"megarepo",
"total",
"path-count",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "count",
"baseline": 5,
"current": 5,
"delta": 0,
"ratio": 1,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10,
"evidenceDeltaUpper": 10,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "nix.closure.path_count",
"label": "Total closure path count",
"group": "nix / closures / packages",
"path": [
"nix",
"closures",
"packages",
"oxlint-npm",
"total",
"path-count",
"nix closure"
],
"groupPath": [
"nix",
"closures",
"packages"
],
"status": "pass",
"direction": "unchanged",
"gateable": true,
"gateReason": "eligible",
"confidence": "noise_floor",
"comparisonMode": "budget",
"unit": "count",
"baseline": 8,
"current": 8,
"delta": 0,
"ratio": 1,
"semanticImpactScore": 0,
"semanticImpactKind": "neutral",
"baselineSources": 18,
"currentSamples": 1,
"pairedSamples": 0,
"evidenceDeltaLower": -10,
"evidenceDeltaUpper": 10,
"pairedEvidenceQuantile": 0.25,
"dimensions": {
"bucket": "total"
}
},
{
"id": "shell_eval_traced",
"label": "Shell eval with OTEL trace",
"group": "devenv / devenv shell",
"path": [
"devenv",
"devenv shell"
],
"groupPath": [
"devenv",
"devenv shell"
],
"status": "missing_baseline",
"direction": "unknown",
"gateable": false,
"gateReason": "missing_baseline",
"confidence": "missing_baseline",
"comparisonMode": "historical",
"unit": "seconds",
"baseline": null,
"current": 132.693,
"delta": null,
"ratio": null,
"semanticImpactScore": null,
"semanticImpactKind": null,
"baselineSources": 0,
"currentSamples": 1,
"pairedSamples": null,
"evidenceDeltaLower": null,
"evidenceDeltaUpper": null,
"pairedEvidenceQuantile": null,
"dimensions": {
"probe": "shell_eval_traced",
"probeLabel": "Shell eval with OTEL trace",
"status": 0,
"sampleCount": 2,
"warmupCount": 0,
"measuredSampleCount": 1,
"pairedSampleCount": 1,
"pairedOrderProtocol": "balanced-seeded-alternating-v1",
"pairedOrderSeed": "27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07",
"measurementProtocol": "devenv-perf-warm-median-v2",
"aggregation": "median",
"phase": "warm",
"devenvRev": "2cf62a010000b70f15c78a72761fad7c9e6fb47a",
"otelServiceName": "devenv-perf-ci"
}
}
]
} |
#785) The body-fidelity classifier only flagged API-`unsupported` blocks, so `child_database`, `table_of_contents`, `synced_block`, `child_page`-in-body, and degraded `bookmark`/`embed`/`link_preview`/`breadcrumb`/`link_to_page` classified `complete`. These render to Markdown that Notion re-parses as a plain paragraph on push, so editing an unrelated paragraph silently destroyed the untouched block (live-proven; affected file `sync`, not just the planned editor). Extend `classifyBodyCompleteness` (notion-core, pure) to flag a curated set of known not-round-trip-safe block types and surface them in the lossy verdict. The shared refusal gate (`assertRemoteMarkdownComplete`) now refuses such pages at the pull on every surface (`cat`/`put`/`edit`/file `sync`/tree) with a message naming the block class and pointing to the Notion UI. `child_page` is dual-role: a child page that is a tree node (its own `.nmd` file) is tolerated via `tolerateTreeChildPages` on the tree path while still refused as a single page's body block; any other lossy block on the same tree node is still refused. Hosted/external media stays representable. Criterion is a type set (not a live reparse) on purpose: notion-core is pure and the endpoint vs independent renderings agree, so the suffix heuristic can't catch these. Covered by classifier/gate unit tests, fake-gateway refusal + tree-tolerance tests, and live E2E (lossy page refused at pull, representable page still round-trips). Closes #785 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…hashed/gated (R36, #785) Notion-hosted media (type:"file") renders with an expiring signed S3 URL whose X-Amz-*/signature/Expires query params rotate on every pull, making the rendered body hash volatile (breaking cat->put idempotence, staling base hashes with zero edits) and causing update_content/replace_content pushes on media pages to be rejected by the post-push semanticEquivalent gate (decision 0007). Add a shared canonicalizeMediaUrl / canonicalizeMediaUrlsInMarkdown in notion-effect-client that strips only the volatile signature/expiry query-param family by name, keeping origin + path + any benign params. The renderer applies it in getBlockUrl's Notion-hosted file.url branch so pull/cat output is deterministic; canonical-markdown.ts applies the identical function inside canonicalizeBlockMarkdown / semanticEquivalent so the hash path and the gate path use the exact same canonical form. External (stable) URLs are left untouched. Verified with deterministic unit tests (signed->canonical, rotated-signature equality, external-with-query untouched) and live E2E on real Notion: a hosted+external-media page's body hash is stable across two no-op pulls and a no-op push is not rejected by the post-push gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add the `cat` / `put` / `edit` Notion-as-Markdown editor surfaces from the
notion-md VRS ("Editor Surfaces"; impl-delta Group A). Builds on Group C / R38
(the fidelity classifier + uniform pull-gate refusal); reuses the body facade
and the file `sync` engine rather than adding a second push path (decision 0017).
- `cat <page> [--frontmatter]`: default-mode `# <title>` + body to stdout
(byte-exact), the title+body base hash to stderr (decisions 0001/0002/0006);
`--frontmatter` dumps the read-only `.nmd` envelope. Refuses a lossy page
(exit 3) at observe time.
- `put <page> (--base-hash <h> | --force)`: body+title from stdin as two writes
body-first (decision 0012, exit 10 on partial); guarded by default (exit 7 on
drift); `--force` concurrency-only (decision 0009) via an unconditional
last-writer-wins body replace; missing title-H1 -> exit 5. No `--frontmatter`
write.
- `edit <page> [--frontmatter]`: ephemeral file-engine session (decision 0017) —
`mktemp -d` under `$TMPDIR`, `pullPage`, body-only splice in
`$VISUAL`/`$EDITOR`/`vi`, `syncPage` with a forced full-body `replace_content`,
relocate any `.conflict.roughdraft.md` to a durable `<page>.conflict.md`, and
scope-clean the temp tree on every path; non-zero editor exit -> exit 8;
unchanged buffer -> no-op.
Shared title<->H1 splice helper with exact-byte round-trip (idempotent cat->put
fixpoint); `<page>` resolved via `parseNotionUuid` (id/dashed/URL), unresolvable
-> exit 4. Distinct tagged errors mapped to exit codes 0/1/3/4/5/7/8/9/10 at the
`runMain` teardown (after finalizers close, so `edit` cleanup is safe). Commands
wired under `notion-md cat|put|edit` and `notion md cat|put|edit`. OTEL spans
`notion-md.cat`/`put`; `edit` wraps the engine spans.
Hosted-media URL canonicalization (Group B) and `edit --frontmatter` schema-drift
(exit 6, Group F) are deferred; the editor serves the representable non-media
majority and tests on non-media pages.
Covered by splice/command unit tests, a fake-gateway `edit` e2e (round-trip,
no-op, editor-abort exit 8, lossy refusal exit 3), and live Notion E2E
(cat->put fixpoint, guarded conflict exit 7, real `$EDITOR` spawn round-trip,
lossy refusal exit 3, CLI exit-code mapping 0/3/4/5/7).
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…up F, R14)
Implement impl-delta Group F / decision 0017: detect data-source schema drift
before a property write through the file engine's sidecar schema_snapshot,
instead of the deleted stateless in-buffer fingerprint (decision 0013).
For a data-source-backed page, `pullPage` retrieves the parent data source
(`GET /v1/data_sources/{id}` via `page.parent.data_source_id`) and captures the
writable property schema into the sidecar `data_source` binding as a canonical
projection: `{ name, type, sorted option names }` sorted by property name,
options only for select/multi_select/status, hashing names not ids (a rename is
id-preserving), excluding ids/colors/descriptions/status-groups/timestamps/
created_by/last_edited_by/request_id/computed properties. Before any property
write the engine re-retrieves the live schema, recomputes the hash, and on drift
refuses with `NmdSchemaDriftError` (exit 6) — distinct from the exit-7 conflict
and not `--force`-able; resolve by re-pulling. Standalone pages have no snapshot
and skip the check. This is the path `edit --frontmatter` reuses (no
`put --frontmatter`, no parallel streaming subsystem).
- Add `NmdSchemaDriftError` (errors.ts, exit-codes.ts maps it to 6).
- Add `schema-snapshot.ts`: writable-only canonical projection + hash + binding
field projections.
- Add `retrieveDataSource` to the gateway (model.ts interface, live.ts impl,
observability span); thread the captured binding through `buildSyncState` at
all three pull/persist sites; assert-unchanged before all three property
writes in `pushGuarded`.
- Re-export `NmdSchemaDriftError` from mod.ts.
Tests: deterministic projection/drift unit tests (benign color-only stable; the
five structural mutations trip) + fake-gateway engine tests (drift refusal +
benign round-trip). notion-md 170 tests green. Downstream notion-datasource-sync
fake gateways gain the new method stub. Live E2E on real Notion confirmed: a
row's page.parent decodes as data_source_id, the sidecar binding is captured
non-null, a benign property push round-trips, and after a live structural schema
mutation a property push refuses with exit 6 without writing.
Refs VRS requirements R14, spec "Guard plumbing" / Push Flow step 8, decision
0017 (supersedes 0013), impl-delta Group F.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… umbrella alias Group G (R21-R24, R29): span-assertion tests for the editor surface via the in-process otelite capture bridge. Drive the real instrumented cat/put/edit paths against a fake gateway and assert the actually-emitted span shape (notion-md.cat/put/edit roots with their notion_md.* attributes; edit wraps the engine sync-page/status-page/push-page children, decision 0017). R24 leak guard pushes a sentinel body through the gateway and asserts no span attribute carries the body, a signed-URL marker, or a Bearer token. Add the previously-untested edit conflict-relocation path: an unmergeable concurrent same-line remote change relocates the roughdraft out of $TMPDIR to a durable <page>.conflict.md. The spec span table's nmd.* attribute shorthand was never implemented; tests assert the real notion_md.* keys (divergence documented for separate reconciliation, not papered over). notion-cli umbrella (R18, decision 0004): wire the top-level `notion edit <page>` marquee alias delegating to the same engine-backed session as `notion md edit` via a shared command factory. cat/put/edit remain composed through the existing md dispatch. Verified at the type/dispatch level and live through the standalone notion-md binary; the umbrella runtime is blocked by a pre-existing tui-react TDZ at startup (#787), unrelated to this work. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
….* attrs The span table used an nmd.* shorthand that was never implemented; align it with the actual notion_md.* attribute keys emitted by cat/put/edit (Group G), and note that result/changed/partial_write attrs are a follow-up. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…rash (#787) The standalone `notion` binary crashed at startup for every command with `ReferenceError: Cannot access 'createTuiApp' before initialization`. `runRootCli` (cli.ts) imports the three command trees concurrently via `Promise.all`, and each schema/db renderer's `app.ts` built its `*App` by calling `createTuiApp(...)` as a module-load side-effect. Under Bun's concurrent async module evaluation that top-level call reached the shared `@overeng/tui-react` graph while it was still mid-initialization, leaving a re-exported binding (createTuiApp, then its body's createInterruptedAction) in the temporal dead zone. Root cause is the module-load side-effect, not a barrel export-order bug and not a circular import: the only barrel self-imports are JSDoc, and converting `createTuiApp` to a hoisted function merely relocated the crash to the next top-level const. Fix: the five renderer `app.ts` modules now construct their app lazily via a memoized `get*App()` accessor instead of at module top level, so no `createTuiApp(...)` runs during import. Memoization preserves the previous single-instance (one registry/atom set) semantics. Confined to notion-cli; shared `@overeng/tui-react` is untouched. Regression test (src/concurrent-import.unit.test.ts) spawns the umbrella's concurrent Promise.all import path under Bun (the binary's runtime) and asserts no TDZ crash. Verified RED on pre-fix code, GREEN after. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a log-safe Notion token fingerprint so a user can tell *which* integration token is active when a gateway call fails (e.g. a secrets-run token resolving to a different integration than expected). - notion-effect-client: add `notionTokenFingerprint(token)` formatted `<scheme>…#<8hex>` where `<scheme>` is the public token-type prefix (up to and incl. the first `_`) and `<8hex>` is the first 8 hex of sha256(token). Empty token → `<none>`; no-`_` token → empty scheme. Emits zero secret bytes beyond the scheme prefix. Exported from `mod.ts`. - notion-md: `NmdGatewayError` gains a `token_fingerprint` field; the live gateway computes the fingerprint once and threads it into every error construction, appending ` [integration token <fp>]` to the message. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add decision record 0019 (canonical body is one function applied at both wire boundaries; renderer emits parseable-not-canonical Markdown; spacing/tightness policy lives only in the canonical layer; renderer joins must not be made type-aware; with the deliberate non-changes — line-ending normalize stays a sub-step, notion-core's pure normalizer stays). Add the 04-fidelity "Canonical Body Form" spec note, update the hash.ts doc (canonicalization now lives in notion-effect-client and is applied at pull receive too), and a CHANGELOG [Unreleased] entry. Re-baseline demo/showcase.nmd to the canonical shape (headings blank-separated, lists tight, enhanced-markdown tags preserved; verified idempotent). Also fix the decision number (0018 → 0019; 0018 is the staged-task-list record) and a stacked duplicate comment in live.ts. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Moving the remark/unified/unist deps from notion-md into notion-effect-client rewrote the root pnpm-lock.yaml, which is an input to two pnpm-deps fixed-output derivations (the oxc-config oxlint plugin and the notion-cli bundle). Reconcile both stale `pnpmDepsHash`/`hash` values (oxc-config via the file's documented procedure, notion-cli via the evergreen FOD-refresh workflow) so `devenv shell` and `dt check:all` build green again. Nix-side completion of the Option 2 move. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ds, redirect citations The reconciler/placeholder design (0005 inline placeholders, 0010 visible-placeholder deletion, 0011 block-level reconciliation, 0014 reconciliation-as-universal-engine, 0015 renderer-symmetric converter) is wholly superseded by decision 0016 (refuse lossy pages); the 0013 stateless in-buffer schema fingerprint by decision 0017 (ephemeral file-engine session, drift from the engine base snapshot). Delete those six `.decisions/` records (rationale lives in experiments.md) and redirect every citation to the superseding decision — as a live link where one is cited, or as historical prose naming the superseded concept. Touched: schema-snapshot.ts doc-comment (0013→0017), 01-editor / 04-fidelity / 06-data-source spec+requirements, experiments.md, README.md decision-range label, and the surviving records 0003/0008/0009/0016/0017/0019. Tighten 0016/0017 to state the supersession once; trim the "Refined by 0017" blockquotes on 0003/0008/0009 to one line. No IDs renumbered (log: 19 → 13 records). Zero links resolve to a deleted record; all internal markdown links resolve; ts:check + lint:check green; oxfmt clean. Docs/comment-only — no library surface change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ixpoint Fold in the two test-coverage items flagged as urgent for the body/Markdown consolidation (decision 0019), in canonical-markdown.test.ts: 1. Two-oracle agreement on canonical inputs. The raw-hash oracle (sha256Digest, used by classifyPlan in tree.ts) and the canon-invariant semanticEquivalent must agree for canonical inputs (raw-hash-equal <=> semanticEquivalent-true) now that both wire boundaries route through canonicalizeBlockMarkdown. The referee is neither oracle (canonicalize-then-byte-equal): a curated seed set of raw-spelling variants that converge to identical canonical bytes, a seeded fast-check property over arbitrary bodies, and an idempotence premise. Guards the failure mode a future canonicalization change could silently introduce. 2. Golden-file fixpoint over demo/showcase.nmd. Asserts the committed demo body is already canonical (canonicalizeBlockMarkdown(demoBody) === demoBody) and idempotent, locking the consolidation re-baseline and catching future drift. Test-only, no production change. Both invariants hold against real code (the demo body is canonical; the oracles agree on canonical inputs). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 425a36c4e4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
… dep move The dep-move commit (bbd358b) that shifted remark/unified/unist from notion-md into notion-effect-client rewrote pnpm-lock.yaml, which invalidates the notion-md-pnpm-deps FOD. The prior refresh commit (8f2327d) updated notion-cli and oxc-config-plugin but missed notion-md. Repaired via `evergreen fod reconcile` (rung 1: nix already printed `got:`): notion-md: sha256-2V8S6/... → sha256-hYDYbicQQZY0XUlzrJT2awHs1zv3FyqAc8HDoFY6IcY= Verified: `nix build .#notion-md-pnpm-deps .#notion-cli-pnpm-deps .#oxc-config-plugin-pnpm-deps` and `nix build .#notion-md .#notion-cli` all build clean with no FOD hash mismatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
… runtime dep, umbrella exit codes) Three findings from the chatgpt-codex-connector review of #786: - P1 (notion-effect-client/media-url.ts): canonicalizeMediaUrlsInMarkdown stripped X-Amz-*/signature/Expires params from ANY signed-looking Markdown URL by param name. Since canonicalizeBlockMarkdown now runs on both pull and push, an external private-S3 URL embedded by URL could be surfaced and then persisted with its load-bearing credentials stripped. The string path cannot see file vs external (unlike the renderer's getBlockUrl, which only touches type:"file"), so it now gates on a Notion-media host allowlist; non-Notion hosts are left untouched. canonicalizeMediaUrl stays host-agnostic. - P2 (notion-effect-client/config.ts): sha256Hex (notionTokenFingerprint) is a runtime import of @overeng/utils, but the package listed it only as a dev/peer dependency. Moved utilsPkg into the generated runtime dependencies via package.json.genie.ts + genie:run. The regen also reconciled a pre-existing stale generated package.json (leftover external peerDependencies the current genie source no longer emits) that had drifted the lockfile and broken the frozen-lockfile Nix build. - P2 (notion-cli/cli.ts): the umbrella `notion edit` alias ran under the default runMain teardown, collapsing tagged editor failures (3 lossy / 6 schema-drift / 8 abort) to exit 1 and diverging from `notion-md edit`. The umbrella now wires the same editorExitCode teardown; safe for non-editor commands (falls back to 1 for unmapped failures, 0 on success). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
All 6 pnpm-deps fixed-output derivations were stale after the pnpm-lock.yaml update (removed phantom @effect/*/@playwright deps from notion-effect-client importer + reclassified @overeng/utils). Hashes updated from build-observed `got:` values. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The pnpm-lock.yaml change in 228cc0d (utils runtime dep) invalidated the oxc-config plugin's pnpmDepsHash, but the reconcile pass in 8f4f19a only covered the computed mk-pnpm-cli/bun-deps hashes and missed this hardcoded literal. The stale FOD cascaded into every CI job that builds the devshell (typecheck, lint, test, nix-check, nix-fod-check, integration suites). Verified locally: all three exported pnpm FODs (genie/megarepo/oxc-config) cold-rebuild clean and `dt nix:check` passes. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…e install mode
notion-effect-client imports `@overeng/utils` at runtime (sha256Hex), and
utils carries peer dependencies (the @effect/* cluster + @playwright/test).
The committed manifest/lockfile lacked them, so genie validation failed
("Missing dep … peer dep of @overeng/utils") and downstream tsc could not
resolve modules — failures previously masked by the FOD hash mismatch on
PR #786.
Switch the genie composition to `mode: 'install'` so genie installs the
inherited peers explicitly (matching utils/notion-cli/tui-react). Regenerate
package.json and sync pnpm-lock. The lockfile change re-stales every
full-workspace pnpm-deps FOD, so reconcile all of them from build-observed
values: oxc-config plus the genie/megarepo/notion-cli/notion-md/tui-stories/
workflow-report CLI deps.
Verified locally against the committed tree: `dt check:quick`, `dt nix:check`,
`dt test:run`, the three exported nix-fod-check FODs, and all full package
builds pass.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…45, decisions 0018/0020) After the editor exits, `notion-md edit`'s push made ~8–10 Notion round-trips emitting only OTEL spans — the terminal was silent and read as a hang. Implement the already-specified staged write-path progress indicator (R43–R45, decision 0018) and surface the guarded push's existing remote-drift outcomes as visible stderr notes. - progress.ts: a `ProgressReporter` Context.Tag render seam. The engine emits purpose-tagged stage transitions at the existing push call sites (observe → write-body → write-title[skip if unchanged] → settle); the emit helpers use `Effect.serviceOption` (zero engine R-churn) and swallow all failures AND defects (`Effect<void, never>`), so the path is byte-identical with the reporter absent/present/hostile (R45). - `edit` wires a stderr-line renderer on the write path, gated on `process.stderr.isTTY`; non-TTY → no layer → silent (R44). The static-line rung is deliberate (decision 0018 sanctions it): `edit` returns from a full-screen editor that owned the TTY, so a mounting TaskList TUI would fight the terminal, and lines sidestep the #787 module-load TDZ. The animated TaskList Layer is the same Tag's later drop-in (zero engine re-touch). - "+ warn": reuse the guarded push's existing drift outcomes (no background poller, no extra last_edited_time pull) — auto-merge against a moved remote and exit-7 conflict each emit a visible stderr `note:`. - Live two-way watch mode (push-on-save + live upstream reflection in the editor) was investigated and rejected: the editor owns the TTY (no live feedback) and the CLI cannot trigger an `$EDITOR` reload (no live reflection) — both empirically spiked. Recorded as decision 0020. Scope: the `edit` path. `put`/file-`sync` emit through the engine but do not yet wire a render Layer. Tests pin R45 neutrality (no/capturing/ failing reporter → identical EditResult), the stage sequence, and both warn notes. notion-md 194 passed; engine function signatures unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Captures the "+ warn" behavior shipped in fd86d27 as a requirement: a write push must surface the guarded engine's existing drift outcomes (auto-merge against a moved remote; exit-7 conflict) as a visible stderr note, reusing existing detection — no background poller, no extra last_edited_time pull. The surfacing half of the single-shot edit model; live upstream reflection into the editor is rejected (decision 0020). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
What this PR delivers
Plugin-free, Unix-style
$EDITOR-based two-way Notion↔Markdown editing for@overeng/notion-md— VRS (design source of truth) and the implementation,landed group-by-group with live-Notion E2E per group. See
packages/@overeng/notion-md/docs/vrs/(decisions 0001–0017, impl-delta).Shape (decisions 0001–0017):
cat/put= stateless stdin/stdout body pipes(
cat --frontmatterread-only; no stateless property write);edit= ephemeralfile-engine session over a
$TMPDIR.nmd; refuse-lossy uniform acrosscat/put/edit/syncat the shared pull gate.Progress
Blocking prerequisite
ffb6d159.Classifier flags every not-round-trip-safe body block (
child_database/toc/synced_block/child_page-in-body/bookmark/embed/…); uniform pull-gate refusal(exit 3);
child_pagetree-tolerance so multi-page tree sync still works.Live-E2E: lossy pages now refused at pull instead of silently destroyed.
Editor surface
49c0d2de. Sharedmedia-url.tsutil applied in the renderer (hash path) andcanonicalizeBlockMarkdown/semanticEquivalent(gate path); external URLsuntouched. Live-E2E: hosted-media body hash stable across pulls; no-op push not
gate-rejected.
cat/put/edit(R30–R39) —e0f8e70e. Three commands +title↔H1 splice + exit-code map + spans;
edit= ephemeral file-engine session(mktemp → pull → splice →
$EDITOR→syncPage→ conflict relocation →cleanup). 154 notion-md tests; live-E2E: cat→put fixpoint, guarded conflict
(exit 7), edit round-trip via real
$EDITOR, abort (exit 8), no-op, lossyrefusal (exit 3).
--frontmatterschema-drift via the engineschema_snapshot(exit 6, R14) —23dc68af. Writableschema_snapshotcaptured at pull (in the sidecardata_sourcebinding), compared at push before any property write; structural drift refuses with exit 6 (not--force-able). 170 notion-md tests; live-E2E: a live select-option add refused the property write, value not written.b38e6ffe. Span-assertion suite forcat/put/edit(attributes + the R24 leak-guard: no tokens/bodies/signed URLs in any span);editconflict-relocation test (the Group-A-deferred gap). notion-md 174 tests. Spec span table reconciled to the realnotion_md.*attrs (6ae91231).Umbrella (notion-cli)
notion md cat|put|editdispatch + top-levelnotion edit <page>alias wired (sharedmakeEditCommandfactory → sameeditsession) —b38e6ffe. notion-cli 52 tests; verified viats:check+ the standalonenotion-mdbinary live. Caveat: the standalonenotionumbrella binary can't run end-to-end yet — a pre-existing TDZ crash (createTuiAppbefore init, concurrentDiffOutputimport) blocks all umbrella commands; filed as notion umbrella runtime crashes: TDZ 'Cannot access createTuiApp before initialization' #787. The alias is correct at dispatch level; runtime verification of the umbrella binary is gated on notion umbrella runtime crashes: TDZ 'Cannot access createTuiApp before initialization' #787.Integration verified
Combined C+B+A tree:
ts:check+lint:checkclean;notion-md154/154,notion-effect-client134/134, classifier 25/25; live-E2E green per group.Follow-on fixes landed on this branch
26e6442a) — umbrellanotionTDZ crash: renderer TUI apps built lazily (root cause = module-load side-effect under Bun's concurrent eval). Bun-level regression test. Umbrellanotion editruntime is now unblocked. (Investigating whether it's also an upstream Bun bug → minimal repro + oven-sh/bun issue + Notion blocker in flight.)7b3fc37c) — gateway errors now show[integration token ntn_…#<8 hex>](zero secret-byte leak) so you can tell which credential is active.01-editor…06-data-source) + sync-progress (staged TaskList) coverage — in flight.Body/Markdown consolidation + VRS cleanup (folded in)
canonicalizeBlockMarkdown(moved intonotion-effect-clientbeside the renderer + media-URL canon) forces tight lists (spread:false) and is routed through pull and push, socat/edit/disk/hash/push see identical bytes. Livepull→put→pullfixpoint passes (Case B/notion-md: tree sync non-idempotent on emphasis-marker style and paragraph-after-list (perpetual update) #756 residual left tracked, not over-claimed).markdownToBlocksconverter, deduped divergentstripChildAnchors, removed dead endpoint fallback, re-baselineddemo/showcase.nmd.01-editor…06-data-source) +03-sync-enginerebalance; requirements lifted to the right altitude.Links
🤖 Generated with Claude Code
Posted on behalf of @schickling
agent_nameagent_session_idagent_toolagent_tool_versionagent_runtimeagent_modelruntime_profileskills_manifestworktreemachinetooling_profile