Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,21 @@ jobs:
steps:
- uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0
- uses: dtolnay/rust-toolchain@fcf085fcb4b4b8f63f96906cd713eb52181b5ea4 # stable
- uses: ./.github/actions/setup-go
- name: Check internal OXC reference formatting
run: cargo fmt --manifest-path internal/oxc_reference/Cargo.toml --all -- --check
- name: Lint internal OXC reference consumer
run: cargo clippy --locked --manifest-path internal/oxc_reference/Cargo.toml --all-targets -- -D warnings
- name: Test internal OXC reference consumer
run: cargo test --locked --manifest-path internal/oxc_reference/Cargo.toml
- name: Gate Go versus Rust semantic conformance
run: ./internal/oxc_reference/run-conformance.sh --output go-rust-conformance.json
- name: Upload Go versus Rust conformance report
if: ${{ always() }}
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: go-rust-semantic-conformance
path: go-rust-conformance.json

build:
runs-on: ubuntu-latest
Expand Down
113 changes: 113 additions & 0 deletions docs/adr/0018-gate-rust-semantics-against-the-go-oracle.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
# ADR-0018: Gate Rust semantics against the Go oracle

- Status: accepted
- Date: 2026-08-17
- Deciders: TypeScript Semantic Kernel maintainers
- Supersedes: —
- Superseded by: —

## Context

ADR-0017 introduced a versioned Rust-owned primitive/literal candidate over the
immutable schema-v1 graph emitted by TypeScript 7's Go checker. Its structured
records make a semantic comparison possible, but tests over one canonical
fixture do not establish corpus compatibility. Display strings also cannot
prove equivalence: a gate must compare fact identity, graph roots, structured
payloads, states, diagnostics, and truncation without collapsing different
failure modes.

The first candidate still consumes Go-produced facts. It is useful shadow
evidence, not an independent semantic producer and not authority to change the
TS7 producer protocol or replace the Go checker.

## Decision

`./internal/oxc_reference/run-conformance.sh` builds the real Go `tsfacts`
oracle, runs every case in `internal/semanticfacts/testdata/corpus/v0`, projects
every returned fact through the Rust primitive/literal candidate, and emits a
deterministic JSON report. `--output <path>` writes the same report for CI or
local inspection.

The comparator checks:

- the portable file/span/syntax fact identity and complete/recovered/truncated
status;
- all ordered actual, contextual, widened, apparent, and declared view states
and their effective response-local TypeIDs;
- each candidate TypeID, source kind/state/issues, structured primitive,
literal, null-like, or union payload, union member identity, candidate state,
and reason;
- the Go diagnostic count and producer budget report as observed by Rust; and
- OXC mapping coverage without dropping facts from sources that OXC cannot
parse.

Every report entry is classified as `semantic`, `transport`, `mapping`,
`unsupported`, or `budget`. Ordering uses case name, category, fact index,
field path, code, and stable serialized values; the report contains no timing,
host path, or random identifier.

The shadow compatibility threshold is:

1. at least the seven complete, in-category primitive/literal records in the
initial corpus baseline, preventing a vacuous pass if coverage disappears;
2. 1,000,000 parts-per-million exact agreement for those supported records;
3. zero unexplained semantic differences;
4. zero unexplained transport differences; and
5. explicit, non-blocking reporting of mapping differences and expected
unsupported/error and producer/consumer budget states.

The CI command exits nonzero when either blocking count is nonzero or supported
compatibility falls below the threshold. Mapping does not block this shadow
semantic gate because the candidate can compare every decoded Go fact directly
and the corpus intentionally retains one syntax-recovery file that OXC cannot
parse. Mapping gaps remain visible and must be eliminated or bypassed by an
independent producer before replacement.

The initial corpus baseline is 25 facts across six cases, 125 compared roots
with 125 identity matches, and seven complete in-category records with seven
exact matches. It also reports the three facts in the known recovery-file
mapping gap and separately enumerates unsupported/error and budget/truncation
states. These counts describe the current shadow candidate; the executable
threshold, rather than these counts, is normative as the corpus expands.

## Replacement checklist

Passing the shadow gate does not approve replacement. A later ADR may transfer
one semantic category only after all of the following are true:

- Rust produces that category independently from source/project inputs instead
of projecting the Go-produced graph.
- Go and Rust run the same pinned corpus, compiler revision, request,
capabilities, and budgets.
- Every complete in-category fact and all five available roots reach 1,000,000
ppm structured agreement with zero semantic or transport differences.
- Response-local graph identity, repeated roots, union member ordering,
diagnostics, completeness, recovery, error, unsupported, and truncation
behavior agree exactly.
- There are zero new unsupported forms, completeness downgrades, or unexplained
mapping gaps for the category; expected unsupported and budget cases remain
named fixtures rather than denominator exclusions hidden from the report.
- Repeated output is byte-stable and the gate runs as required CI on the
supported matrix.
- Go remains an available fallback through rollout, and no TS7 producer
protocol change is bundled with the authority switch.
- A category-specific ADR records fresh differential evidence and explicitly
authorizes the switch.

## Consequences

- Semantic and transport regressions now fail CI with field-level JSON
differences instead of display-text comparisons.
- Unsupported, error, recovery, mapping, and budget evidence stays observable
without incorrectly failing the current shadow category.
- Corpus growth can increase the supported denominator without weakening the
exact compatibility threshold.
- TypeScript 7's Go checker remains the semantic oracle and sole producer
authority. This ADR makes no protocol or production-routing change.

