Skip to content

Vocabulary proposal: rename ported_elsewhere → ported_differently #21

Description

@stevevanhooser

Cross-repo proposal. The bridge status vocabulary is shared by NDI-python, NDR-python and DID-python, and was settled in NDI-python (#208, #209, #210). Filing here because that is where I have write access; this needs to be carried to NDI-python as the vocabulary's home repo before it can be acted on.

Timing: worth deciding before NDI-python #210 merges — that PR carries ~98 entries using the current name.

The proposal

Rename ported_elsewhere to ported_differently. No change to its meaning, to its required python_path, or to the other three values (porting_deferred, matlab_only, retired).

The argument for

"Elsewhere" names a place — but python_path already answers where. "Differently" names the manner, which is the part a reader cannot recover from any other field on the entry.

Checked against real data: of NDR-python's nine ported_elsewhere entries (merged in #19), only three are actually "elsewhere".

Entry What actually happened "elsewhere"?
+ced/+sonpipe/runcmd.m renamed/moved into _invoke.py yes
+sonpipe/private/invoke_binary.m folded into _invoke.py yes
+sonpipe/private/invoke_text.m folded into _invoke.py yes
+omezarr/private/decompressBlosc.m numcodecs.Blosc does it no — different mechanism
+omezarr/private/decompressZstd.m numcodecs.Zstd does it no
+omezarr/private/parseDType.m zarr resolves dtypes internally no
+omezarr/private/readChunk.m zarr.open() assembles chunks no
+omezarr/private/readZArrayMeta.m read off the opened zarr.Array no
+ndr/+reader/imagestack.m four native readers cover it no — different design

Six of nine involve nothing moving anywhere. Python simply does the job a different way, usually because a dependency does it. Calling that "ported elsewhere" sends a reader looking for a relocated file that does not exist.

The suggested full phrasing was "ported_differently (for a reason)". The second half is already enforced — every entry carrying a status must have a decision_log.

The argument against

  • ported_elsewhere is already merged in NDI-python #208/#209 and in flight in #210 (~98 entries); DID-python's parallel PR uses it; NDR-python has 9 live entries.
  • The point of the recent reconciliation was to stop these three repos drifting apart. A rename must land in all three or none — a partial rename is strictly worse than either name.
  • For the three CED entries, "elsewhere" is genuinely the more accurate word. One label has to cover both shapes either way.

Cost, if adopted

Mechanically small and safe per repo:

  1. the status: values on affected entries;
  2. the status_vocabulary key in docs/developer_notes/ndr_matlab_python_bridge.yaml (§ 6);
  3. the ALLOWED_STATUSES constant in tests/test_matlab_bridge_conventions.py;
  4. add ported_elsewhere to REPLACED_STATUSES so it gets a targeted error rather than a bare "not in the vocabulary".

TestTheVocabularyIsTheDocumentedOne::test_spec_and_enforcement_agree fails loudly if only some of those are done, so a half-finished rename cannot merge here. NDI-python #210 has the equivalent check.

Rough sizes: NDI-python ~98 entries, NDR-python 9, DID-python TBD.

Decision needed

@stevevanhooser — one decision for all three repos, not per-repo:

  1. Rename everywhere — do it in NDI-python #210 before it merges, then NDR-python and DID-python follow.
  2. Keep ported_elsewhere — read "elsewhere" loosely as "not under the mirrored name" and let the decision_log say how.
  3. Rename later — merge #210 as-is, then a coordinated rename across all three as its own PR per repo.

NDR-python will follow whatever is decided. If the answer is (1) or (3), our side is a ~15-line change and I can do it on request.

Activity

  1. stevevanhooser commented on Sep 7, 2026

    @stevevanhooser
    ContributorAuthor

    DID-python's side is done — landed in VH-Lab/DID-python#67, commit f372533. All 7 CI checks green. Recording it here so the three repos can be kept in step; NDI-python and NDR-python still carry ported_elsewhere.

    Scope was one entry

    The issue estimated DID-python as "TBD". The answer is 1 live entry, not because the repo is small but because it only grew a status field earlier the same day, in the first commit of that same PR. The rename was applied before it merged, so ported_elsewhere never reaches main there.

    The one entry is sqldb, and it argues the issue's case rather than complicating it: MATLAB has an intermediate abstract class did.implementations.sqldb between database and sqlitedb. Python has no such layer at all — the behaviour is folded into Database and SQLiteDB. Nothing moved anywhere, so "elsewhere" would send a reader hunting a relocated file that does not exist. That is the same shape as six of NDR-python's nine.

    All four places from the cost estimate moved in one commit: the entry, STATUSES, the normative table in PORTING_INSTRUCTIONS.md, and REPLACED_STATUSES.

    One decision to mirror (or to reject) in the other two

    Item 4 asked for ported_elsewhere in REPLACED_STATUSES so it gets a targeted error. DID-python's carries five names, not one:

    Retired Message says to write
    ported_elsewhere ported_differently
    not_yet_ported porting_deferred
    not_applicable matlab_only / porting_deferred / retired — pick the one you mean
    implemented (no status — ported is the default)
    does_not_exist retired

    The last four were retired in NDI-python and were never written in DID-python. They are rejected there anyway, by name, for the reason this issue exists: the vocabulary is shared, the three repos drifted apart once already, and someone arriving from another one should be told the current name rather than merely told no. not_applicable was DID-python's own key name until this week, and gets no single replacement because it had none.

    Whether NDI-python and NDR-python want the same five is a judgement call, not something the issue settled — flagging it so all three end up alike rather than each picking its own set.

    Concretely actionable for the NDR side

    AGENTS.md step 2 still instructs contributors to write status: not_yet_ported or not_applicable — both retired in NDI-python. That is upstream of this rename and worth fixing in the same pass, otherwise the repo's own instructions will keep producing values its checker rejects.

    Guard against the half-finished rename

    The issue notes that a partial rename is strictly worse than either name. DID-python binds both tables to the constants they document — the vocabulary table to STATUSES, and a new "Retired status names" table to REPLACED_STATUSES — and asserts no name is both current and retired. Mutation-tested: reverting the entry, and editing either table without the matching constant, each fail loudly. Equivalent in spirit to TestTheVocabularyIsTheDocumentedOne::test_spec_and_enforcement_agree.


    Generated by Claude Code

  2. stevevanhooser commented on Sep 7, 2026

    @stevevanhooser
    ContributorAuthor

    Status: decided, and NDR-python's side is done. The other two repos are still in progress.

    Recording this before #22 merges, because merging will auto-close this issue and the closure would otherwise read as "the cross-repo vocabulary question is settled" when only one of the three repos has actually landed.

    Repo Entries Status
    NDR-python 9 ✅ done — #22, all 7 checks green, awaiting merge
    NDI-python ~98 🔄 in progress
    DID-python TBD 🔄 in progress

    Option 1 from the decision list was taken: rename everywhere. NDR-python's change is 6 files — the 9 status: values, the status_vocabulary key in the spec (§ 6, with the definition rewritten to lead with manner and to say outright that python_path answers where), and ALLOWED_STATUSES / STATUSES_REQUIRING_PYTHON_PATH in the guard test.

    ported_elsewhere was retired, not merely deleted — it is in REPLACED_STATUSES and in the spec's retired-values block, so the old spelling fails with a message naming its replacement:

    use ported_differently -- the label names the manner, not the location,
    since python_path already answers where
    

    That matters while the three repos are out of step: an entry pasted from a pre-rename branch, or from a sibling repo that has not landed its rename yet, gets a pointer instead of a bare "not in the vocabulary".

    What is still outstanding after this closes

    1. NDI-python and DID-python have not landed. A partial rename across the three is worse than either name. Merge order does not matter; a repo left behind does.
    2. NDI-python never received this proposal as an issue of its own. It is the vocabulary's home repo, but it is outside the scope of the session that filed this, so this issue was raised here instead. If the rename needs a record there, it needs a human to carry it across.
    3. If NDI-python decides against the rename, revert NDR-python's Bridge contract: status vocabulary + file-scoped drift rule (NDI-python#211) #22 rather than leaving the repos diverged.

    None of the three blocks merging #22 — they are the reason this closure should not be read as the whole question being answered.


    Generated by Claude Code

  3. stevevanhooser commented on Sep 7, 2026

    @stevevanhooser
    ContributorAuthor

    Correction to my previous comment — one of its two "actionable for the NDR side" points was wrong, and I'd rather retract it than leave stale advice for whoever picks this up.

    I wrote that AGENTS.md step 2 "still instructs contributors to write status: not_yet_ported or not_applicable". That was read from a checkout taken before #19 merged. On current main, step 2 names no values at all:

    If it won't be ported 1:1, still add an entry with a status and a decision_log explaining why

    So there is nothing to fix, and no contradiction between NDR-python's instructions and its checker. #19 also already added REPLACED_STATUSES to tests/test_matlab_bridge_conventions.py — the same mechanism I described as worth adding. Both points were already done before I wrote them.

    The rest of that comment stands: DID-python's side is landed in DID-python#67, and its scope was one entry.

    Two things that survive the correction

    1. Naming no values in AGENTS.md is the better pattern, and it makes the pending rename cheaper here. Instructions that name specific status values have to be updated in lockstep with the vocabulary, or the repo ends up documenting a value its own CI rejects. NDR-python's step 2 is now immune to that by construction — the ported_elsewhere → ported_differently rename touches no instruction text at all. Worth preserving when #19's successor lands.

    2. NDR-python retires ported; DID-python did not, and now does. REPLACED_STATUSES here maps "ported" to "no status at all (a plain port is the default)". DID-python already refused an explicit status: ported — the value is deliberately absent from its vocabulary tuple — but via the generic "unknown status" branch, which lists the four valid values and leaves the reader to work out that what they wrote is the default and belongs nowhere.

    Fixed in DID-python#67 (f244f2f), matching NDR-python's wording. Noting it because it is the one place the two sets differed, and because the reasoning is easy to lose: there is no replacement name for ported, and that is the point. The default is spelled by the absence of a status plus a python_path. If it were sometimes written out, a missing status would stop meaning "ported" and start meaning "ported, or nobody filled this in" — which is the ambiguity the field was introduced to remove. So the error says to write nothing rather than naming a substitute.

    That leaves DID-python and NDR-python with the same five retired names. NDI-python is the one to check — if #210 does not retire ported too, the three will differ on exactly this.


    Generated by Claude Code

  4. stevevanhooser commented on Sep 7, 2026

    @stevevanhooser
    ContributorAuthor

    Amendment: write regular_port explicitly instead of leaving a plain port blank

    Requested by @stevevanhooser and now implemented in #22 (commit bb54b63), alongside the ported_differently rename.

    The change. A 1:1 port is written status: regular_port rather than carrying no status at all. The original design made the ordinary case implicit on the grounds that there should be exactly one way to say it — but that optimised for the wrong reader. These YAML files are read by people far more often than by the test suite, and an absent field does not announce what it means: someone looking at an entry with no status cannot tell "this is a normal port" from "nobody filled this in". One line removes the guess.

    The vocabulary is now five values, with regular_port listed first since it is the common case:

    status count in NDR-python
    regular_port 105
    ported_differently 9
    matlab_only 4
    porting_deferred 2
    retired 0

    All 120 entries now carry a status; none is left to inference. status: regular_port sits directly under matlab_path, so a reader sees the MATLAB file and then immediately what became of it.

    Rules that changed as a consequence

    1. The old rule inverted. test_a_plain_port_is_not_spelled_out forbade saying the ordinary thing. test_every_entry_states_its_status now requires it — an entry with a matlab_path and no status fails.
    2. ported and implemented were retired as "omit status instead". They now map to regular_port, and the spec records that an absent status no longer means "regular port" — it means the entry is incomplete.
    3. regular_port requires a python_path, same as ported_differently: a port that names no module is not a recorded port.

    The one thing worth flagging for NDI-python and DID-python

    Every status-carrying entry previously owed a decision_log. Writing regular_port onto 105 entries would therefore have demanded 105 boilerplate justifications — and NDI-python has ~98 more.

    So regular_port is exempt from the decision_log requirement: mirroring MATLAB is what the port does by default, there is no decision to record, and forcing one would only devalue the logs that carry real reasoning. Every other status still owes a reason, which is the case that check exists for.

    If NDI-python and DID-python adopt this amendment, they will hit the same wall — worth handling deliberately rather than discovering it as ~98 red tests. In NDR-python the exemption set is defined once, beside the check that enforces it, and a test pins it to what the spec tells a reader, so it cannot quietly widen to a status that does owe an explanation.

    Verification

    172 bridge tests pass against a full NDR-matlab clone; 561 passed / 15 skipped / 24 xfailed on the default suite; black and ruff clean. Fault-injected six ways: an omitted status fails; a regular_port with no python_path fails; ported is redirected to regular_port; a divergent status with no decision_log still fails; widening the exemption in code fails against the spec; renaming the spec key alone still fails.

    One defect in the change was caught by that injection and fixed before pushing — the "says where" failure message hardcoded ported_differently and would have misreported a regular_port offender. It now names each entry's actual status.


    Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions