Skip to content

Commit 20095e8

Browse files
committed
the Ruby corpus era: ISC dependencies, lint gate, drift guard
TODO.impl 12-14 (all COMPLETE): - 12: NodeAdapter wires ISC dependencies - the adapter built metadata/tests/aliases/stages and silently DROPPED dependencies, so every cross-map run in an .isc document dereferenced nil (interpreter.rb:78 via the run branch). Deps now carry full_name, the alias, import semantics (unaliased = library import, the ISC form of the .imp 'dependency x, import: true'), and documents loaded through the compiler's own dispatch so chains recurse. Also: rule constraints read :item (the builder's shape) - reading the wrong key silently produced a match-always before-guard, the exact Oleghovych regression the Python port hit too. Repro spec RED then GREEN against the real corpus (bgnpcgn-ukr -> Anton Olehovych, bgnpcgn-deu -> Tschueß!). Local runs had been green only because the gem's load path preferred the installed 2.4.3 .imp maps gem. - 13: standardrb 0 offenses (was: the job never got past bundler). Autofix + hand fixes; the deliberate idioms (/-d flags, DSL method_missing) carry inline disables with reasons; URI.open gets a scoped disable (fixed interscript-hosted model URI, not input). - 14: weekly lockstep-check workflow - the maps-version drift that red-broke CI silently for two weeks now fails as a named alert with both version numbers in the log.
1 parent fb6836d commit 20095e8

53 files changed

