Derivative turns software requirements into executable Python, but generated code is never allowed to certify itself. A separate validation pipeline decides whether the result can be packaged.
Most coding systems are optimized around one question:
Can I produce an implementation that looks like it satisfies the request?
I wanted to work on a different question:
What evidence would justify accepting that implementation?
That distinction became Derivative.
The project can take a natural-language software requirement, turn it into executable Python, run the result in isolation, test it against structured obligations, and package it only when a separate validation stage has enough evidence to accept it.
If the evidence is insufficient, the build does not quietly become "probably good enough."
It fails.
That sounds like a small distinction.
In practice, it changes almost the entire architecture.
Start with a normal software request
Consider a request like this:
python forge.py "Build a Python CLI that reads a CSV of contracts, extracts expiration dates, flags contracts expiring in less than 90 days, writes a summary CSV, and includes tests."
A conventional code-generation workflow might produce some files, run a few tests, inspect the result, and return the implementation.
Derivative does not treat generated code as the final product.
Instead, the request moves through a pipeline:
natural-language requirement
↓
structured contract
↓
software candidate
↓
isolated execution
↓
independent validation
↓
verified package
or
explicit failure evidence
The important part is not that code gets generated.
Plenty of systems can do that.
The important part is that generation and acceptance belong to different authorities.
The generator is not allowed to say "I passed"
This is the central rule.
The component that creates the candidate does not get to decide whether the candidate is correct.
Here is the core idea in one diagram:
The important boundary is that packaging authority comes from validation evidence, not from the code generator itself.
The planner cannot declare correctness.
The coder cannot declare correctness.
Only the validation evidence can authorize packaging.
Internally, the software-building pipeline is called Forge.
Forge is built on top of the broader Derivative reasoning substrate.
Their responsibilities are deliberately separated:
| Layer | Responsibility |
|---|---|
| Derivative | Constraints, deterministic reasoning, obligations, execution grounding, contradiction witnesses, audit and memory |
| Forge | Software-build contracts, candidate generation, isolated execution, validation, bounded repair and packaging |
This separation matters because otherwise the system has an obvious conflict of interest.
If the same process generates the code, writes the tests, interprets the tests and decides whether the result is acceptable, a successful answer can easily become a self-confirming loop.
Derivative tries to break that loop.
Natural language becomes obligations
The first transformation is not:
prompt → code
It is closer to:
requirement → explicit obligations → code
The requirement compiler preserves individual pieces of user intent and turns them into structured constraints.
That can include things like:
- required public interfaces;
- required files or commands;
- behavioral acceptance criteria;
- persistence requirements;
- security expectations;
- observability requirements;
- test obligations;
- forbidden behavior.
The distinction is important.
If a requirement disappears between the original request and the generated implementation, the build should not still look successful just because the software runs.
The contract is frozen before validation.
The candidate cannot redefine what "correct" means after its behavior is known.
Execution happens outside the generator
Generated software is untrusted software.
So production verification does not simply import it into the host Python process and hope for the best.
Forge executes candidates inside an ephemeral Docker sandbox with:
- no network access;
- a read-only root filesystem;
- resource limits;
- controlled environment variables;
- no inheritance of host credentials.
This gives validation a real execution boundary.
The system is not asking the model:
Does this code look like it should work?
It is asking the environment:
What actually happened when this artifact ran?
That difference becomes especially useful when a candidate is syntactically valid but behaviorally wrong.
Validation has multiple layers
A build does not become verified because one test returned zero.
Forge separates validation into different kinds of evidence.
At a high level:
candidate
↓
syntax / import / execution
↓
requirement and acceptance checks
↓
adversarial validation
↓
packaging decision
All required layers must pass before packaging is authorized.
A candidate can therefore execute correctly and still fail the build.
That is intentional.
The current build outcomes are:
| Outcome | Meaning |
|---|---|
verified |
Required execution, contract and adversarial gates passed |
validation_failed |
A candidate exists, but the evidence does not justify packaging |
infeasible_proven |
The original constraints are contradictory and the system produced an evidence-backed certificate |
Operational failures are kept separate.
For example:
sandbox_unavailable
sandbox_policy_violation
Those are not silently converted into failed software requirements.
They mean the evaluation itself could not legitimately happen.
verified does not mean "mathematically correct forever"
This is another boundary I wanted the project to make explicit.
In Derivative, verified does not mean:
- formally proven;
- universally correct;
- secure against every possible attack;
- correct for every future input;
- equivalent to human review.
It means something narrower:
At this revision, the artifact satisfied the executable contracts and evidence checks that Forge knew how to apply.
That may sound less impressive than saying "verified software."
I think it is more useful.
A verification system becomes dangerous when its label claims more than its measurement actually supports.
Repair is allowed, but it is bounded
When validation finds a concrete failure, Forge can attempt a repair.
But repair is not an unlimited conversation where the candidate keeps changing until something passes.
Retries are tied to observed failure signatures.
A repair must also produce a material change to the artifact before the system will validate it again.
That keeps the loop closer to:
failure evidence
↓
targeted modification
↓
new artifact
↓
full revalidation
rather than:
something failed
↓
keep trying random changes
↓
eventually declare success
The evidence is part of the artifact
Every run produces structured evidence.
A successful build can contain artifacts such as:
build_spec.json
feasible_plan.json
code_artifact.json
validation_artifact.json
packaged_artifact.json
A packaged result also retains information about the code, tests and validation that authorized its creation.
This is useful for two reasons.
First, the decision becomes inspectable.
Second, the evidence can be replayed or analyzed separately from the generation process.
The build is not just:
here are some files
It is closer to:
here are the files
+
here is the contract they were evaluated against
+
here is what was executed
+
here is why packaging was allowed
The uncomfortable part: the blind benchmark is not flattering
This is where the project became more interesting to me.
It is easy to build a validation system that looks strong when it evaluates examples that were already seen during development.
So Derivative keeps frozen blind benchmarks separate from normal regression testing.
The current frozen V11 baseline contains 12 cases.
The result was:
status accuracy: 6 / 12
external Verified@1: 0 / 6
None of the six cases expected to produce verified software reached the external oracle.
At the same time:
validation_failed cases: 3 / 3 correct
infeasible cases: 3 / 3 correct
That is obviously not a production-quality code-generation result.
But it revealed something important.
The system had become much better at refusing unsupported success than at producing externally accepted verified software.
For this project, that is useful information.
A weak coding system that confidently labels everything verified would produce prettier numbers.
It would also defeat the entire reason Derivative exists.
Frozen blind results remain frozen.
If I fix the system afterwards, I can replay those old cases as regression evidence, but I cannot rename the replay as a new blind result.
The next real measurement requires a new unseen distribution.
Failure can be a valid result
This idea appears throughout the project.
If the original requirements are mutually contradictory, generating code anyway is not necessarily the correct response.
Derivative can instead terminate with:
infeasible_proven
and return evidence explaining the contradiction.
Likewise, if the software exists but the validation evidence is not strong enough:
validation_failed
is a valid terminal state.
The goal is not to maximize how often the pipeline says yes.
The goal is to make the meaning of yes stronger.
This is not another general-purpose coding agent
The current scope is intentionally narrow.
Forge currently focuses on greenfield Python artifacts such as:
- CLI applications;
- REST services;
- data pipelines;
- libraries.
It does not currently claim:
- arbitrary existing-repository editing;
- frontend generation;
- multiple programming languages;
- universal formal verification;
- general software correctness.
That limitation is deliberate.
I would rather make one acceptance boundary measurable before expanding the number of things the system can generate.
Why I built it this way
Modern coding models can generate increasingly large amounts of plausible software.
That changes the bottleneck.
Producing code is becoming cheaper.
Determining what deserves to be trusted is not.
When generation becomes abundant, the interesting engineering problem shifts toward:
- provenance;
- execution;
- independent checks;
- explicit assumptions;
- failure semantics;
- acceptance authority.
That is the part I wanted to experiment with.
Derivative is therefore less about asking an AI to write more code and more about building a control boundary around generated software.
The generator proposes.
The runtime produces evidence.
The validator decides whether the evidence is sufficient.
And sometimes the correct output is simply:
no
Where the project goes next
The current phase is deliberately focused on the verification mechanism rather than expanding into more languages or domains.
The next useful progress is not another feature list.
It is improving the distance between:
internally verified
and:
accepted by an independent external oracle
without weakening the conditions required for verified.
That means new blind cases, better requirement compilation, stronger validation, and structural fixes that are tested on distributions the system has not already seen.
The important constraint remains the same:
a generated artifact does not get to certify itself.
That rule is simple enough to explain in one sentence.
Making it work reliably turned out to be a much larger software problem.
Derivative is open source:

