Source Code Is Becoming a Build Artifact AI is shifting software engineering from hand-written source code to requirements-driven generation, making source code a build artifact produced by tools. This transition mirrors the shift from assembly to high-level languages like FORTRAN, where compiler quality was the key to acceptance. The challenge for AI-generated code is ensuring it can be trusted without relying on the generator's own assessment. Source Code Is Becoming a Build Artifact Engineers have programmed systems through physical configuration, assembly code and languages such as C, C++ and Rust. Each higher layer delegated more translation to tools. In disciplined embedded development, requirements already guide the human implementation. AI may now change which layer engineers author directly: precise requirements, controlled design inputs and acceptance criteria can become the reproducible basis for generated code - but only if the result can be accepted without trusting the generator's own verdict. There is a pattern in the history of software that becomes easy to overlook once a new abstraction has become normal. Engineers move the point at which they describe a system. The lower layer does not disappear; it becomes something a tool produces for us, and we go back to it only when we have a reason. These examples overlap historically. They are arranged by abstraction level: the point at which a human describes what the machine should do. At the lowest level, that description can be physical. Connections, switches, coefficients and machine configuration embody the computation directly. At the next level, symbolic instructions describe machine operations. With high-level languages, engineers still implement against requirements, but they express the solution as algorithms, control flow and data structures while the compiler translates it into machine instructions. AI-based development may delegate one more transformation: from controlled engineering intent to candidate source code and tests. Requirements are not new; what changes is how directly they drive the implementation. Early computing makes the abstraction boundary unusually visible. In a machine like this analog computer, the intended calculation is expressed directly in the physical configuration of the machine: connections, functional blocks and parameters become part of the program. The machine in the photograph dates from 1968, long after stored-program computers and FORTRAN already existed. It illustrates coexistence, not sequence: lower and higher abstraction levels remained in use at the same time. What changed was the level at which more engineers could describe the result they wanted and delegate the translation to another layer. We have made this move before The more important transition was not from one particular machine to the next, but from one level of description to another. High-level languages made that shift consequential: programmers could express algorithms in a notation designed for humans while a compiler took responsibility for translating them into machine instructions. FORTRAN moved that boundary dramatically. Programmers still implemented required behavior, but they no longer had to express every operation in the vocabulary of the processor. The quality of the compiler’s translation - especially the efficiency of the generated code - became the central engineering question. When John Backus and his team developed FORTRAN in the 1950s, programmers were skeptical of automatic programming, and for good reason. Skilled programmers knew their machines intimately and could produce very efficient hand-written code. A translator that produced poor code would not have been useful simply because it made programming more convenient. Backus later wrote that the FORTRAN team believed their system would only be accepted if the generated programs came close to the efficiency of hand-coded ones. The compiler was therefore not an afterthought; it was the central engineering problem. Once that problem was solved well enough, the level at which programmers worked changed surprisingly quickly. Looking back, it is tempting to describe that transition as the replacement of one skill by another. I think that misses the more important point. The programmer still had to understand the problem, the machine and the consequences of the implementation. What changed was the level at which the solution was normally expressed. Assembly code did not vanish when C arrived, and machine code did not vanish when assembly code arrived. Both moved down the stack. We still inspect them when timing is tight, when a compiler does something unexpected or when the hardware behaves differently from what the source suggests. They remain important without remaining the place where every development starts. The important sequence is not chronological. It is functional. We are not looking at generations of machines, but at generations of abstraction between human intent and machine execution. Now source code may be moving down the stack Requirements are not new. In a disciplined embedded project, they already define the behavior to be implemented and accepted. Today, a human engineer interprets them - together with architecture, interfaces and other controlled design inputs - and writes C, C++ or Rust. The compiler turns that source into object code, and the target executes it. AI-based development changes the translation step, not the existence of the requirements. A generator can produce candidate code, tests and configuration from sufficiently precise requirements and acceptance criteria. The engineer still owns the intent; the compiler still produces the executable; and a separate acceptance mechanism still has to establish whether the resulting binary did what was required. The source language did not make the machine code unimportant. It changed who was expected to produce it. AI-based development may do something similar to source code. C, C++ or Rust remain important artifacts. Engineers still need to inspect and debug them. But increasingly they can be generated and regenerated from a higher-level description of intent. That is why source code is becoming a build artifact. Requirements and acceptance criteria have long been durable engineering assets. What changes is their operational role: together with controlled design inputs, they can become the reproducible basis from which candidate code and tests are generated, while evidence records whether the resulting binary satisfied them. If that is the new development stack, the difficult part moves as well. Generating another implementation becomes cheap; deciding whether that implementation can be accepted does not. That chain contains three different engineering layers that are often grouped together. A hardware-aware generator can read technical documentation, produce firmware, flash a board and run functional tests. A standards-aware generator can produce code aligned with rules such as MISRA® or AUTOSAR. Both are useful advances. Neither one, by itself, is the release decision. Acceptance asks a different question: did the optimized target binary satisfy the declared acceptance criteria, measured through a mechanism whose criteria, oracles and verdict the generator cannot control - and with anything not established left visible? CEDARtools.Forge is built around that third layer. Generation produces candidates. A separately controlled acceptance mechanism decides what the available evidence actually establishes. A build artifact can be extremely important and still be replaceable. We care deeply about the binary that runs in a controller, but we do not normally edit that binary by hand. If it is wrong, we fix the source and build it again. In the same way, if generated C can be reproduced from an authoritative requirement, then the long-term asset is no longer every individual line in the generated file. This also gives us a useful test for whether the abstraction is complete. If engineers routinely have to hand-edit generated C and preserve those edits because the higher-level description cannot express them, then the C file is still partly source. The day those changes can be expressed in the controlled higher-level inputs - requirements, architecture, interfaces, constraints or configuration - and the code can be regenerated without losing intent is the day the role has genuinely changed. A prompt is not the new source language There is an obvious trap here. If requirements and controlled design inputs are to become a direct generation basis for source code, that does not mean ordinary prose suddenly becomes an executable specification. A prompt such as “build a safe motor controller” leaves almost everything that matters unanswered. A human programmer would have to make assumptions, and an AI generator will do the same. The useful abstraction is therefore not natural language by itself. It is intent that is precise enough to be checked. Depending on the system, that may combine normal prose with interface definitions, timing limits, state models, invariants, examples and acceptance criteria. The exact notation matters less than whether two engineers can read the requirement and agree on what observable behavior would satisfy it. In that sense, AI makes good requirements more important rather than less important. A strong generator can turn ambiguity into a plausible implementation very quickly. That is helpful when the ambiguity is harmless, but dangerous when the missing decision concerns a failure mode, timing constraint or boundary condition. The better generation becomes, the less comfort we should take from code that merely looks reasonable. Domain rules improve the candidate, but they do not replace acceptance. Code can be MISRA-clean, AUTOSAR-aligned or generated from a hardware-aware context and still implement the wrong requirement, omit a boundary condition or leave behavior unexamined in the optimized binary. Conformance by construction reduces known classes of defects. Independent acceptance establishes what has actually been demonstrated for this particular build. The compiler analogy has a limit A compiler and a generative model are not the same kind of tool. Given the same source, compiler version, options and target, a compiler will normally perform the same transformation again. That repeatability is valuable, but it should not be confused with infallibility. Conventional compilers are complex programs and can contain defects, including defects that silently produce wrong code. CompCert https://compcert.org/compcert-C.html is a notable exception to the usual level of assurance. Its central compilation passes are accompanied by machine-checked proofs that the generated assembly code preserves the behavior defined by the source program. The qualification is important: not every step around the compiler lies inside that proof boundary. Preprocessing, parts of the front end, assembling and linking still have to be considered separately. CompCert therefore does not make verification unnecessary; it shows how much confidence can be gained when the translation itself is no longer treated as an opaque implementation detail. Generative AI starts from a less stable position. The same requirement can lead to different implementations, and gaps in the requirement may be filled with assumptions that are plausible rather than intended. Reading the generated source helps, but it is not enough to establish trust. If the chain from requirements through AI to code is to be dependable, every generated candidate has to be evaluated through an acceptance mechanism whose criteria, test oracles, measurement path and verdict are controlled outside the generator. The generator may propose artifacts, including tests; it must not be the sole authority over what counts as a pass. So what does that evidence have to look like? Generated source code is not literally a black box: the code is visible, can be reviewed and can be debugged. What remains opaque is the relationship between that code and the engineer’s original intent. Reading the implementation cannot tell us with sufficient confidence that every requirement was implemented, that no unintended behavior was introduced, or that all relevant behavior will occur correctly on the target. Trust therefore has to come from evidence collected independently of the generator’s verdict. Independence here does not mean that no generated artifact may be used. It means that acceptance-policy ownership is separated from artifact generation, that tests and oracles are versioned and reviewable, and that the generator cannot silently alter the conditions under which its own output is accepted. Within that arrangement, the evidence itself needs two kinds of completeness. What acceptance evidence has to establish The first is functional completeness. Every declared acceptance criterion in scope needs a recorded verification outcome. Where the claim concerns runtime behavior, tests include relevant boundaries, error cases and failure behavior and run in an environment representative of that claim. This is the minimum, and on its own it proves less than it seems: requirements-based verification says nothing about implementation behavior that no requirement asked for. The second is structural completeness. Structural coverage does not prove that the implementation is correct, but it reveals which parts of the generated implementation were not exercised by the requirements-based tests. That makes the remaining gaps visible and turns them into explicit engineering questions rather than hidden behavior. We measure coverage top-down: start at the highest integration level at which the requirement can be meaningfully observed, then use integration and unit tests for obligations that demonstrably remain. Each step down the ladder is justified, and so is whatever remains unexecuted at the bottom: a missing requirement, which gets written; a missing test, which gets added; or unreachable code, which is either removed or - where it exists intentionally, for example as a defensive check or deactivated feature - retained with a documented justification. The accounting is complete only when every structural item is either exercised or explicitly dispositioned. A justification does not turn an unexecuted item into covered code. It records why the item remains outside executed coverage, the evidence supporting that decision and who approved it. That is the method we laid out at DX 2024 https://doi.org/10.4230/OASIcs.DX.2024.19 and Embedded World 2026 https://arxiv.org/abs/2608.13322 , and it is the only sense in which we use the word complete. Where the agreed assurance scope calls for target evidence, execution and coverage are collected on the target or project-representative hardware from the optimized binary under review. This avoids relying solely on a model for the observed behavior. Virtual platforms may still be useful elsewhere in the lifecycle, but representativeness has to be argued wherever credit is taken for their results. The application executes on the target while trace-based observation avoids application-side coverage counters in the binary under review. The supported trace configuration, measurement boundary and applicable probe effect are stated for the target and engagement. Is that more effort than most projects spend today? Yes, and by a wide margin if people had to write and run those tests by hand. The objection weakens when the tests are generated and executed by machine against requirements the engineer owns and the structural accounting comes from the observation described above. Only then is the generated implementation trusted for a reason that can be written down: every declared acceptance criterion in the agreed scope has a recorded outcome, every structural item in the agreed scope was either exercised or explicitly dispositioned, with executed coverage and justified non-execution reported separately, and the measurement was taken on the target, not in the generator’s own account of itself. The result is a stronger and more reviewable assurance case - not proof of complete system correctness, but a recorded basis for understanding what was tested, what was observed and what remains outside the claim. Without that evidence, the process is still trusting a black box. The generated code may be visible and readable, yet the decision to accept it would rest on the model’s apparent plausibility rather than on a checkable engineering case. The objective is not to prove that AI is generally trustworthy. It is to make each generated implementation earn trust by showing what it does, what was tested and how the results relate back to the requirement. CEDARtools.Forge closes the acceptance loop AI coding tools no longer necessarily stop at source code. Hardware-aware agents can compile, flash, drive bench instruments, run their own tests and repair the result on real silicon. That closes a functional generation loop. It does not by itself close an acceptance loop, because the same system may still propose the implementation, propose much of the test logic and interpret the result. CEDARtools.Forge places a separately controlled acceptance policy around the candidate-generation loop. The engineer owns the requirements and acceptance criteria. Within the workflow, a generator may produce code, tests and configuration; deterministic build, test, coverage and integration gates evaluate the result and produce pass, fail or unresolved outcomes from recorded inputs. The generator may iterate, but it cannot change what counts as a pass or set its own result to green. The candidate executes on the target, and Forge combines the functional results with structural coverage derived from the target’s trace data. Anything left uncovered has to lead somewhere explicit: to a missing requirement, a missing test, unreachable code that should be removed, or a documented justification for code that intentionally remains. This creates a closed acceptance loop. When a requirement changes, the implementation and its tests can be regenerated and the evidence can be rebuilt from execution on the target, without making the engineering decision less reviewable. There is also an economic consequence. Generating tests from requirements and collecting the structural accounting from trace-based observation of the binary under review can make this discipline economically plausible for a broader class of projects than those that already budget for certification-driven evidence. This is the important difference between code generation and closed-loop development. Generating software is becoming easy. Forge is intended to make it practical to decide, with evidence rather than plausibility, whether the generated software should be accepted. What changes for engineers? Some low-level skills will become less common, just as many software engineers today rarely write assembly code. That is not necessarily a loss of engineering competence. It is a shift in where that competence is used. Someone still has to notice that two requirements contradict each other, that a specified timing budget cannot be met on the selected hardware, or that a failure case has never been defined. Someone still has to decide whether a trace from the target actually supports the claim being made. Those tasks become more important when generating another candidate implementation becomes cheap. There will also be plenty of reasons to go down a layer. When a compiler behaves unexpectedly, we inspect assembly code. When generated C behaves unexpectedly, we will inspect C. The existence of a higher abstraction does not remove the lower ones; it changes how often we have to work there and what we expect to preserve by hand. One more layer up The progression from plugboards to assembly code and from assembly code to C, C++ and Rust did not remove engineering from the process. It changed the level at which engineers normally expressed their intent and allowed increasingly complex systems to be built without manually carrying every lower-level detail. There is no obvious reason to assume that today’s programming languages are the final layer engineers must author directly. Requirements and acceptance criteria already guide disciplined development. They may now become the controlled, reproducible basis from which candidate implementations are routinely generated - provided they are precise enough and the result can be accepted independently of the system that generated it. That is the condition that matters. If changing a requirement allows us to regenerate the implementation, execute it on the real target and rebuild a reviewable chain of evidence from the requirement to the observed behavior, then source code no longer has to carry the project’s intent by itself. AI can now write firmware that runs. Hardware-aware agents can even flash the board and run bench tests. Standards-aware models can produce rule-aligned code. CEDARtools.Forge answers the next question: did the optimized target binary satisfy the declared acceptance criteria - measured through a mechanism other than the generator, with everything not established left visible? Once that loop is complete, source code has not disappeared. It has simply taken the same step that machine code and assembly code took before it: still important, still inspectable and still available whenever engineers need to go down a layer, but no longer necessarily the primary artifact they author and preserve as the expression of system intent. Explore CEDARtools.Forge /forge/ · See the CEDARtools.Forge assurance boundary /forge/assurance-boundary/ Historical notes For the history of FORTRAN and the skepticism around automatic programming, see John Backus, The History of FORTRAN I, II, and III , ACM SIGPLAN Notices 13 8 , 1978, DOI 10.1145/960118.808380 https://doi.org/10.1145/960118.808380 , and IBM’s historical material on FORTRAN https://www.ibm.com/history/fortran . For compiler correctness, see Xuejun Yang et al., Finding and Understanding Bugs in C Compilers https://www.flux.utah.edu/paper/yang-pldi11 PLDI 2011 , and the CompCert C compiler https://compcert.org/compcert-C.html . Image credits: the EAI 580 plugboard photograph by Tomwsulcer is released under CC0 via Wikimedia Commons https://commons.wikimedia.org/wiki/File:EAI 580 analog computer plugboard at CHM.jpg ; the Motorola 6800 listing Michael Holley and the FORTRAN punch card Tangopaso are in the public domain. Related: CEDARtools.Forge /forge/ · the CEDARtools.Forge assurance boundary /forge/assurance-boundary/ · closed-loop AI-based development /technology/closed-loop-ai-development/ · coverage for integration evidence /products/coverage/integration-evidence/ · our publications /evidence/publications/ Comments or feedback on this article? blog comments@accemic.com mailto:blog comments@accemic.com