Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
68 commits
Select commit Hold shift + click to select a range
be94b7e
Feat/portable logic foundation (#386)
cukas Jun 6, 2026
4d00fa5
build(deps): bump the minor-and-patch group with 5 updates (#388)
dependabot[bot] Jun 7, 2026
a5c1bf1
Feat/portable logic foundation (#389)
cukas Jun 7, 2026
e5cbb34
Fix guard typecheck findings
cukas Jun 7, 2026
270d442
Add KERN core runtime foundation
cukas Jun 7, 2026
4a8107a
feat(core): add kern object runtime foundation
cukas Jun 7, 2026
eb09948
Merge branch 'main' into feat/kern-core-runtime
cukas Jun 7, 2026
2011262
feat(core): add semantic substrate for review
cukas Jun 7, 2026
7f75f40
fix(core): harden semantic substrate typing
cukas Jun 7, 2026
b1d7cdc
fix(core): avoid unknown stdlib entries
cukas Jun 7, 2026
2307eac
feat(core): add class object semantic validation
cukas Jun 8, 2026
2d6a94e
feat(core): harden class runtime setters
cukas Jun 8, 2026
3cf2413
fix(core): resolve runtime merge and guard value ir fallback
cukas Jun 8, 2026
cdcb60a
feat(core): add static class member runtime
cukas Jun 8, 2026
82c4581
feat(core): enforce constructor super discipline
cukas Jun 8, 2026
274396b
feat(core): enforce declared class shapes
cukas Jun 8, 2026
4decdf4
feat(core): expose class semantic facts
cukas Jun 8, 2026
6ea95d1
test(core): expand class object conformance
cukas Jun 8, 2026
eebf294
test(core): add object protocol negative conformance
cukas Jun 8, 2026
732a8f9
feat(core): add rag language contracts
cukas Jun 8, 2026
27eff2e
feat(core): bind mcp tools to rag contracts
cukas Jun 8, 2026
335225a
feat(core): add mcp resource rag ingress
cukas Jun 8, 2026
bc6240f
Merge remote-tracking branch 'origin/main' into feat/kern-core-runtime
cukas Jun 8, 2026
177c695
feat(core): add typed rag retrieval outputs
cukas Jun 8, 2026
922b18b
feat(core): add rag eval contract cases
cukas Jun 8, 2026
99422a5
feat(core): harden rag eval contracts
cukas Jun 8, 2026
0d7b5f3
feat(core): add in-memory rag runtime
cukas Jun 8, 2026
970bb84
feat(core): evaluate rag runtime contracts
cukas Jun 8, 2026
479a88c
feat(core): add rag runtime provenance
cukas Jun 8, 2026
c9e1737
feat(core): add rag answer contracts
cukas Jun 8, 2026
398bcac
feat(core): add rag answer contract surface
cukas Jun 8, 2026
3aa8813
test(core): add rag contract conformance fixtures
cukas Jun 8, 2026
7d287b7
feat(core): add declared shape validators
cukas Jun 9, 2026
2a2059a
feat(core): harden declared shape validators
cukas Jun 9, 2026
36db5f0
feat(core): expose effective class member facts
cukas Jun 9, 2026
fc06188
feat(core): add class implements conformance facts
cukas Jun 9, 2026
1891221
feat(core): add constructor discipline facts
cukas Jun 9, 2026
fb1febf
feat(core): enforce class implements at runtime
cukas Jun 9, 2026
4d85443
feat(core): enforce interface method protocols
cukas Jun 9, 2026
f02dd28
feat(core): enforce static interface member protocols
cukas Jun 9, 2026
863f9b8
fix(core): pin core contract element type to satisfy strict tsc
cukas Jun 9, 2026
04a1e06
fix(core): clear kern-guard findings in class semantic validator
cukas Jun 9, 2026
750fd93
feat(python): lower KERN classes to pure Python
cukas Jun 9, 2026
377a8ee
test(python): lock single-source class codegen
cukas Jun 9, 2026
a2459ec
feat(python): per-instance field defaults and static field values
cukas Jun 9, 2026
c0e7fe6
test(python): cover field defaults and super-ordering
cukas Jun 9, 2026
fcd4b7b
test(conformance): CI-enforce class TS<->Python parity
cukas Jun 9, 2026
399777a
Merge branch 'main' into feat/kern-core-runtime
cukas Jun 9, 2026
bba9db8
feat(python): static accessors via per-class metaclass (with chaining)
cukas Jun 9, 2026
9f8615d
test(python): static-accessor metaclass + inheritance conformance
cukas Jun 9, 2026
dfb8080
fix(python): reject static-accessor params that shadow the cls receiver
cukas Jun 9, 2026
bd884b5
feat(python): lower list push in class methods via shared list-ops
cukas Jun 9, 2026
b23f028
test(python): cover class-method push lowering + per-instance isolation
cukas Jun 9, 2026
6641114
feat(class): erase abstract + implements at codegen with symmetric fa…
cukas Jun 9, 2026
845c214
test(class): abstract + interface parity fixtures and codegen guards
cukas Jun 9, 2026
b8184b0
feat(class): inject implicit super() in derived constructors (KERN ct…
cukas Jun 9, 2026
e7c2510
test(class): derived-constructor implicit super() parity + injection …
cukas Jun 9, 2026
944c3cd
feat(class): reconcile derived-ctor super semantics across validator,…
cukas Jun 9, 2026
7ceb286
test(class): cover Option-C super reconciliation + canonical predicate
cukas Jun 9, 2026
f0d9d1c
fix(class): resolve effective base ctor transitively for implicit-sup…
cukas Jun 9, 2026
62855eb
fix(class): name the actual arg-requiring base in implicit-super diag…
cukas Jun 9, 2026
9e354a7
Merge origin/main into feat/kern-core-runtime
cukas Jun 9, 2026
844a7df
feat(class): enforce KERN's abstract-class contract in the validator
cukas Jun 9, 2026
763aa87
test(class): cover abstract-class contract enforcement
cukas Jun 9, 2026
43f1219
fix(class): scan field/param default= for abstract instantiation
cukas Jun 9, 2026
48ebd2e
fix(test): align bad-cases super fixture with Option-C semantics
cukas Jun 9, 2026
792dcda
test(python): add coercion differential conformance oracle (red at base)
cukas Jun 9, 2026
a569d11
feat(python): JS value→string coercion parity on native KERN bodies
cukas Jun 9, 2026
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
4 changes: 4 additions & 0 deletions examples/native-test/conformance-bad-cases.kern
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,10 @@ class name=PlainSuper

class name=ProtocolBase
field name=id type=string
constructor
param name=id type=string
handler
assign target="this.id" value="id"
method name=load returns=string
param name=id type=string
handler
Expand Down
2 changes: 1 addition & 1 deletion examples/native-test/conformance-bad-cases.test.kern
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ test name="Bad KERN conformance" target="./conformance-bad-cases.kern" coverage=
expect has=semanticViolations matches="declares more than one constructor"
expect has=semanticViolations matches="conflicting instance member 'value'"
expect has=semanticViolations matches="uses .*super.* does not extend a base class"
expect has=semanticViolations matches="constructor does not call .*super"
expect has=semanticViolations matches="omits .*super.* requires arguments"
expect has=semanticViolations matches="member access before .*super"
expect has=semanticViolations matches="calls .*super.* more than once"
expect has=semanticViolations matches="must call .*super.* definitely on every path"
Expand Down
33 changes: 30 additions & 3 deletions packages/core/src/codegen/type-system.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
*/

import { emitExpression } from '../codegen-expression.js';
import { hasDirectSuperCtorCall } from '../constructor-super.js';
import { propsOf } from '../node-props.js';
import { parseExpression } from '../parser-expression.js';
import { type IRNode, isExprObject } from '../types.js';
Expand Down Expand Up @@ -243,6 +244,17 @@ function emitSingletons(node: IRNode, lines: string[], className: string, exp: s
}

function emitClassBody(node: IRNode, lines: string[]): void {
// Abstract members — handler-less methods/getters/setters under an
// `abstract=true` class — emit a fail-fast `throw` body, identical to the
// Python `raise`, so an un-overridden abstract member fails the same way on
// both targets. The class-level `abstract` keyword stays (tsc still rejects
// `new X()`); only the member BODY is synthesized, since TS forbids a body on
// an `abstract` method.
const className = emitIdentifier(p(node).name as string | undefined, 'Unknown', node);
const isAbstractClass = p(node).abstract === 'true' || p(node).abstract === true;
const isHandlerless = (m: IRNode): boolean => firstChild(m, 'handler') === undefined;
const abstractThrow = (kind: string, memberName: string): string =>
`throw new Error("abstract ${kind} ${className}.${memberName} not implemented");`;
// Fields
for (const field of kids(node, 'field')) {
const fp = propsOf<'field'>(field);
Expand Down Expand Up @@ -288,6 +300,19 @@ function emitClassBody(node: IRNode, lines: string[]): void {
const ctorCode = classMemberBodyCode(ctorNode);
lines.push('');
lines.push(` constructor${generics}(${ctorParams}) {`);
// KERN constructor semantic: a DERIVED constructor that omits a direct
// super(...) call gets an implicit no-arg super() injected FIRST, so
// `this`/field access is legal (JS forbids touching `this` before super in a
// derived ctor). Mirrors the Python side's injected base-init; class-field
// initializers then run after super per JS semantics. The author writes
// explicit `super(args)` only to pass args up. The inject decision uses the
// canonical structural predicate (shared with the validator, runtime, and
// Python target) rather than scanning emitted text — a `super(` inside a
// string literal or comment no longer suppresses injection.
const isDerived = typeof p(node).extends === 'string' && p(node).extends !== '';
if (isDerived && !hasDirectSuperCtorCall(ctorNode)) {
lines.push(' super();');
}
if (ctorCode) {
for (const line of ctorCode.split('\n')) {
lines.push(` ${line}`);
Expand All @@ -310,7 +335,7 @@ function emitClassBody(node: IRNode, lines: string[]): void {
const staticKw = isStatic ? 'static ' : '';
const star = isStream || isGenerator ? '*' : '';
const asyncKw = isAsync || isStream ? 'async ' : '';
const mcode = methodBodyCode(method);
const mcode = isAbstractClass && isHandlerless(method) ? abstractThrow('method', mname) : methodBodyCode(method);

// stream=true → AsyncGenerator, generator=true → Generator/AsyncGenerator
// If user already declared full Generator<...>/AsyncGenerator<...>, use as-is
Expand Down Expand Up @@ -345,7 +370,8 @@ function emitClassBody(node: IRNode, lines: string[]): void {
const gvis = gp.private === 'true' || gp.private === true ? 'private ' : '';
const gstatic = gp.static === 'true' || gp.static === true ? 'static ' : '';
const greturns = gp.returns ? `: ${emitTypeAnnotation(gp.returns, 'unknown', getter)}` : '';
const gcode = classMemberBodyCode(getter);
const gcode =
isAbstractClass && isHandlerless(getter) ? abstractThrow('getter', gname) : classMemberBodyCode(getter);
lines.push('');
lines.push(` ${gvis}${gstatic}get ${gname}()${greturns} {`);
if (gcode) {
Expand All @@ -363,7 +389,8 @@ function emitClassBody(node: IRNode, lines: string[]): void {
const svis = sp.private === 'true' || sp.private === true ? 'private ' : '';
const sstatic = sp.static === 'true' || sp.static === true ? 'static ' : '';
const sparams = emitParamList(setter, { fallback: 'value: unknown' });
const scode = classMemberBodyCode(setter);
const scode =
isAbstractClass && isHandlerless(setter) ? abstractThrow('setter', sname) : classMemberBodyCode(setter);
lines.push('');
lines.push(` ${svis}${sstatic}set ${sname}(${sparams}) {`);
if (scode) {
Expand Down
120 changes: 120 additions & 0 deletions packages/core/src/constructor-super.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* Canonical constructor-super analysis — the SINGLE source of truth for the one
* question every KERN layer must answer the same way: does a constructor contain
* a direct `super(...)` constructor call?
*
* KERN's constructor semantic (Option C): a derived constructor MAY omit
* `super(...)`. When it does, KERN implicitly initializes the base first; when it
* writes an explicit `super(...)`, the author owns its placement and the strict
* discipline (no double/conditional super, no `this` before super) applies. The
* fork between those two modes is decided by exactly this predicate, and it MUST
* be decided identically by the semantic validator, the in-process core runtime,
* and BOTH codegen targets (TS + Python) — otherwise a program is legal in one
* layer and rejected/divergent in another (the precise bug this module exists to
* prevent). Previously each layer answered it differently: the validator walked
* the IR, while both codegens scanned EMITTED text (`/\bsuper\s*\(/` /
* `"super().__init__"`), which false-matched `super(` inside string literals and
* comments. One structural predicate, consumed everywhere, removes that drift.
*
* "Direct" mirrors the validator's long-standing rule precisely:
* - a `super(...)` call where the callee is the bare `super` identifier counts;
* - `super.method()` (a super MEMBER call) does NOT — it never initializes base;
* - a `super(...)` inside a lambda/arrow body does NOT — it never runs at
* construction time;
* - calls inside `if`/`else` branches DO count (the call is structurally present;
* whether it runs on every path is a separate discipline concern).
*/

import { parseExpression } from './parser-expression.js';
import type { IRNode } from './types.js';

// Props on a body statement whose value is an expression we must scan. Kept in
// sync with the validator's BODY_EXPRESSION_PROPS — a `super(...)` can appear in
// a `do value=...`, a `return value=...`, an `if cond=...`, etc.
const SUPER_SCAN_PROPS = [
'value',
'expr',
'target',
'cond',
'on',
'in',
'from',
'to',
'initial',
'source',
'sources',
'cleanup',
'min',
'max',
] as const;

/** True when `value` is the parser's wrapped-expression object `{__expr:true, code}`. */
function expressionCode(value: unknown): string | undefined {
if (typeof value === 'string') return value;
if (
typeof value === 'object' &&
value !== null &&
(value as { readonly __expr?: unknown }).__expr === true &&
typeof (value as { readonly code?: unknown }).code === 'string'
) {
return (value as { readonly code: string }).code;
}
if (typeof value === 'number' || typeof value === 'boolean') return String(value);
return undefined;
}

/**
* Structural recursion over a parsed expression looking for a direct `super(...)`
* constructor call. Equivalent to the validator's `valueIRCallsSuperConstructor`
* (super-ident callee => yes; lambda => stop, never descend; else recurse), but
* self-contained so this module depends only on the parser + node types.
*/
function valueContainsSuperCtorCall(value: unknown): boolean {
if (!value || typeof value !== 'object') return false;
const node = value as { kind?: string; callee?: { kind?: string; name?: string } };
// A lambda body that calls super never runs during construction — do not descend.
if (node.kind === 'lambda') return false;
if (node.kind === 'call' && node.callee?.kind === 'ident' && node.callee.name === 'super') {
return true;
}
for (const child of Object.values(value as Record<string, unknown>)) {
if (Array.isArray(child)) {
if (child.some(valueContainsSuperCtorCall)) return true;
} else if (child && typeof child === 'object') {
if (valueContainsSuperCtorCall(child)) return true;
}
}
return false;
}

/** The constructor's executable statements (handler body, minus params/decorators). */
function constructorBodyStatements(ctor: IRNode): readonly IRNode[] {
const handler = ctor.children?.find((child) => child.type === 'handler');
const body = handler ? (handler.children ?? []) : (ctor.children ?? []);
return body.filter((child) => child.type !== 'param' && child.type !== 'decorator');
}

/** Walk a statement subtree, stopping at a nested `class` (its super belongs to it). */
function statementContainsSuperCtorCall(node: IRNode, isRoot: boolean): boolean {
if (!isRoot && node.type === 'class') return false;
for (const prop of SUPER_SCAN_PROPS) {
const code = expressionCode(node.props?.[prop]);
if (code === undefined) continue;
try {
if (valueContainsSuperCtorCall(parseExpression(code))) return true;
} catch {
// Unparseable expression text can't be a structural super call — ignore.
}
}
return (node.children ?? []).some((child) => statementContainsSuperCtorCall(child, false));
}

/**
* Does this constructor contain a direct `super(...)` constructor call anywhere
* in its body (including inside `if`/`else` branches, but not inside lambdas or
* nested classes)? `true` => explicit-super mode (author owns placement, strict
* discipline applies). `false` => implicit-super mode (KERN injects base init).
*/
export function hasDirectSuperCtorCall(ctor: IRNode): boolean {
return constructorBodyStatements(ctor).some((stmt) => statementContainsSuperCtorCall(stmt, true));
}
29 changes: 26 additions & 3 deletions packages/core/src/core-runtime/index.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { hasDirectSuperCtorCall } from '../constructor-super.js';
import {
CORE_TYPE_CONTRACTS,
CoreContractEvaluationError,
Expand Down Expand Up @@ -1187,8 +1188,25 @@ function initializeClassLayer(
instance.initializedClasses.add(klass.name);
return;
}
if (base && !hasDirectSuperCtorCall(ctor)) {
// Implicit-super mode (KERN Option C): a derived constructor that omits a
// direct super(...) gets base init injected FIRST, then its own field
// defaults, then its body — identical to what both codegen targets emit, so
// the interpreter and generated TS/Python agree. The frame starts with
// superCalled=true so this/super access inside the body is unguarded; an
// unexpected late super(...) would still trip the double-init guard. The same
// `hasDirectSuperCtorCall` predicate decides this mode in the validator and
// both codegens, so all four layers classify the constructor identically.
initializeClassLayer(instance, base, [], false);
initializeClassFields(instance, klass);
withConstructionFrame(instance, klass, true, () => {
callClassMemberBody(ctor, klass, instance, receivesConstructorArgs ? args : []).value;
});
instance.initializedClasses.add(klass.name);
return;
}
if (base) {
withConstructionFrame(instance, klass, () => {
withConstructionFrame(instance, klass, false, () => {
callClassMemberBody(ctor, klass, instance, receivesConstructorArgs ? args : []).value;
});
} else {
Expand Down Expand Up @@ -1432,9 +1450,14 @@ function callSuperConstructor(value: KernSuperValue, args: readonly KernValue[])
return value.receiver;
}

function withConstructionFrame(instance: KernInstanceValue, ownerClass: KernClassValue, run: () => void): void {
function withConstructionFrame(
instance: KernInstanceValue,
ownerClass: KernClassValue,
initialSuperCalled: boolean,
run: () => void,
): void {
const stack = ACTIVE_CONSTRUCTORS.get(instance) ?? [];
const frame: RuntimeConstructionFrame = { ownerClass, superCalled: false };
const frame: RuntimeConstructionFrame = { ownerClass, superCalled: initialSuperCalled };
stack.push(frame);
ACTIVE_CONSTRUCTORS.set(instance, stack);
try {
Expand Down
3 changes: 3 additions & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,9 @@ export {
VALID_STRUCTURES,
VALID_TARGETS,
} from './config.js';
// Canonical constructor-super predicate — single source of truth shared by the
// validator, runtime, and both codegen targets (TS here + Python via this export).
export { hasDirectSuperCtorCall } from './constructor-super.js';
export type {
CoreFixture,
CoreFixtureError,
Expand Down
Loading