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
71 changes: 46 additions & 25 deletions docs/adr/0020-keep-primitive-literals-shadow-only-after-dual-run.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,18 +46,36 @@ machine-readable owner and concrete action required for reclassification. A
changed code, state, diagnostic, or mapping outcome is unexplained and blocks
the gate.

Runtime and output sizes are retained as two ordered measurement samples.
Artifact sizes and the controller's peak or current resident memory are
recorded with their measurement method and scope. These measurements are
characterization data: the Go lane includes a one-shot process and JSON Lines
transport, while Rust runs in-process, and controller RSS excludes the Go child
process. The measured fields are therefore not part of the byte-stability gate
and do not establish a performance advantage.

The primitive/literal category is **not ready for a later authority decision**.
Rust remains shadow-only. Go remains both the serving semantic authority and
the production fallback. There is no authority switch, TS7 producer protocol
change, external consumer behavior change, or Palamedes change.
Issue #53 selects `one-shot-child-process-shadow` as the production-equivalent
observation boundary. For every classified corpus case, the release rollout
controller starts the real `tsfacts` command as the Go serving child and the
release `oxc-occurrence-map primitive-shadow-worker` command as the Rust shadow
child. Both receive the same ordered project selections and equivalent limits.
Only the Go child's stdout is eligible to become the served response; Rust
stdout is observation-only and uses an internal request/response shape that is
not the TS7 producer protocol.

The serving controller's failure state machine is executable library code. A
Rust failure is observed, preserves the Go response, disables subsequent
shadow execution, and can be re-enabled only by an explicit reset. A Go
failure is returned and never masked by Rust. Unit tests and every rollout
report exercise failure, rollback, skipped-after-rollback, reset, and Go
failure paths.

Runtime, raw producer-output size, artifact size, and peak resident memory are
retained as two ordered measurement samples. Both runtime lanes now cover the
same one-shot child-process boundary. On Unix, the controller uses `wait4`
`ru_maxrss` for each child and aggregates the maximum case-process RSS per
producer. Raw output sizes deliberately cover different internal schemas and
the sequential samples remain characterization data, not performance claims or
compatibility thresholds.

With these production-integration and measurement gaps closed, the checked
evidence is **ready to inform a later authority decision**. This ADR still
keeps Rust shadow-only. Go remains both the serving semantic authority and the
production fallback. There is no authority switch, TS7 producer protocol
change, external consumer behavior change, or Palamedes change. A switch still
requires an explicit later proposal and ADR.

## Resolved rollout observations

Expand All @@ -74,19 +92,20 @@ These are resolved, explicit limitations rather than authority-readiness
blockers or hidden exclusions. Their fixture contracts remain CI-blocking when
the observed behavior changes.

## Remaining authority blockers
## Remaining caveats

- Rust is not integrated into the serving path, so production fallback,
rollback, and shadow observation at that boundary have not been exercised.
- Runtime and output-size samples do not yet use one production-equivalent
process or library boundary for both implementations.
- The evidence does not isolate peak resident memory for the Go child process,
so per-producer memory parity is not established.
- The four unsupported and three recovery-file observations remain explicit,
owned limitations; evidence readiness does not make them supported.
- Raw Go and Rust output-byte counts cover different internal payload schemas
and must not be interpreted as wire-protocol parity.
- The sequential one-shot samples are not daemon throughput or latency
benchmarks.
- Peak RSS uses Unix `wait4`; a non-Unix release must supply or justify a
comparable child-process method.

A later authority proposal must close or explicitly re-scope these blockers,
rerun the supported platform matrix, preserve Go fallback, and create a new ADR
that authorizes the category-specific switch. This record does not authorize
that proposal.
A later authority proposal must assess these caveats, rerun the supported
platform matrix, preserve Go fallback, and create a new ADR that authorizes any
category-specific switch. This record does not authorize that proposal.

## Consequences

Expand All @@ -95,8 +114,9 @@ that proposal.
size evidence.
- Host-dependent measurements remain reproducible through a documented command
without weakening the exact semantic gate.
- A green rollout job means that shadow comparison is healthy. It does not mean
the category is production-ready or approved to replace Go.
- A green rollout job means that exact comparison, production-equivalent shadow
observation, rollback, and scoped measurements are healthy. It does not
approve replacing Go.

## References

