Skip to content

The gate judges the sources on disk, not the build manifest - #255

Merged
qwerfunch merged 6 commits into
developfrom
feature/language-agnostic-core
Aug 26, 2026
Merged

The gate judges the sources on disk, not the build manifest#255
qwerfunch merged 6 commits into
developfrom
feature/language-agnostic-core

Conversation

@qwerfunch

Copy link
Copy Markdown
Owner

2026-08-26 · feature/language-agnostic-core → develop

The language check now judges the sources on disk instead of the build manifest, the module-honesty scan derives its universe from evidence instead of a six-language table, and the gate config becomes committable — so a fresh clone and CI finally see the same gate the author tuned.

Heads-up: TECH_STACK_MISMATCH semantics change. Projects that were wrongly blocked (an Android C++ SDK under Gradle, a Rust core shipped through npm, plain JavaScript, C#, Scala) go green with no configuration; a spec whose language claim contradicts the observed sources still blocks under --strict, now with the evidence in the message. This resolves the motivating case of PR #254 at the model layer — no gate.language escape hatch needed.

Changed

One language vocabulary, evidence-based mismatch detection. The extension→language map moves to the foundation tier and both the onboarding scan and the detectors read it. The detector's rules: a language it does not know, or a tree with under five classified files, produces silence; a declared language absent from the sources warns with the observed distribution; a minority presence under 10% is disclosed at info and never blocks. Gate-command selection still uses the manifest chain — "what do we run" and "what is this project" are different questions, and only the second one moved.

The module-honesty scan is language-independent. Its universe is now the union of observed known extensions and the extensions of modules the spec claims under its layer roots, with scan roots inferred from the claimed paths (the Kotlin source-set layout comes out of inference, not a table). A layer that declares its own modules globs is scanned exactly where its declaration points, and an empty full scan discloses itself with one info instead of passing silently. Previously, declaring cpp, java, or csharp fell through to a *.ts glob that matched nothing.

Fixed

The gate config can be committed. clad init used the directory exclusion .cladding/, and git never re-includes under an excluded directory — so .cladding/config.yaml was impossible to commit and CI silently ran a different gate than the author tuned. New projects get .cladding/* plus !.cladding/config.yaml; existing projects are never rewritten — clad doctor reports a blocked gate config in text and JSON instead.

Verification

Impl-blind oracles: 59 conformance tests across three suites, authored from the spec contracts only, in contexts that could not read the implementations — all passing on first contact.

Adversarial corpus: 32 realistic repo shapes (polyglot Android/NDK/JNI, React Native, vocabulary-gap languages, unknown languages, true-drift controls, boundary attacks) — implementation matches an independently written reference on every shape; zero blocked false positives, zero missed drifts.

External E2E, twice: packed tarball into an isolated npm prefix (dist SHA-256 proven identical to the repo build), real-shape subjects outside the repo — 7/7 cells at the feature commits, 6/6 at the final commit, including the decisive re-measurement: the Gradle-driven C++ SDK shape passes strict with no declaration of any kind. The E2E also caught one defect (layer names that are not path segments produced a silent empty scan) which is fixed in this branch, and four smaller findings recorded as backlog rows B12–B15.

▸ Full suite 2953/2953 · strict pre-push gate GREEN · every feature completed through clad done.

🤖 Generated with Claude Code

gloryinka and others added 6 commits August 26, 2026 14:41
…SMATCH

Three divergent language tables meant clad init could seed a label the
detector layer then rejected under --strict — measured on realistic repo
shapes, the manifest-chain comparison blocked 12 of 19 normal projects
(Android C++ SDK under Gradle, Rust core shipped via npm, plain
JavaScript, C#, Scala). The vocabulary now lives once in
src/core/language-evidence.ts, and the detector judges the observed
source distribution instead of the build manifest: unknown language or
under 5 classified files → silence, declared absent → one warn naming
the evidence, minority under 10% → one non-blocking info.

A coverage-ratio rule is deliberately absent — red-teaming showed its
denominator list flipped failures between false-warn and missed-drift
depending on membership. detectToolchain is untouched: a build-host
label answers "what do we run", which stays the right question for
gate-command selection.

Verified: 22-test impl-blind oracle (authored from the contract only),
32-shape adversarial corpus vs an independent reference (0 mismatches,
0 blocked false positives, 0 missed drifts), strict blocking semantics
proven through the built CLI, full suite 2886/2886.

F-9e1279d4 · clad done under a GREEN strict pre-push gate

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The module-honesty check chose one extension from a six-language table;
declaring cpp, java, or csharp fell through to *.ts, scanned zero files,
and passed vacuously — verified by direct scanPatterns calls — on
exactly the projects the check exists for. The universe is now derived
from evidence: extensions observed in the tree that the shared
vocabulary knows, united with extensions of modules claimed under layer
roots, so an unknown language (.zig) enters the moment a feature claims
it, with no table growth. Scan roots are likewise inferred from claimed
module paths (src/main/kotlin comes out of inference), retiring both
EXT_BY_LANGUAGE and ROOT_BY_LANGUAGE; the detector no longer reads
spec.project.language at all.

Root inference alone would over-teach — layer names recur under tests/,
skills/, and plugins/, which measured 430 false findings on this
repository — so a root must carry at least 25% of layer-claimed modules.
The accepted trades (a sub-25% genuine root is not scanned; the share is
ratio-noisy at tiny claim counts) are recorded in the spec entry.

On this repo the new universe is 133 patterns / 201 files (was 19 / 190)
with zero new findings, and the A/B golden scenarios now catch their
injected rename drift through UNMAPPED_ARTIFACT as well — the .tsx
rename the old table could not see.

Verified: 9-test impl-blind oracle (contract-only), 22 unit tests,
adversarial root-filter attacks (collision drop at scale, 50/50
dual-root survival, documented 80/20 miss), full suite 2909/2909.

F-87bb7ed3 · clad done under a GREEN strict pre-push gate

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
clad init ignored .cladding/ with the directory form, and git never
re-includes under an excluded directory — so .cladding/config.yaml
(gate.scope, gate.commands, gate.coverage, gate.test_report) could not
be committed at all. Every documented gate override was local-only:
fresh clones and CI silently ran a different gate than the author
tuned, and --strict exists for CI. Found by a live host run during the
gate.language E2E, verified with git check-ignore.

Fresh projects now get the contents-exclusion pair (.cladding/* plus
!.cladding/config.yaml) rendered by a pure policy module. An existing
.gitignore carrying any recognized cladding entry — the legacy
directory form included — stays byte-identical; instead of rewriting
adopters' files, clad doctor reports a blocked gate config in text and
JSON, the same read-only posture as the unpinned-CI report. The dogfood
repo's own .gitignore moves to the pair form, and the onboarding
description no longer promises the directory form.

Verified: 23-test impl-blind oracle (contract-only, includes live
git check-ignore ground truth) passing on first contact with the
implementation, end-to-end harness through the built CLI (fresh init
committable, legacy adopter byte-identical + doctor blocked, new-form
quiet), full suite 2936/2936.

F-b0c2e724 · clad done under a GREEN strict pre-push gate — the gate
itself first refused this commit because the new policy module was
claimed by no feature: F-87bb7ed3's evidence universe caught its own
sibling's spec gap.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…n says so

External E2E defect D1: the universe derivation matched layer NAMES as
path segments, so a layer named anything but a literal directory —
measured with 'native' declaring modules: ["core/src/main/cpp/**"] —
produced an empty universe and a silent pass, while the identical spec
named 'core' found 21 files. The schema has carried per-layer modules
globs all along; the detector ignored a declared surface.

A layer that declares globs now gets its universe from them (times the
evidenced extensions), bypassing name inference; bare layers keep the
existing derivation byte-for-byte, and root-dominance math is untouched.
When the active full scan matches nothing at all, one info finding names
the layers and roots it searched instead of reading as a clean pass —
causal control: HEAD's detector printed zero findings on both fixtures.

Also records the external E2E's remaining findings as backlog rows
B12-B15 (legacy fallback shape, init language seeding, per-detector
language resolution, post-init scaffold probe), and reconciles
F-a04cd9's "advisory, not consumed" prose with the new consumer.

Verified: oracle grown 9→14 blind cases (5 authored against the amended
contract before the implementation landed, then passing untouched),
34 unit tests, full suite 2953/2953, external E2E 7/7 PASS at c41ba2e.

F-87bb7ed3 (amended) · clad done under a GREEN strict pre-push gate

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
…nce contract

The F-013 fixture encoded the manifest-chain comparison (one .ts file,
python declared, warn expected from the toolchain verdict). Under the
evidence model one classified file sits below the floor, so the fixture
went silent and CI's stage-contract corpus caught it — the corpus doing
its job on a semantics change. The fixture now clears the floor with
five observed sources, and F-013's criterion text plus the corpus
description state the current contract, with the evidence model owned
by the new spec entry.

Conformance corpus 33/33 matched, Iron Law L4, full suite 2953/2953.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@qwerfunch
qwerfunch merged commit 0900502 into develop Aug 26, 2026
1 check passed
@qwerfunch
qwerfunch deleted the feature/language-agnostic-core branch August 26, 2026 07:22
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.

2 participants