## References

- [RFC 0001](../../rfcs/0001-semantic-facts-kernel.md)
- [ADR-0016](0016-port-occurrence-attachment-before-semantic-categories.md)
- [ADR-0017](0017-project-primitive-literal-candidates-from-go-graph-identity.md)
- [Issue #41](https://github.com/swernerx/typescript-semantic-kernel/issues/41)
13 changes: 7 additions & 6 deletions docs/adr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ decision is recorded in a successor ADR that links to the record it replaces.
| [0015](0015-attach-semantic-facts-without-expanding-graph-identity.md) | Accepted | Attach semantic facts without expanding graph identity |
| [0016](0016-port-occurrence-attachment-before-semantic-categories.md) | Accepted | Port occurrence attachment before semantic categories |
| [0017](0017-project-primitive-literal-candidates-from-go-graph-identity.md) | Accepted | Project primitive/literal candidates from Go graph identity |
| [0018](0018-gate-rust-semantics-against-the-go-oracle.md) | Accepted | Gate Rust semantics against the Go oracle |

## Current migration decision

Expand All @@ -35,9 +36,9 @@ Rust/OXC consumer remains a reference and migration harness; the evidence does
not establish compiler equivalence, production readiness, or a performance
advantage.

[ADR-0017](0017-project-primitive-literal-candidates-from-go-graph-identity.md)
records the first Rust-owned primitive/literal candidate over the Go-produced
graph. It makes structured comparison possible but is not an independent
producer. Primitive/literal construction remains Go-authoritative until a
future Rust producer passes ADR-0016's replacement gates; no semantic category
is currently approved for replacement.
[ADR-0018](0018-gate-rust-semantics-against-the-go-oracle.md) runs that first
Rust-owned primitive/literal candidate over the complete Go corpus and enforces
exact supported structured agreement in shadow CI. The candidate is still a
projection of the Go-produced graph, not an independent producer. Passing the
shadow gate therefore leaves primitive/literal construction Go-authoritative;
no semantic category is currently approved for replacement.
11 changes: 8 additions & 3 deletions docs/porting/migration-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -91,9 +91,11 @@ ADR-0016 selects occurrence identity and attachment plumbing as the first safe
mechanical port. ADR-0017 adds the first Rust-owned primitive/literal candidate
record over the Go-produced graph so future comparisons can use structured
kinds, literal values, union edges, roots, and states. It is not an independent
producer, and no semantic category may replace Go until independent Rust output
matches the normalized structured oracle with no completeness/state downgrade
or new unsupported form. Project loading, resolution, binding, symbols,
producer. ADR-0018 adds the corpus-wide shadow gate and exact compatibility
threshold, but passing it does not transfer semantic authority. No category may
replace Go until independent Rust output completes ADR-0018's replacement
checklist with no completeness/state downgrade, new unsupported form, or
unexplained mapping gap. Project loading, resolution, binding, symbols,
inference, contextual/widened types, overloads, generic instantiation,
narrowing, and recovery remain Go-authoritative.

Expand All @@ -113,6 +115,9 @@ narrowing, and recovery remain Go-authoritative.
Accepted.
8. `./internal/oxc_reference/run-evidence.sh --output <path>` produces stable
repeated observations and passes ADR-0016's occurrence/attachment gate.
9. `./internal/oxc_reference/run-conformance.sh --output <path>` emits the
deterministic ADR-0018 report and fails on any unexplained semantic or
transport mismatch while retaining mapping, unsupported, and budget cases.

## Upstream synchronization

Expand Down
21 changes: 19 additions & 2 deletions internal/oxc_reference/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,23 @@ intentional syntax-recovery source as a consumer failure. It does not infer
semantic equivalence from those mappings. See
[ADR-0016](../../docs/adr/0016-port-occurrence-attachment-before-semantic-categories.md).

Run the deterministic Go-versus-Rust shadow conformance gate with:

```sh
./internal/oxc_reference/run-conformance.sh \
--output /tmp/ts7-rust-conformance.json
```

The command runs the same corpus through the Go oracle and the version-1 Rust
primitive/literal candidate. Its JSON compares fact identity, all five roots,
response-local graph identity, structured payloads, diagnostics, unsupported
and error states, and truncation. Every entry is classified as `semantic`,
`transport`, `mapping`, `unsupported`, or `budget`. Unexplained semantic or
transport differences fail the command; expected unsupported/budget cases and
the known recovery-file mapping gap remain separately reported. The gate is a
shadow comparison and does not change the producer or production routing. See
[ADR-0018](../../docs/adr/0018-gate-rust-semantics-against-the-go-oracle.md).

The first test suite applies the Rust implementation of the portable contract
to every shared JSON fixture in `internal/occurrencemap/testdata/v1` and checks
its complete expected report. It separately parses those fixture sources with
Expand Down Expand Up @@ -101,8 +118,8 @@ Occurrence identity and attachment plumbing is the first approved mechanical
port category. The primitive/literal projection is the first implemented
semantic candidate record, but it consumes Go-produced graph facts and does
not constitute an independent producer. All semantic answers remain
Go-authoritative until an independent Rust producer passes ADR-0016's
differential gate.
Go-authoritative until an independent Rust producer completes ADR-0018's
replacement checklist.

Adding a projection does not transfer semantic authority. New parser-boundary
normalizations require a shared fixture and the versioned portable contract.
Loading