Skip to content

notion-md: $EDITOR-based editing — VRS + implementation epic#786

Merged
schickling merged 37 commits into
mainfrom
schickling/2026-06-14-notion-md-vim
Jun 16, 2026
Merged

notion-md: $EDITOR-based editing — VRS + implementation epic#786
schickling merged 37 commits into
mainfrom
schickling/2026-06-14-notion-md-vim

Conversation

@schickling-assistant

@schickling-assistant schickling-assistant commented Jun 15, 2026

Copy link
Copy Markdown
Collaborator

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 --frontmatter read-only; no stateless property write); edit = ephemeral
file-engine session over a $TMPDIR .nmd; refuse-lossy uniform across
cat/put/edit/sync at the shared pull gate.

Progress

Blocking prerequisite

Editor surface

  • B — Hosted-media URL canonicalization (R36)49c0d2de. Shared
    media-url.ts util applied in the renderer (hash path) and
    canonicalizeBlockMarkdown/semanticEquivalent (gate path); external URLs
    untouched. Live-E2E: hosted-media body hash stable across pulls; no-op push not
    gate-rejected.
  • A — cat / put / edit (R30–R39)e0f8e70e. Three commands +
    title↔H1 splice + exit-code map + spans; edit = ephemeral file-engine session
    (mktemp → pull → splice → $EDITORsyncPage → 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, lossy
    refusal (exit 3).
  • F — --frontmatter schema-drift via the engine schema_snapshot (exit 6, R14) — 23dc68af. Writable schema_snapshot captured at pull (in the sidecar data_source binding), 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.
  • G — observability + coverageb38e6ffe. Span-assertion suite for cat/put/edit (attributes + the R24 leak-guard: no tokens/bodies/signed URLs in any span); edit conflict-relocation test (the Group-A-deferred gap). notion-md 174 tests. Spec span table reconciled to the real notion_md.* attrs (6ae91231).

Umbrella (notion-cli)

Integration verified

Combined C+B+A tree: ts:check + lint:check clean; notion-md 154/154,
notion-effect-client 134/134, classifier 25/25; live-E2E green per group.

Follow-on fixes landed on this branch

Body/Markdown consolidation + VRS cleanup (folded in)

Links

🤖 Generated with Claude Code

Posted on behalf of @schickling
field value
agent_name 🏔️ cl1-ridge
agent_session_id dd0a9ae0-bba8-43ad-8c01-449bd0bdca58
agent_tool Claude Code
agent_tool_version 2.1.165
agent_runtime Claude Code 2.1.165
agent_model claude-opus-4-8
runtime_profile /nix/store/4p78dsfk8riqcgk91zzjlgraibyf1hd4-coding-agent-runtime-profile/share/coding-agents/profile.json
skills_manifest /nix/store/cly1vi1qvxfyk37gmdk4qw4j33zrfpny-agent-skills-corpus/share/agent-skills/manifest.json
worktree effect-utils/schickling/2026-06-14-notion-md-vim
machine dev3
tooling_profile dotfiles@e7e383b

…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>
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

Storybook Previews

No storybooks were deployed.

github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
@github-actions

github-actions Bot commented Jun 15, 2026

Copy link
Copy Markdown

CI Measurements

partial - advisory gate - readiness partial (17/26 enabled observations gateable) - commit b9b23a5 - protocol devenv-perf-warm-median-v2

No regressions. Comparable movement is below the semantic impact threshold; neutral rows are collapsed below.

Measurement change vs baseline chart

SVG source

nix / closures / packages

What changed? Group Probe Baseline -> current Raw change Impact Confidence
unchanged nix / closures / packages Nix sources closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Nix sources closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Nix sources closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Node / pnpm closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Node / pnpm closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Node / pnpm closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Rust closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Rust closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
unchanged nix / closures / packages Rust closure size 0 B -> 0 B +0 B / n/a n/a unknown, baseline n=18, current n=1
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

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
devenv / devenv cli devenv tasks list 0.04 s -> 0.063 s +0.023 s / 57.5% 0.00x yes paired n=9, 25-75% delta 0.001 s..0.025 s Too small to matter
devenv / devenv cli devenv processes --help 0.02 s -> 0.02 s +0 s / 0% 0.00x yes paired n=9, 25-75% delta 0 s..0.002 s Too small to matter

devenv / devenv shell

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
devenv / devenv shell Warm shell eval 2.219 s -> 2.236 s +0.017 s / 0.8% 0.00x yes paired n=5, 25-75% delta -0.081 s..0.25 s Too small to matter

devenv / genie

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
devenv / genie Genie check direct 5.46 s -> 5.401 s -0.059 s / -1.1% 0.00x yes paired n=5, 25-75% delta -0.072 s..0.009 s Too small to matter
devenv / genie Genie run task 1.256 s -> 1.236 s -0.02 s / -1.6% 0.00x yes paired n=5, 25-75% delta 0.004 s..0.089 s Too small to matter

devenv / quality gates

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
devenv / quality gates Forced check:quick 11.547 s -> 11.592 s +0.045 s / 0.4% 0.00x yes paired n=3, 25-75% delta -0.106 s..0.045 s Too small to matter
devenv / quality gates Warm cached check:quick 3.019 s -> 3.018 s -0.001 s / 0% 0.00x yes paired n=5, 25-75% delta -0.06 s..-0.001 s Too small to matter

devenv / workspace setup

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
devenv / workspace setup pnpm install task 0.643 s -> 0.626 s -0.017 s / -2.6% 0.00x yes paired n=5, 25-75% delta -0.06 s..-0.014 s Too small to matter

nix / closures / packages

Group Probe Baseline -> current Raw change Impact Gate Evidence Why hidden
nix / closures / packages Total closure size 535.6 MiB -> 535.9 MiB +291.9 KiB / 0.1% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
nix / closures / packages Total closure size 144.4 MiB -> 144.4 MiB +60 KiB / 0% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
nix / closures / packages Total serialized NAR size 144.4 MiB -> 144.4 MiB +60 KiB / 0% 0.00x yes within_budget, baseline n=17, current n=1 Slightly higher, ok
nix / closures / packages Total serialized NAR size 535.8 MiB -> 535.9 MiB +48 KiB / 0% 0.00x yes within_budget, baseline n=17, current n=1 Slightly higher, ok
nix / closures / packages Total closure size 156.3 MiB -> 156.3 MiB +9.8 KiB / 0% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
nix / closures / packages Total serialized NAR size 156.3 MiB -> 156.3 MiB +9.8 KiB / 0% 0.00x yes within_budget, baseline n=17, current n=1 Slightly higher, ok
nix / closures / packages Total closure path count 79 count -> 79 count +0 count / 0% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
nix / closures / packages Total closure path count 5 count -> 5 count +0 count / 0% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
nix / closures / packages Total closure path count 8 count -> 8 count +0 count / 0% 0.00x yes noise_floor, baseline n=18, current n=1 Too small to matter
Diagnostic / ungated measurements (7)

source / effect-utils

Group Probe Current Baseline Impact Gate Reason Evidence
source / effect-utils Genie runtime lines 20266 lines 19809 lines diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=67
source / effect-utils Genie CI workflow helpers lines 7418 lines 7318 lines diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=9
source / effect-utils Genie runtime files 67 count 66 count diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=67
source / effect-utils Genie CI workflow helpers files 9 count 9 count diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=9
source / effect-utils Nix workspace tools files 14 count 14 count diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=14
source / effect-utils Nix workspace tools lines 3632 lines 3632 lines diagnostic disabled Diagnostic only diagnostic, baseline n=19, current n=14

devenv / devenv shell

Group Probe Current Baseline Impact Gate Reason Evidence
devenv / devenv shell Shell eval with OTEL trace 132.693 s n/a n/a missing_baseline No baseline yet missing_baseline, baseline n=0, current n=1
All measurements
Status Gate Target Observation Dimensions Baseline Current Delta Ratio Impact
pass yes Genie package Total closure size bucket=total 535.6 MiB 535.9 MiB +291.9 KiB 0.1% 0.00x
pass yes Megarepo package Total closure size bucket=total 144.4 MiB 144.4 MiB +60 KiB 0% 0.00x
pass yes Megarepo package Total serialized NAR size bucket=total
sizeKind=nar
144.4 MiB 144.4 MiB +60 KiB 0% 0.00x
pass yes Genie package Total serialized NAR size bucket=total
sizeKind=nar
535.8 MiB 535.9 MiB +48 KiB 0% 0.00x
pass yes oxlint npm package Total closure size bucket=total 156.3 MiB 156.3 MiB +9.8 KiB 0% 0.00x
pass yes oxlint npm package Total serialized NAR size bucket=total
sizeKind=nar
156.3 MiB 156.3 MiB +9.8 KiB 0% 0.00x
pass disabled effect-utils repository Genie runtime lines scope=genie_runtime 19809 lines 20266 lines +457 lines 2.3% diagnostic
pass disabled effect-utils repository Genie CI workflow helpers lines scope=genie_ci_workflow 7318 lines 7418 lines +100 lines 1.4% diagnostic
pass disabled effect-utils repository Genie runtime files scope=genie_runtime 66 count 67 count +1 count 1.5% diagnostic
pass yes Dev shell Genie check direct aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=5
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=5
phase=warm
probe=genie_check_direct
probeLabel=Genie check direct
sampleCount=11
status=0
warmupCount=1
5.46 s 5.401 s -0.059 s -1.1% 0.00x
pass yes Dev shell Forced check:quick aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=3
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=3
phase=warm
probe=task_check_quick_forced
probeLabel=Forced check:quick
sampleCount=6
status=0
taskCacheMode=refresh
warmupCount=0
workload=forced-task-cache
11.547 s 11.592 s +0.045 s 0.4% 0.00x
pass yes Dev shell devenv tasks list aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=9
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=9
phase=warm
probe=tasks_list
probeLabel=devenv tasks list
sampleCount=19
status=0
warmupCount=1
0.04 s 0.063 s +0.023 s 57.5% 0.00x
pass yes Dev shell Genie run task aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=5
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=5
phase=warm
probe=task_genie_run
probeLabel=Genie run task
sampleCount=11
status=0
warmupCount=1
1.256 s 1.236 s -0.02 s -1.6% 0.00x
pass yes Dev shell Warm shell eval aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=5
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=5
phase=warm
probe=shell_eval_warm
probeLabel=Warm shell eval
sampleCount=11
status=0
warmupCount=1
2.219 s 2.236 s +0.017 s 0.8% 0.00x
pass yes Dev shell pnpm install task aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=5
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=5
phase=warm
probe=task_pnpm_install
probeLabel=pnpm install task
sampleCount=11
status=0
warmupCount=1
0.643 s 0.626 s -0.017 s -2.6% 0.00x
pass yes Dev shell Warm cached check:quick aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=5
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=5
phase=warm
probe=task_check_quick_warm
probeLabel=Warm cached check:quick
sampleCount=11
status=0
taskCacheMode=warm
warmupCount=1
workload=cached-no-op
3.019 s 3.018 s -0.001 s 0% 0.00x
pass yes Dev shell devenv processes --help aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=9
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=9
phase=warm
probe=processes_help
probeLabel=devenv processes --help
sampleCount=19
status=0
warmupCount=1
0.02 s 0.02 s +0 s 0% 0.00x
pass disabled effect-utils repository Genie CI workflow helpers files scope=genie_ci_workflow 9 count 9 count +0 count 0% diagnostic
unknown missing_baseline Genie package Nix sources closure size bucket=nix-sources 0 B 0 B +0 B n/a n/a
unknown missing_baseline Megarepo package Nix sources closure size bucket=nix-sources 0 B 0 B +0 B n/a n/a
unknown missing_baseline oxlint npm package Nix sources closure size bucket=nix-sources 0 B 0 B +0 B n/a n/a
pass disabled effect-utils repository Nix workspace tools files scope=nix_workspace_tools 14 count 14 count +0 count 0% diagnostic
pass disabled effect-utils repository Nix workspace tools lines scope=nix_workspace_tools 3632 lines 3632 lines +0 lines 0% diagnostic
unknown missing_baseline Genie package Node / pnpm closure size bucket=node 0 B 0 B +0 B n/a n/a
unknown missing_baseline Megarepo package Node / pnpm closure size bucket=node 0 B 0 B +0 B n/a n/a
unknown missing_baseline oxlint npm package Node / pnpm closure size bucket=node 0 B 0 B +0 B n/a n/a
unknown missing_baseline Genie package Rust closure size bucket=rust 0 B 0 B +0 B n/a n/a
unknown missing_baseline Megarepo package Rust closure size bucket=rust 0 B 0 B +0 B n/a n/a
unknown missing_baseline oxlint npm package Rust closure size bucket=rust 0 B 0 B +0 B n/a n/a
pass yes Genie package Total closure path count bucket=total 79 count 79 count +0 count 0% 0.00x
pass yes Megarepo package Total closure path count bucket=total 5 count 5 count +0 count 0% 0.00x
pass yes oxlint npm package Total closure path count bucket=total 8 count 8 count +0 count 0% 0.00x
missing_baseline missing_baseline Dev shell Shell eval with OTEL trace aggregation=median
devenvRev=2cf62a010000b70f15c78a72761fad7c9e6fb47a
measuredSampleCount=1
measurementProtocol=devenv-perf-warm-median-v2
otelServiceName=devenv-perf-ci
pairedOrderProtocol=balanced-seeded-alternating-v1
pairedOrderSeed=27650110831-1-2adf92f302c43cfbd1495a25200896dbed94ea07
pairedSampleCount=1
phase=warm
probe=shell_eval_traced
probeLabel=Shell eval with OTEL trace
sampleCount=2
status=0
warmupCount=0
n/a 132.693 s n/a n/a n/a
Previous runs
Commit Status Gate Top changes
26d6b97 partial advisory Slightly higher, ok Dev shell Genie check direct +0.16 s / 3%
Too small to matter Genie package Total closure size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
13430b1 partial advisory Slightly lower, ok Dev shell Forced check:quick -0.944 s / -6.5%
Too small to matter Dev shell Genie check direct -0.039 s / -0.6%
Too small to matter Genie package Total closure size +291.9 KiB / 0.1%
f0c12df partial advisory Slightly lower, ok Dev shell Genie check direct -0.204 s / -3%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
3bea5ec partial advisory Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
c267b69 partial advisory Slightly higher, ok Dev shell Genie check direct +0.271 s / 4.9%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
d1e03eb partial advisory Too small to matter Dev shell Warm cached check:quick -0.067 s / -2%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
39fba1a partial advisory Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
7b3fc37 partial advisory Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
6ae9123 partial advisory Slightly lower, ok Dev shell Warm cached check:quick -0.167 s / -4.7%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
23dc68a partial advisory Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
e0f8e70 partial advisory Slightly lower, ok Dev shell Genie check direct -0.168 s / -3.1%
Slightly lower, ok Dev shell Warm cached check:quick -0.153 s / -4.7%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
49c0d2d partial advisory Too small to matter Dev shell Warm cached check:quick -0.08 s / -2.3%
Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
44e864a partial advisory Too small to matter Genie package Total closure size +535.7 KiB / 0.1%
Slightly higher, ok Genie package Total serialized NAR size +291.9 KiB / 0.1%
Too small to matter Megarepo package Total closure size +60 KiB / 0%
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"
      }
    }
  ]
}

