Skip to content

Record the language service as permanently second-language-only - #2

Merged
samuelduchesne merged 1 commit into
mainfrom
005-idf-language-service
Sep 4, 2026
Merged

Record the language service as permanently second-language-only#2
samuelduchesne merged 1 commit into
mainfrom
005-idf-language-service

Conversation

@samuelduchesne

Copy link
Copy Markdown
Contributor

Adds the idf-language-service capability to the parity ledger and the names it introduces to the naming register, so that the gate accepts the surface idfkit-js is about to publish.

This lands first and alone, and the sequencing is a constitutional rule rather than a preference. check-naming-register.mjs reads the .d.ts files each package's exports map points at, so the change that adds scanIdf to @idfkit/core and publishes @idfkit/language is the change that puts every new name in scope. Landing the code before this means landing it red.

The ledger entry

tier = "never", python = "absent", typescript = "complete", absence_kind = "never", no tracked item. browser-simulation is the standing precedent for a capability complete in one language, absent in the other, and carrying no corpus cases.

The reason is on the entry. The answers are computed from byte offsets into the source text, and a second implementation of that arithmetic is the drift surface the corpus is least able to police: it compares findings on (code, line, typeName) and never on a column, so two implementations could disagree about a position for a long time with no gate noticing. The editor extension needs no port either, because its existing Python server keeps serving Python source while a second server serves IDF text.

never is terminal, and that is the point. Moving off it needs a constitutional amendment rather than an edit, which is the strength at which this decision was made. A not-yet was considered and rejected: the gate requires a real tracking issue for every not-yet, and opening one for a port nobody intends in order to satisfy a gate is exactly what the file's own header warns against.

The register entries

One [[entry]] per name, python = "", kind = "excluded", mirroring the existing eppy entry. @idfkit/language is added to [register] governs, which today reads ["idfkit", "@idfkit/core", "@idfkit/weather"], or the gate would not know to hold the new package's surface against anything.

Also here, and not a name this feature introduces: column in a finding is registered as divergent. Python string indices are code points and JavaScript's are UTF-16 code units, so the two agree below the astral planes and differ above them, which in practice means an emoji in a comment. Harmless today because a column is never compared, and registered because an unregistered divergence is indistinguishable from drift.

Two deviations from the contract, both deliberate

The names list carries 25 concepts, not the 7 contracts/governance-entries.md gives. check-parity-ledger.mjs raises E-UNREGISTERED for any name a scanned package exports that no capability claims. @idfkit/core exports ten names for this feature, so eight of them would have tripped it the moment the layer landed, and no later task adds them. The contract is what is out of date here, not the file.

position was added after the fact. The contract's "position findings" concept binds to findingsIn only, and position is exported separately so a consumer holding findings from its own run can attach regions without a second parse. The naming gate caught it at 135 of 136; it is now 136 of 136.

ProsePool is registered too. @idfkit/core declares it but does not re-export it, and explainAt cannot name its prose parameter without that export.

The corpus level does not move

conformance-2026.8 stays as it is. A capability that exists in one language asserts no cross-language agreement, so there is nothing for the corpus to compare and no case is added. The two tag series are separate precisely so a naming change does not force a corpus release.

Verification

node scripts/check-naming-register.mjs  ->  exit 0, PASS: 136 of 136 public names resolve
node scripts/check-parity-ledger.mjs    ->  exit 0, PASS: the ledger and the exported capability set agree

idf-language-service does not appear in the "no statically checkable TypeScript name" list, which is what SC-014 asked to confirm: a capability with zero corpus cases does not read as an unproven claim.

Before merge, and after

This needs the CODEOWNERS review from a maintainer of the Python side.

governance-2026.11 is deliberately not pushed with this branch. The tag must be cut from main after this merges, per governance/README.md; a tag cut from a branch points at a commit that changes under a squash-merge. Two repositories already pin governance-2026.11 and their CI cannot resolve it until that tag exists on main, so the order is: merge this, cut and push the tag, then the other two PRs go green.

`idf-language-service` joins the ledger at tier `never`, absent in the first
language with `absence_kind = "never"`, complete in the second. The note says
why: the answers are computed from byte offsets into the source text, and a
second implementation of that arithmetic is the drift surface the corpus is
least able to police, since it compares findings on (code, line, typeName) and
never on a column. It also says what the absence costs a reader, which is that
these answers need a JavaScript runtime.

`not-yet` was considered and rejected. That state requires a real tracking
issue, and opening one for a port nobody intends would pass the gate and
dead-link the reader. `never` is terminal, which is exactly the guarantee
wanted: a counterpart cannot be added without amending the constitution first.

Twenty-five names are registered ahead of their implementation, one entry each
rather than one entry for the surface. The eppy and local-simulation exclusions
are quotations from one ecosystem; these are names either language could have
spelled, so each is written down and each blocks its own counterpart.

The `column` divergence is registered too. It is not a name this change
introduces, and that is the point: two names that already look identical mean
different things, code points in one language and UTF-16 code units in the
other, and an unregistered divergence is indistinguishable from drift.

`governs` gains `@idfkit/language`, without which the naming gate would not know
to hold the new package's surface against anything.

The corpus level does not move. This adds no cases.
@samuelduchesne
samuelduchesne requested review from a team as code owners September 4, 2026 19:15
@samuelduchesne
samuelduchesne merged commit 9962077 into main Sep 4, 2026
1 check passed
@samuelduchesne
samuelduchesne deleted the 005-idf-language-service branch September 4, 2026 19:29
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.

1 participant