Lines changed: 592 additions & 372 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
name: lockstep-check
2+
3+
# The gemspec derives its interscript-maps constraint from this gem's
4+
# own version. When maps ships a new minor, bundler resolution fails
5+
# everywhere with an opaque exit code — that ran silent for two weeks
6+
# (2026-08-26 to 2026-09-08). This probe makes the drift a named,
7+
# weekly alert instead.
8+
on:
9+
schedule:
10+
- cron: "0 6 * * 1"
11+
workflow_dispatch:
12+
13+
jobs:
14+
resolve:
15+
runs-on: ubuntu-latest
16+
steps:
17+
- uses: actions/checkout@v7
18+
- name: Clone the maps corpus as the path dependency
19+
run: git clone --depth 1 https://github.com/interscript/maps ../maps
20+
- uses: ruby/setup-ruby@v1
21+
with:
22+
ruby-version: "3.4"
23+
- name: Report both version numbers
24+
run: |
25+
echo "gem: $(ruby -Ilib -e 'require "interscript/version"; print Interscript::VERSION')"
26+
echo "maps: $(ruby -e 'print File.read("../maps/interscript-maps.gemspec")[/INTERSCRIPT_MAPS_VERSION = "([^"]+)"/, 1]')"
27+
- name: Resolve the lockfile against the corpus
28+
run: bundle lock

.standard.yml

Lines changed: 6 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,7 @@
1-
# StandardRB config — enforce Ruby style on lib/, leave specs/bin/exe flexible for now.
2-
# Existing violations in lib/ were auto-fixed in this PR; remaining violations are
3-
# tracked in TODO.complete/06-standardrb-ruby-gems.md.
1+
# Deliberate-design exclusions, each with its reason:
2+
# - utils/helpers.rb: spec-example bookkeeping uses intentional globals
3+
# - compiler/ruby.rb: the compiler's job is eval'ing generated code;
4+
# debug capture uses intentional globals
45
ignore:
5-
- "bin/**/*"
6-
- "exe/**/*"
7-
- "spec/**/*"
8-
- "docs/**/*"
9-
- "reference-docs/**/*"
10-
- "pkg/**/*"
11-
- "vendor/**/*"
6+
- lib/interscript/utils/helpers.rb
7+
- lib/interscript/compiler/ruby.rb
Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
# [COMPLETE 2026-09-08 — repro spec RED then GREEN; deps wired, constraint key fixed, library-import semantics restored] 12 — Ruby adapter drops ISC dependencies (P1)
2+
3+
## Goal
4+
`NodeAdapter#build` wires the ISC document's dependencies into the
5+
Node::Document (dependencies, dep_aliases, lazily-loaded documents) so
6+
`run map.<alias>.stage.<name>` works — unblocking PR #769 and the
7+
corpus-era rspec failures.
8+
9+
## Why
10+
Root cause of the 79-failure batch (named 2026-09-08): the adapter
11+
sets metadata/tests/aliases/stages/name and silently drops
12+
`dependencies` — every cross-map run in an `.isc` document then
13+
dereferences nil (`reverse_run` on nil at interpreter.rb:78 via the
14+
run branch). Local runs never saw it: they resolved the installed
15+
`interscript-maps-2.4.3` gem (`.imp`) because the gem's load path
16+
prefers installed map gems over siblings.
17+
18+
## Spec (TDD)
19+
20+
1. RED: a spec that forces the ISC corpus onto the load path
21+
(`Interscript.load_path` prepended with the maps checkout),
22+
transliterates `bgnpcgn-ukr-Cyrl-Latn-2019` "Антон Олегович", and
23+
asserts "Anton Olehovych" — currently raises the nil crash.
24+
2. GREEN: `build` constructs `Node::Dependency` per ISC dependency —
25+
`full_name`, `name` (the alias, symbolized), `import = false` (ISC
26+
v1 carries no import marker; alias-having deps are reached through
27+
`dep_aliases`, which `import` does not gate), `document` parsed
28+
through the same dispatch the compiler uses (`.isc` → parse_isc,
29+
else DSL.parse) so chains resolve recursively.
30+
3. `doc.dep_aliases[name] = dep` mirroring the DSL (document.rb:40).
31+
32+
## Acceptance
33+
- The repro spec green; PR #769's gallery parity green against the
34+
real corpus (not the stale gem).
35+
- Corpus-census rspec run: failure count measured before/after,
36+
remainder categorized in the PR body (this fix may not cure all 79;
37+
what remains is named, not hidden).

TODO.impl/13-ruby-style-debt.md

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# [COMPLETE 2026-09-08 — standardrb clean (0 offenses); autofix + hand fixes + reasoned inline disables for the deliberate $DEBUG/method_missing idioms] 13 — Ruby style debt unblock (P2)
2+
3+
## Goal
4+
StandardRB can pass again, so the lint gate is real.
5+
6+
## Why
7+
The StandardRB job never got past bundler resolution since the Gemfile
8+
gained its path dependency — a full never-linted offense pile sits in
9+
lib/. With item 12's CI repair, every PR now inherits the red lint.
10+
11+
## Spec
12+
- `standardrb --fix` for the auto-fixable; the remainder fixed by hand
13+
EXCEPT `Security/Open` (URI.open) — that one is a behavioral risk
14+
(network fetch in rababa model download) and gets an inline
15+
disable with a reason, not a blind rewrite.
16+
- Zero diff beyond style; specs stay green.
17+
18+
## Acceptance
19+
- `bundle exec standardrb` clean; rspec unchanged by the cleanup.
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
# [COMPLETE 2026-09-08 — weekly lockstep-check workflow; failure mode evidenced this round] 14 — Lockstep drift guard (P2)
2+
3+
## Goal
4+
A scheduled check that fails loudly when the maps gem version outruns
5+
the runtime's lockstep constraint — the failure mode that ran silent
6+
from 2026-08-26 to 2026-09-08.
7+
8+
## Why
9+
The gemspec derives `interscript-maps ~> X.Y.0a` from the gem's own
10+
version. When maps shipped 2.5.0, every CI job failed at bundler with
11+
an opaque exit 6/13 and main sat red for two weeks. A weekly probe
12+
that resolves the gemspec against maps main turns that into a named,
13+
actionable alert.
14+
15+
## Spec
16+
- Weekly workflow on interscript-ruby: clone maps, run
17+
`bundle lock` against the Gemfile with the clone as the path
18+
dependency; failure output includes both version numbers.
19+
- No new dependencies; a single job.
20+
21+
## Acceptance
22+
- The workflow runs green on the current pair (2.5.0/2.5.0) and
23+
demonstrably fails when the constraint can't resolve (verified once
24+
by temporarily pinning back — or trusted from this round's
25+
evidence, stated either way).

TODO.impl/README.md

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# TODO.impl — round three: the Ruby corpus era
2+
3+
Round two's discovery: the Ruby gem is the last runtime not working
4+
against the ISC corpus — its adapter drops dependencies (79 rspec
5+
failures, root cause named), its lint gate never ran, and the
6+
version-lockstep break that caused both ran silent for two weeks.
7+
Status: ALL 3 ITEMS COMPLETE (2026-09-08). This register closed that era.
8+
9+
| # | Item | Priority |
10+
|---|------|----------|
11+
| 12 | [ISC dependencies in the NodeAdapter](12-isc-dependencies-adapter.md) | P1 |
12+
| 13 | [Style debt unblock](13-ruby-style-debt.md) | P2 |
13+
| 14 | [Lockstep drift guard](14-lockstep-drift-guard.md) | P2 |
14+
15+
Completing 12 unblocks PR #769 (the gallery parity spec, held open
16+
with the root cause). Standing rules unchanged: TDD where behavior is
17+
touched, staged sets verified, no attribution trailers.