Expand All @@ -105,3 +125,4 @@ that proposal.
- [Checked rollout evidence](../evidence/primitive-literal-rollout-2026-08-17.json)
- [Issue #47](https://github.com/swernerx/typescript-semantic-kernel/issues/47)
- [Issue #52](https://github.com/swernerx/typescript-semantic-kernel/issues/52)
- [Issue #53](https://github.com/swernerx/typescript-semantic-kernel/issues/53)
17 changes: 13 additions & 4 deletions docs/evidence/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,9 +45,17 @@ record is generated by:
It embeds the exact primitive/literal conformance report and requires that
stable report to be byte-identical across two complete dual-runs. Runtime,
resident memory, artifact size, and producer-output size are retained in a
separate measurement section because those values or their comparison boundary
vary by host. The report is a shadow rollout artifact, not an authority switch
or a general benchmark.
separate measurement section. Go serving and Rust shadow execution use the
same one-shot release-child boundary over the same per-case requests; Unix
`wait4` records each child's peak RSS. Raw output bytes still cover different
internal schemas, and sequential samples vary by host, so the report is not a
general benchmark.

The report also exercises an executable serving controller: Rust failure is
observed while the Go response survives, rollback disables later shadow work,
explicit reset re-enables it, and a Go failure is never masked. Only Go output
is served. This makes the evidence ready for a later authority decision but is
not itself an authority switch.

Conformance schema v5 reports both the exact supported-record compatibility
and a CI-enforced 100% accounting denominator over every selected fact. The
Expand All @@ -56,4 +64,5 @@ mapping gaps remain present as stable regression limitations with explicit
owners and reclassification actions.

[ADR-0020](../adr/0020-keep-primitive-literals-shadow-only-after-dual-run.md)
records why the category is not ready for a later authority decision.
records the selected boundary, measurements, remaining caveats, and continued
Go authority.
81 changes: 55 additions & 26 deletions docs/evidence/primitive-literal-rollout-2026-08-17.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"schemaVersion": 2,
"schemaVersion": 3,
"evidenceKind": "primitive-literal-controlled-go-rust-dual-run",
"command": "./internal/oxc_reference/run-rollout.sh --output <path>",
"environment": {
Expand All @@ -17,6 +17,25 @@
"ts7ProducerProtocolChanged": false,
"externalConsumerBehaviorChanged": false
},
"boundary": {
"name": "one-shot-child-process-shadow",
"controller": "release rollout controller",
"goServingProcess": "cmd/tsfacts JSON Lines serving child",
"rustShadowProcess": "oxc-occurrence-map primitive-shadow-worker child",
"requestScope": "same ordered manifest selections, capabilities, budgets, and project per classified corpus case",
"servedOutput": "Go stdout only; Rust stdout is observation-only",
"sameOneShotChildProcessBoundary": true,
"releaseArtifacts": true
},
"failurePaths": {
"goResponseSurvivesShadowFailure": true,
"shadowFailureIsObserved": true,
"shadowFailureDetailRetained": true,
"rollbackDisablesShadow": true,
"rolledBackRequestSkipsShadow": true,
"explicitResetReenablesShadow": true,
"goFailureIsNotMasked": true
},
"determinism": {
"completeRuns": 2,
"conformanceReportsByteEqual": true,
Expand All @@ -28,7 +47,7 @@
"candidate": "independent-primitive-literal-v2",
"shadowOnly": true,
"execution": {
"repositoryRevision": "20d726c587bc612b5be8311530a553f70b0ed79b",
"repositoryRevision": "5d79fe8dcc7fbe740fd0391ee3b7f1bb45e90409",
"typescriptVersion": "7.1.0-dev",
"typescriptRevision": "1bcfa18d79a3be41772223d5c05dfe4480e614ff",
"requestSchemaVersion": 1,
Expand Down Expand Up @@ -3975,40 +3994,48 @@
"passes": true
},
"measurements": {
"scope": "one-shot Go process versus in-process Rust producer over identical ordered requests; characterization only",
"scope": "sequential one-shot release child processes over identical ordered per-case requests; characterization, not a throughput benchmark",
"outputScope": "raw successful child stdout bytes before decoding; Go JSON Lines and the internal Rust shadow payload have different schemas",
"memoryScope": "per-child peak resident set from wait4 rusage, aggregated as the maximum across case processes for each producer",
"samples": [
{
"ordinal": 1,
"cases": 4,
"goOracleNanoseconds": 700402249,
"rustProducerNanoseconds": 1367000,
"rustDeterminismCheckNanoseconds": 236041,
"totalNanoseconds": 704086084,
"goSnapshotBytes": 36671,
"rustCandidateBytes": 22615
"goServingWallNanoseconds": 297564333,
"rustShadowWallNanoseconds": 38245417,
"goServedOutputBytes": 36671,
"rustShadowOutputBytes": 22619,
"goPeakResidentBytes": 44023808,
"rustPeakResidentBytes": 3162112,
"residentMeasurement": "wait4-ru-maxrss-peak",
"memoryComparable": true,
"servedGoResponsesUnchanged": true,
"shadowObservationFailures": 0
},
{
"ordinal": 2,
"cases": 4,
"goOracleNanoseconds": 127294084,
"rustProducerNanoseconds": 789167,
"rustDeterminismCheckNanoseconds": 237917,
"totalNanoseconds": 129383625,
"goSnapshotBytes": 36671,
"rustCandidateBytes": 22615
"goServingWallNanoseconds": 248149001,
"rustShadowWallNanoseconds": 29793292,
"goServedOutputBytes": 36671,
"rustShadowOutputBytes": 22619,
"goPeakResidentBytes": 43384832,
"rustPeakResidentBytes": 3162112,
"residentMeasurement": "wait4-ru-maxrss-peak",
"memoryComparable": true,
"servedGoResponsesUnchanged": true,
"shadowObservationFailures": 0
}
],
"artifacts": {
"goExecutableBytes": 25670354,
"rustExecutableBytes": 3345040,
"peakOrCurrentControllerResidentBytes": 4964352,
"residentMeasurement": "macos-getrusage-peak",
"memoryScope": "Rust rollout controller including decoded Go snapshots; excludes child Go process RSS"
"rustExecutableBytes": 3498416,
"scope": "on-disk bytes of the two release executables invoked at the selected child-process boundary"
}
},
"readiness": {
"readyForLaterAuthorityDecision": false,
"status": "not-ready",
"readyForLaterAuthorityDecision": true,
"status": "evidence-ready-authority-unchanged",
"resolvedRolloutLimitations": [
{
"case": "primitive-literal-conformance",
Expand Down Expand Up @@ -4130,11 +4157,13 @@
"action": "Reclassify only when OXC parses the recovery file and produces an exact NodeId mapping for this recursive-budget selection."
}
],
"blockers": [
"the Rust producer is not integrated into the serving path, so production fallback and rollback have not been exercised",
"runtime and output measurements compare a one-shot Go process with an in-process Rust shadow path; a production-equivalent boundary is not selected",
"controller RSS excludes the child Go process, so per-producer peak-memory parity is not established"
]
"remainingCaveats": [
"the four unsupported primitive/literal selections and three recovery-file mapping gaps remain explicitly fixture-classified limitations",
"raw producer-output byte counts characterize different internal payload schemas and are not protocol-parity evidence",
"sequential one-shot process samples characterize the selected boundary; they are not daemon throughput or latency benchmarks",
"peak resident memory uses Unix wait4 rusage and requires a separately justified method on non-Unix platforms"
],
"blockers": []
},
"passes": true
}
35 changes: 28 additions & 7 deletions docs/porting/migration-contract.md
Original file line number Diff line number Diff line change
Expand Up @@ -94,16 +94,16 @@ gate compares its five roots, structured graph identity, states, mapping, and
truncation against Go at an exact threshold. Passing it does not transfer
semantic authority or alter production routing. Project loading, resolution,
binding, symbols, inference beyond the supported contextual literals,
overloads, generic instantiation, narrowing, recovery, and all production
fallback behavior remain Go-authoritative.
overloads, generic instantiation, narrowing, and recovery remain
Go-authoritative.

ADR-0020 adds the controlled rollout lane. Two complete runs must share the
checked-out corpus and repository revision, TS compiler revision, request
schema, project, capabilities, budgets, and ordered selections. The stable
conformance reports must be byte-identical. Host-dependent runtime, resident
memory, artifact size, and producer-output size remain separately scoped
characterization evidence. The lane is shadow-only and does not exercise or
authorize production routing.
characterization evidence. The lane is shadow-only and does not authorize
production routing.

Issue #52 adds conformance schema v5's all-selection accounting gate. Every
selected fact must be classified, the classification count must remain equal
Expand All @@ -114,6 +114,26 @@ owners and concrete reclassification actions. They are neither dropped from
the corpus nor counted as supported. Go remains semantic authority and the
production fallback.

Issue #53 selects a production-equivalent, one-shot child-process boundary for
the rollout controller. The controller obtains the served response from the
real release `tsfacts` child and starts the release Rust
`primitive-shadow-worker` child only for observation. Both children cover the
same ordered per-case project selections and limits. The Rust worker's compact
JSON is an internal harness contract, not a change to the TS7 producer
protocol. A Rust failure preserves the Go response, records the failure, and
disables shadow execution until an explicit reset; a Go failure is never
masked. These fallback, rollback, reset, and failure-observation paths are
executable tests and structured rollout fields.

Both runtime samples now measure the same release child-process scope. Raw
successful stdout bytes are counted before decoding, artifact sizes cover the
two invoked executables, and Unix `wait4` peak RSS is measured independently
for every child and aggregated per producer. Different internal payload
schemas and sequential one-shot execution remain explicit characterization
caveats. Passing this evidence makes the slice ready for a later authority
decision, but does not itself switch authority: Go remains the serving path and
production fallback.

## Gate ladder

1. Focused `internal/semanticfacts` and `internal/tsfacts` tests pass, including
Expand All @@ -136,9 +156,10 @@ production fallback.
mapping cases. Its supported-record metric is paired with a mandatory 100%
all-selection accounting denominator, so no classified case is hidden.
10. `./internal/oxc_reference/run-rollout.sh --output <path>` executes two
release-profile dual-runs, requires byte-identical embedded conformance,
records the controlled measurements in ADR-0020, and retains Go as serving
authority and production fallback.
release-profile dual-runs at the same one-shot child-process boundary,
requires byte-identical embedded conformance, exercises fallback and
rollback, records comparable runtime/output/artifact/child-RSS scopes in
ADR-0020, and retains Go as serving authority and production fallback.

## Upstream synchronization

Expand Down
7 changes: 7 additions & 0 deletions internal/oxc_reference/Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions internal/oxc_reference/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -13,5 +13,6 @@ oxc_ast = "0.144.0"
oxc_parser = "0.144.0"
oxc_semantic = "0.144.0"
oxc_span = "0.144.0"
libc = "0.2"
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
Loading