github-actions Bot added a commit that referenced this pull request Jun 15, 2026
schickling-assistant and others added 2 commits June 15, 2026 21:45
#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>
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
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>
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
…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>
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
github-actions Bot added a commit that referenced this pull request Jun 15, 2026
schickling-assistant and others added 2 commits June 15, 2026 23:47
… 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>
schickling-assistant and others added 2 commits June 16, 2026 09:09
…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>
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
schickling-assistant and others added 4 commits June 16, 2026 11:42
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>

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 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".

Comment thread packages/@overeng/notion-effect-client/src/media-url.ts
Comment thread packages/@overeng/notion-effect-client/src/config.ts
Comment thread packages/@overeng/notion-cli/src/cli.ts
schickling-assistant and others added 5 commits June 16, 2026 14:16
… 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>
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
…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>
@schickling schickling enabled auto-merge (squash) June 16, 2026 21:18
schickling-assistant and others added 2 commits June 16, 2026 23:19
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>
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
@schickling schickling merged commit fc87f14 into main Jun 16, 2026
19 checks passed
@schickling schickling deleted the schickling/2026-06-14-notion-md-vim branch June 16, 2026 21:51
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
github-actions Bot added a commit that referenced this pull request Jun 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

notion-md: body-fidelity classifier misses renderable-but-lossy blocks → silent data loss on push

2 participants