lib/interscript.rb

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -146,13 +146,13 @@ def rababa_provision(model_name, model_uri)
146146
model_path = "#{write_path}/model-#{model_name}.onnx"
147147

148148
# Redownload every hour
149-
if File.exist?(model_path) && File.mtime(model_path) + 3600 >= Time.now
150-
model_path
151-
else
149+
unless File.exist?(model_path) && File.mtime(model_path) + 3600 >= Time.now
150+
# standard:disable Security/Open (fetches a fixed interscript-hosted model URI, not user input)
152151
data = URI.open(model_uri, encoding: "BINARY").read
152+
# standard:enable Security/Open
153153
File.binwrite(model_path, data)
154-
model_path
155154
end
155+
model_path
156156
end
157157

158158
def map_aliases

lib/interscript/compiler.rb

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,16 @@ class Interscript::Compiler
1010

1111
def self.call(map, **kwargs)
1212
if String === map
13-
path = Interscript.locate(map) rescue nil
13+
path = begin
14+
Interscript.locate(map)
15+
rescue
16+
nil
17+
end
1418
map = if path&.end_with?(".isc")
15-
parse_isc(path)
16-
else
17-
Interscript::DSL.parse(map)
18-
end
19+
parse_isc(path)
20+
else
21+
Interscript::DSL.parse(map)
22+
end
1923
end
2024
compiler = new
2125
compiler.compile(map, **kwargs)

lib/interscript/compiler/json_ir.rb

Lines changed: 10 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -60,14 +60,12 @@ def serialise_document(doc)
6060
# reference via alias() without listing the library as an explicit
6161
# dependency in the dependency list.
6262
Interscript.maps(libraries: true).each do |lib|
63-
begin
64-
libdoc = Interscript.parse(lib)
65-
libdoc.aliases.each do |aname, defn|
66-
all_aliases[aname.to_s] ||= serialise_item(defn.data)
67-
end
68-
rescue
69-
# skip unparseable libraries
63+
libdoc = Interscript.parse(lib)
64+
libdoc.aliases.each do |aname, defn|
65+
all_aliases[aname.to_s] ||= serialise_item(defn.data)
7066
end
67+
rescue
68+
# skip unparseable libraries
7169
end
7270

7371
# Document's own aliases override everything.
@@ -91,9 +89,9 @@ def serialise_metadata(metadata)
9189
out = {}
9290
metadata.data.each do |k, v|
9391
out[k.to_s] = case v
94-
when Symbol then v.to_s
95-
else v
96-
end
92+
when Symbol then v.to_s
93+
else v
94+
end
9795
end
9896
out
9997
end
@@ -155,7 +153,7 @@ def serialise_run_rule(rule)
155153
doc_name = stage.map
156154
if doc_name && @map.respond_to?(:dep_aliases) && @map.dep_aliases[doc_name.to_sym]
157155
resolved = @map.dep_aliases[doc_name.to_sym].document
158-
doc_name = resolved.name.to_s if resolved && resolved.respond_to?(:name)
156+
doc_name = resolved.name.to_s if resolved.respond_to?(:name)
159157
end
160158
{
161159
kind: "run",
@@ -203,7 +201,7 @@ def serialise_item(item)
203201
when ::Range
204202
{kind: "any_char_class", range: [item.value.first, item.value.last]}
205203
when ::String
206-
{kind: "any_char_class", chars: item.value.split("")}
204+
{kind: "any_char_class", chars: item.value.chars}
207205
else
208206
data = item.data || []
209207
{kind: "any", of: data.map { |i| serialise_item(i) }}

lib/interscript/dsl/items.rb

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
module Interscript::DSL::Items
22
include Interscript::DSL::SymbolMM
33

4+
# standard:disable Style/MissingRespondToMissing (DSL method_missing catches unknown method names by design)
45
def method_missing sym, *args, **kwargs, &block
6+
# standard:enable Style/MissingRespondToMissing
57
super if args.length > 0
68
super if kwargs.length > 0
79
super if /[?!=]\z/.match?(sym.to_s)

0 commit comments

Comments
 (0)