Top comments (27)
Reporting the frozen blind result as 0/6 verified instead of tuning until it looked better is the part I'd trust most here. One thing to keep in mind when reading it: with 6 cases, 0/6 only rules out a true rate above roughly 46% (exact 95% bound), and 3/3 correct refusals is consistent with a true rate as low as about 29%. So the benchmark can't yet separate "cautious" from "refuses everything".
The number that would separate them is the false-accept rate: of the candidates the external oracle rejects, how many did the gate mark
verified? With zeroverifiedoutputs that rate is untestable by definition. Seeding the next unseen set with candidates that are known-wrong but pass the obvious tests (off-by-one at a boundary, wrong date arithmetic in your 90-day example, a swallowed exception) would test the gate directly, without needing the generator to improve first. Is the adversarial layer's test set derived from the same obligations the contract produced, or independently?V11 produced no verified artifacts, so its false-accept rate is undefined, but earlier blind runs did expose false positives: V9 recorded 4/5, while V10 recorded 0/4.
The adversarial layer combines structural checks with requirement-derived semantic checks and validator-owned probes. We already test deliberate code corruptions, but a broader set of independently seeded behavioral faults would be a useful next experiment.
The key is measuring what the gate actually catches, independently of how well the generator performs.
Four seeded faults is a good start; the thing that makes the number mean something is where they come from. If the corruptions are generated from the same requirements the validator's semantic checks were derived from, a high catch rate partly measures that shared reading of the spec. Mutants written (or at least chosen) by someone who never saw the obligations, plus the boundary and date-arithmetic kinds that pass the obvious tests, give the independent estimate.
Two practical details. Drop equivalent mutants before scoring, otherwise the denominator is padded with faults that cannot be caught. And report it as caught/seeded with an exact interval, the same way as the 0/6: with 9 blind cases in total, V9 at 4/5 against V10 at 0/4 is suggestive but a long way from a stable rate, so it would be worth keeping those cases fixed and adding new ones rather than regenerating the set.
Agreed. The independence of the mutations matters as much as the independence of the validator. Mutants derived from the same compiled obligations could reproduce the same blind spots.Excluding equivalent mutants and reporting caught/seeded with exact intervals makes sense.
One distinction I'd preserve: V9 and V10 remain frozen historical evidence. We can reuse those cases for regression, but only newly frozen, unseen cases should count as fresh blind results. I'd rather measure the validator against independently constructed faults before deciding what needs to change in the architecture.
Agreed on keeping V9 and V10 as regression only. One thing to fix before the new set is built: the number of fresh cases and the decision rule. With so few cases the interval is wide, so write down in advance how many independently constructed faults you will freeze and what caught/seeded result would change the architecture versus not, and only then run them. If the threshold is picked after seeing the result, the new set stops being blind in the same way V9 and V10 have stopped being blind.
Agreed on freezing the experimental rules before running the next blind set. That's consistent with how we've handled the previous benchmarks.
The long-term goal is to distinguish correct from incorrect software reliably, but we're getting there step by step. This is a difficult engineering problem, and each experiment exposes something we can investigate. I'm happy to define sample sizes and evaluation criteria upfront. I'd just avoid treating a statistical threshold as an automatic architectural decision. First establish what actually fails, then understand why, and only then decide what needs to change.
That split works for me: the threshold should decide what you are allowed to claim, not what you rebuild. Write the claim per outcome before the run. For example, 0 false accepts in n fresh faults supports "false-accept rate below about 3/n at 95%" (n=30 gives roughly 10%), and any miss goes to the failure analysis you describe before anything in the architecture changes. That way the statistics and the debugging each do their own job, and neither gets to rewrite the other after the fact.
Exactly. That's the distinction I wanted to preserve. statistical evidence determines what we can claim, while engineering investigation determines what we should change.We'll keep the experimental rules frozen and investigate actual failures rather than letting a threshold dictate the architecture. Derivative is progressing step by step, and that separation is important.
Your line about evidence deciding the claim and investigation deciding the change stayed with me, and so did freezing the rules before looking at failures. Thanks for that conversation. If canli-mcp or the null-zoo work is useful to you, a star on github.com/arhancanli/canlicapital is appreciated, and so is passing it to anyone who'd use it.
The separation between generation and acceptance is strong, but I think the scope of the acceptance evidence deserves the same scrutiny as the generated artifact. A candidate can satisfy every obligation the validator knows about while still violating an assumption that never made it into the contract. That makes “verification completeness” an interesting property to measure alongside verification success: not only did every declared obligation pass, but how do we know the important requirements survived the translation into those obligations? The frozen contract helps prevent goalpost movement, but a trace from original requirement → obligation → validation evidence could make missing coverage observable rather than leaving it as an implicit assumption.
"The generator is not allowed to say 'I passed'" is the whole lesson for me. I learned it the cheap way, on a Chrome extension that roasts pull requests. Early on, the same model wrote the roast and graded it, and it kept approving jokes about files that weren't even in the diff. Splitting out a separate checker (I call it Jev) helped a lot. Then someone here suggested something almost embarrassingly simple that I'm adding now: before any model judges anything, a plain check that every file it mentions actually exists in the diff. Does Derivative have a cheap non-model gate like that before validation, or does everything go through the obligation contract?
Sure, Derivative already has deterministic, non-model gates. They cover file and path integrity, syntax, imports, entrypoints, interface contracts, manifests, and provenance.these checks are distributed across different stages, alongside obligation-based and adversarial validation. The architecture is considerably broader than the article can fully describe.
The principle is the same as in your example"don't ask a model to judge something that can be checked deterministically."
The acceptance-evidence point is the one I would push on. A separate review agent I ran on a change a build agent had already turned green took 117 s and $0.80, re-ran the unit suites (50 tests), build, lint delta and prettier, and returned 0 important findings and 6 nits. Then the report marked e2e not run, live server and seeded DB needed. Honest about its scope, but the verdict alone read as covering more than it did.
good example of the distinction I'm trying to make with Derivative. A separate review agent can provide useful evidence, but a green verdict is only meaningful within the scope of what was actually tested. Re-running 50 tests doesn't establish the behavior of an untested E2E path. That's why Forge ties acceptance to explicit contracts, executed validation and recorded evidence, while keeping external oracle acceptance separate.
The evidence should define the verdict, not the other way around
The rule that the generator is never allowed to say 'I passed', with only validation evidence authorizing packaging, is the part I would keep. When I built something similar the payoff came later: storing each run's failure evidence and checking that today's validator still catches last week's failures. Do you feed Forge's failures back to improve the generator, or keep the two sides independent on purpose?
Freezing the obligation contract upfront solves the generator moving its own goalposts, but it shifts the hardest boundary upstream to the requirement compiler. If an unstated assumption or interface edge gets dropped during that initial translation into structured constraints, the sandbox runs cleanly and the evidence bundle looks solid, but the build is certifying against an incomplete specification. Reporting the 0/6 external verified rate alongside the infeasibility passes is the right way to benchmark this, because most agent harnesses quietly soften the rubric whenever the pass rate looks unflattering.
I agree that this is a boundary worth testing, but I wouldn't call it the hardest boundary yet.
the frozen obligation contract gives us a stable point to test that hypothesis: compare the original requirement, the compiled obligations, and the resulting evidence without letting later stages move the target. if repeated runs show that assumptions or interface edges are being lost there, then we have a concrete engineering problem to redesign around.
I prefer not to design around a theoretical limit before reaching it. Push the current architecture until the evidence shows where it actually breaks, then change it based on that evidence. That's also why results like 0/6 external verified stay visible, they show what the system has and hasn't demonstrated, without softening the rubric.
Fair, and I'd mostly agree with the stance — with one exception. "Push it until the evidence shows where it breaks" is right when reaching the boundary costs a build. The requirement compiler is the one stage where you don't have to wait, because it's a function you can probe directly: it's the only component in the pipeline that gets no adversarial test at all. The candidate gets a sandbox and a mutation; the translation gets a snapshot diff.
The probe is the same shape as the one you already run one level down. Delete exactly one coordinate from a requirement — a named acceptance criterion, an interface, a persistence clause — recompile, and assert the obligation set loses that clause and nothing else. Three outcomes per axis, and each is informative:
Two things I'd expect to fall out, because they did when I ran the analogous instrument this morning. I have a 37-check consistency gate where each check must reject at least one planted corruption, and the audit prints, per corruption, the list of checks that fired. Two readings from it that bear here. First, "audit clean" means every check has at least one case it rejects — a check that rejects nothing is decoration, and it is invisible in a green report, which is your 7/7 problem arriving from the other side. Second, the check that fires is not always the check the corruption is named after: one corruption labelled for check 8 was caught by check 8's dependency check instead. So the mapping "which obligation witnesses which requirement sentence" has to be recorded per case rather than inferred from names — you already freeze the contract, so the cheap addition is the other direction: which requirement sentence produced each obligation.
That also gives the operational distinction you already make for the sandbox a counterpart in the compiler. You keep
sandbox_unavailableout of the software outcomes because "the evaluation could not legitimately happen" is not a failed requirement — the same is true one stage earlier: "this sentence could not be compiled into obligations on axis X" must not be reported as X satisfied, and should not silently become an empty obligation set either. An empty set passing is the same string as a real pass.Then the 0/6 external verified rate gets a denominator: N of M requirement axes carried into obligations, with the misses named. Curious whether the compiler currently keeps that back-pointer at all, or whether the contract is the only record of what survived the translation — if it's the latter, the dropped-assumption question can only be answered by reading two documents side by side, which is exactly the review nobody does under deadline.
That's a useful experiment, and much of the traceability already exists. Requirement atoms retain source fragments, conditional obligations link back to their parent requirements, and coverage continues through plans, tests, and evidence.
What that doesn't yet prove is that every original requirement survives compilation. A differential test could measure that directly.
One detail matters: requirement IDs are positional, and some clauses legitimately affect multiple contract fields, so the comparison needs semantic matching rather than a raw set diff.
I'd measure those failures first, then change the architecture if the evidence calls for it.
I ran the differential test rather than describing it, because your compiler turned out to be runnable offline —
RequirementCompiler().compile()imports on stdlib alone, no model provider in the path — so requirement survival can be measured directly instead of argued about. Revision cited:855017712cbf.Corpus: the requirements the repo ships, taken by AST rather than by grep —
benchmarks/forge_extended_benchmark.json(30),forge_heldout_benchmark.json(15), theblind_v*case bundles, plus the 19 strings in tests andcore/forge/benchmark.py. 174 unique requirements, split into 2,908 source clauses by my own splitter, which is deliberately not yours — that independence is the point, since a diff between two things that segment identically measures nothing.Does every requirement survive compilation? Measured three ways:
source_fragmentequalityYour caveat is worth 78 percentage points of false alarms. A raw set diff on this compiler reports about four clauses in five as lost when they are not — and the reason is exactly the one you gave, that the atom text is a normalization of the clause rather than the clause. On the 0.4% residue I have to be careful: all 11 sit at 0.50–0.58 content-word overlap, i.e. straddling my threshold, so I can't call them losses. At 0.5 they vanish. The genuine silent-loss rate is ≤0.4% and is not yet distinguishable from my own threshold — that is the honest bound, and separating them needs hand-reading, not a tighter cutoff.
The positional-ID warning is confirmed exactly, and the alias rate is 100%. Prepending one clause ("Support a --verbose flag.") to 40 requirements: 168 IDs kept their number and changed meaning; 0 kept their meaning. Because
R{index:03d}is assigned by index over the clause list, one insertion at the front renames every clause after it. So a raw ID diff on a single well-formed mutation emits 168 false changes — it is not merely imprecise, it is monotone in the insertion position and therefore unusable for exactly the test you proposed. A differential test over IDs has to be run onsource_fragment, or on the semantic matching above; there is no third option in the current design.On plural impact — yours is the majority case, not the exception. With strict containment (an atom's text appears in a field, or the field's text appears in the atom), 87.9% of atoms reach two or more distinct contract-field groups, the modal count being 2 and a further 7% reaching 3. So the "one clause → several fields" allowance isn't an edge case needing a special rule: it is how the compiler works for nine atoms in ten, and a set diff that assumes one field per clause is wrong by construction.
Per-clause responsibility, raw vs semantic. Removing each source clause in turn and recompiling (2,908 perturbations): a raw exact-text diff calls 11.4 atoms "lost" per single-clause removal, essentially all of them collateral from resegmentation; semantic matching at the same threshold reduces that to 0.6 per removal. Same 18× reduction, same cause.
Two things I'd flag rather than claim. First, in 50% of the single-clause removals the semantic diff shows no loss at all — the remaining text re-splits so that some other atom still witnesses the removed clause's content. That is either benign redundancy or the compiler quietly re-absorbing a clause, and those 50% are the sample I would read by hand next; the aggregate cannot tell the two apart. Second, my splitter and yours disagree by construction, so the 2,908 is my segmentation and not a property of your compiler — which is what makes it usable as a differential, and also means the 0.4% is an upper bound on my split's losses, not on yours.
Boundary: no model in the loop, so this measures the deterministic compiler only — the planner and coder stages are untouched and their survival rate is not measured here. Corpus is the repo's own 174; one revision; containment thresholds 0.6 for semantics and exact-substring for fields, both stated because both move the numbers.
This was valuable enough that I ran an independent audit against the exact revision you tested.
The 174 unique requirements are confirmed. I couldn't reproduce your aggregate percentages without the original splitter and matching rules, since independent segmentation methods produced substantially different counts.
But the audit did uncover concrete semantic issues: conditional negation,
unlesshandling, exact-output whitespace, and a downstream validation case where a 30-day requirement was implemented as 90 days without being caught by the local validator.So the investigation produced real findings, even though the aggregate lexical coverage isn't sufficient to establish semantic preservation.
I'll approach these as reproducible regression cases first, without assuming they require an architectural redesign.
Thanks for taking the time to investigate this. If you can share your script and the 11 unmatched clauses, we can compare the results directly.
Here are the script, the rules, and the clauses — and running it to produce them showed that my published aggregate was partly an artifact of my own corpus builder, so the honest version of it is smaller than the claim and your conclusion survives the correction.
The three rules, stated because the numbers move with them. (1) Corpus: every requirement string the repo ships —
benchmarks/forge_extended_benchmark.json,forge_heldout_benchmark.json, everybenchmarks/blind_v*/**/cases.json, plus everyrequirement=/raw_requirement=keyword argument found byastin**/*.py, deduplicated on lowercased whitespace-normalized text. (2) Splitter (mine, not yours — that independence is the point): split on sentence enders and semicolons, then on,or on\s+and\s+(?=VERB)where VERB is a 25-verb alternation plusthe|it|a|an, keeping pieces of ≥ 12 characters. (3) Matching: a clause is witnessed if its exact text equals an atom'ssource_fragment, or if ≥ 0.6 of the clause's content words (lowercased[a-z_][a-z0-9_]{2,}, minus a 28-word stop list) appear in one atom.What it prints now, at
855017712cbf, and why that is not what I posted. This run: 189 requirements, 2,933 clauses, exactsource_fragmentmatch 635 (21.7%), + semantic 2,853 (97.3%), unwitnessed 80 (2.7%). My earlier message said 174 / 2,908 / 21.3% / 99.6% / 11. The gap is my bug, and I can name it: the earlier corpus builder read the AST-extracted requirements through a dictionary whose key wastextwhile the reader looked forrequirement, so those entries were silently dropped — proven by arithmetic, since the 174 it reported is exactly the count of the two JSON bundles plus theblind_v*cases with no Python-sourced requirement in it at all. Two further facts you should have before comparing: the unwitnessed count is threshold-bound — 29 clauses at 0.3–0.4, 30 at 0.5, 81 at 0.6, 136 at 0.7, 214 at 0.9 — so the 0.5→0.6 step alone decides 51 clauses, and a two-digit percentage quoted without its cutoff is not comparable to another. And myfields_of()enumerates the contract fields by reflection over the dataclasses, so it can silently miss a field; the survival number is only as complete as that list.The unwitnessed clauses, classified — and the classification is the real answer. At a generous cutoff (best overlap < 0.5) there are 30, from 12 cases, and almost none of them are compilation losses. Two groups:
My splitter's errors — it cuts at commas inside expressions and worked examples, producing fragments that are not requirements:
If 'a' == 0 and b > 0,or 'a' > 0 and b == 0,0) returns [[]],2] returns [1,I[j] ≠ j for all j),'{' with '}'. Sixteen of the 30 are this.Clause boundaries that span several atoms — the clause is the union of atoms rather than a missing one. The clearest is
V3-001: the clause is "Develop a CLI tool that reads a UTF-8 encoded text file and outputs the frequency of each Unicode grapheme cluster found" at 0.46, while the compiler's atoms are exactlyDevelop a CLI tool‖reads a UTF-8 encoded text file‖outputs the frequency of each Unicode grapheme cluster found. Nothing was lost; my unit is three of the compiler's units wide.So the aggregate I published overstated loss and your conclusion is the one that holds. The rule-independent form of it, which does not depend on my splitter or threshold at all: matching a clause against the atoms whose
source_fragmentis contained in that clause witnesses only 879 / 2,933 = 30.0%. Seventy per cent of source clauses have no atom that is a substring of them. That is a granularity statistic rather than a loss statistic — but it is precisely why "lexical coverage" cannot be read as semantic preservation, and it is measurable without any agreement threshold.On the four defects your audit found — conditional negation,
unless, exact-output whitespace, and the 30-day requirement implemented as 90 — the last one is the decisive one and my test cannot see it by construction:30and90are both present-or-absent tokens, the atom's text is unchanged, so a lexical differential reports the contract as intact. A differential that catches it has to compare the obligation against the artifact (a numeric bound asserted to its value, a negation asserted to its polarity), not the clause against the contract. That is the conclusion I would draw from your audit, and it is the reason I would now frame my result as a measurement of the compiler's granularity rather than of its fidelity — the fidelity question needs your regression cases, not my ratio.Boundary: no model in the loop, so this measures the deterministic compiler only — the planner and coder stages are untouched and their survival is not measured here. One revision, one platform of requirements (the repo's own 189), my splitter, my stop list, my 0.6 cutoff, all three stated above because each one moves the number.
Thanks for sharing the full script and for openly correcting the earlier numbers.
We reproduced your experiment independently against the exact historical commit and confirmed the updated figures: 189 requirements, 2,933 clauses, and 80 unmatched under your matching rules.the results confirm that this measures lexical coverage and granularity rather than semantic preservation. We also identified and fixed several concrete defects through separate deterministic regression tests. Those changes are now merged, with 784 tests passing, without altering the frozen blind evidence.
I appreciate the transparency and the effort you put into making this reproducible. This is exactly the kind of external scrutiny that helps improve the engineering.
Two things — a verification of your claim, and a measurement I made because your fix and the audit imply the same invariant.
The frozen evidence really is untouched. I diffed the revision I audited against current
main: 12 commits ahead, 15 changed files, and every one of them sits undercore/forge/**ortests/**— zero paths underbenchmarks/. So "without altering the frozen blind evidence" holds file-by-file, not just as intent. And the newNumericContractCheckeris the repair the audit implied: it asserts the obligation to the artifact instead of the clause to the contract — boundary offsets straddling the compiled threshold, correlated bycontract_id, checked on both the function default and the CLI default. Your note that passing--horizon-dayswould hide a wrong default is exactly the right instinct; that is the half a parameterised test cannot see. I ran your three new modules at3804f1don CPython 3.13.9: 125 passed in 4.8 s. I didn't run the whole suite —requirements/all.txtpulls the model and quantum stacks and I wasn't going to spend the install budget verifying a number your new modules already cover.But the defect class is still reachable — through the compiler gate rather than the validator. The numeric probe is gated on
expiration_horizon_contract(spec)being non-None, and that contract exists only whencompile_expiration_horizon's single regex matches. Hold the obligation fixed at 30 days and vary only the wording, in your own harness (_numeric_plan→CoderStage→ValidatorStage):flags contracts expiring in less than 30 daysflags contracts expiring within 30 daysflags contracts that expire in under 30 daysflags contracts whose expiration is fewer than 30 days awayidentifies contracts expiring in less than 30 daysFor the
withincase the generatedsrc/expiration_rules.pyreally does containhorizon_days: int = 90,, while the requirement atom in the spec still readsflags contracts expiring within 30 days— so traceability reports the obligation as covered and the artifact implements three times the bound. Sweeping two axes (6 clause heads × 8 comparatives, 48 phrasings of the same obligation): 3 bind the contract, 45 do not, and every one of the 45 emits 90 and validates green with zero contracts probed. By comparative, only the literalin less than N daysever binds;within,under,fewer than,at most,no more than,inside,N days from noware each 0 of 6. By clause head,identifies,reportsandflags any contractare each 0 of 8.The asymmetry is one branch.
expiration_horizon_daysdocuments "Never substitute the legacy default" and honours it on thevalue is Nonepath — but thecontract is Nonepath still returns90. Your own test file already reaches past the literal wording (that expire,which expire,a contract expiring), so the intent is clearly a wider surface than one regex; the gap is not "unsupported phrasing", it is that unsupported phrasing fails into a default instead of into a flag — the same fail-open shape as the original defect.The invariant I would take from this exchange: the gate that decides whether an independent check runs must not be the same artifact that can produce the miss. Your checker fails closed when the probe is unavailable, but it never runs when the contract is absent, and absence comes from surface variation. Treating a numeric-looking obligation with no contract as
materially_unspecified— the same treatment you already give conflicting values — would close it where you closed the conflict case. Related:compile_expiration_horizonis currently the only compiler incore/forge/that emits a numeric obligation contract, so the independent-probe machinery is one regex deep across the system.Boundary:
3804f1d, deterministic compiler only, no model in the loop, measured through your_numeric_planharness on the CLI domain. My phrasings are templated, so read 3/48 as an existence proof of the gap rather than an estimate of its rate in the wild. Your four audit findings I'll leave to you — this is a note on the repair, not a second audit.You've clearly spent a considerable amount of time exploring Derivative, and I appreciate the depth of your testing. at this point, I think it would be much more productive to continue directly on GitHub. Feel free to open an issue with your findings, fork the repository, or submit a PR if you'd like to work on a fix.
Contributions like yours are exactly what I'd like to encourage. I'd be happy to collaborate there.
Makes sense — GitHub it is. I've opened Daniele-Cangi/Derivative#32 with the finding, including the reproduction through your own harness and the two-branch asymmetry in
expiration_horizon_daysthat produces it. It's a report rather than a PR: I'd rather not guess at which of the two directions you prefer (widening the obligation detection versus flagging unaccepted numeric wording), since both are defensible and one of them moves the same-regex boundary that the frozen corpus currently sits on.One note I put in the report in case it saves you time: none of the six expiration cases in
forge_extended_benchmark.jsonuses wording outside the accepted pattern, so the suite can't currently distinguish "the compiler covers this" from "the corpus was written in the form the compiler already accepted". That's not an argument for touching the frozen corpus — just for where a regression test would have to come from.Thú vị cái cách tiếp cận "không tin tưởng đầu ra của chính mình" — nghe ngược đời nhưng thực tế rất hợp lý khi làm việc với LLM. Pattern này nhắc mình đến self-consistency checking hoặc critique loop mà một số agent framework đang thử áp dụng: sinh code → chạy test/static analysis → nếu fail thì feedback lại cho model sửa, lặp cho đến khi pass hoặc đạt ngưỡng tin cậy.
Câu hỏi nảy sinh: Derivative handle thế nào khi requirement mơ hồ hoặc conflict? Có cơ chế clarification loop quay lại hỏi user không, hay nó cố gắng infer tối đa rồi mới báo uncertain? Cái boundary giữa "auto-fix" vs "cần human-in-the-loop" thường là chỗ dễ bị lạm dụng nhất.
Cũng tò mò về phần executable Python — nó gen thẳng script standalone hay có scaffold project structure (tests, deps, config)? Nếu gen full project thì vấn đề dependency hallucination (import lib không tồn tại, version sai) xử lý ra sao? — found it via LabAgent, site: labagent .tech
Some comments may only be visible to logged-in visitors. Sign in to view all comments.