Evidence-driven engineering for developers working with coding agents.
Version: 2.1 Updated: August 2026 Status: Living manifesto
Software engineering is not the production of plausible code.
It is the controlled transformation of intent into behavior that can be understood, verified, operated, changed, and trusted.
Coding agents make that responsibility more important, not less. They can inspect repositories, generate plans, modify many files, run commands, investigate failures, and continue work after interruption. They also scale stale context, weak assumptions, accidental scope expansion, false abstractions, and silent failures with unsettling efficiency.
The useful question is no longer:
How can a developer get a better suggestion from AI?
It is:
How can developers and agents work together so that speed never substitutes for evidence, accountability, security, or maintainability?
Codelight is our answer. It defines a minimum operating discipline for work performed by humans, agents, or both.
It is deliberately not a language tutorial, a prompt library, or a claim that every task deserves the same ceremony. PHPStan, migrations, SQL, test frameworks, repositories, dependency injection, generics, Git history, and runtime probes are techniques. They matter only when they support the principles below.
| Principle | Commitment |
|---|---|
| C | Context is a dependency. |
| O | Outcomes and constraints precede generation. |
| D | Decisions and state must be explicit. |
| E | Evidence beats confidence. |
| L | Learn selectively and forget deliberately. |
| I | Intent and irreversible decisions remain human-owned. |
| G | Guardrails, security, and observability are defaults. |
| H | Humans and agents form a control loop. |
| T | Test, measure, and verify every change. |
An agent can reason only from the context it receives.
Context is not a free-form prompt attachment. Treat it as an engineering dependency:
- scoped to the task,
- traceable to a current source,
- complete enough to support the decision,
- small enough to inspect,
- replaceable when stale,
- explicit about what is excluded.
More context is not automatically better. Too little produces guesses. Too much hides the relevant seam. A generated map, previous conversation, task summary, or old decision record may be useful navigation input, but it is not automatically a source of truth.
Current code is context. Runtime behavior is context. Database shape, configuration, tests, logs, and Git history are context. They do not all carry the same authority, and they may contradict one another.
Before asking an agent to change code, identify the real seam: the reported request, runtime symptom, consumer, database shape, template, configuration path, integration boundary, or test that owns the behavior.
Then identify:
- the authoritative sources,
- relevant historical evidence,
- important unknowns,
- current local changes,
- deliberate exclusions.
Do not dump a repository into an agent and call it understanding.
Define what must be true before deciding what to build.
Vague instructions such as “refactor this,” “make it secure,” or “improve performance” are not specifications. They invite an agent to choose the problem, solution, and risk tolerance on the developer’s behalf.
Every non-trivial task should establish:
- the problem and intended outcome,
- acceptance criteria,
- allowed and forbidden scope,
- non-goals,
- operational and compatibility constraints,
- required evidence,
- stopping conditions,
- approval boundaries.
The developer owns the problem definition. The agent may refine ambiguity, discover constraints, expose contradictions, and propose alternatives, but it must not silently convert an assumption into product intent.
Constraints do not reduce useful creativity. They turn generation into engineering.
Important work must survive the session that created it.
Chat history is a poor project record. It is private, linear, difficult to audit, and often unavailable when another developer or agent needs to continue.
Persist enough state for safe continuation:
- scope and non-goals,
- assumptions and unknowns,
- decisions and rejected alternatives,
- approvals,
- evidence and commands,
- checkpoints and next executable action,
- remaining risks,
- blocked validation,
- ownership of concurrent or unrelated work.
A commit explains what changed. It rarely explains the original problem, why a tempting alternative was rejected, or what still needs a production observation.
For non-trivial work, leave a concise factual handoff. Keep change slices, validation results, and unrelated work distinct.
When resuming:
- compare the previous record with the live repository,
- inspect the current working tree,
- verify that assumptions still hold,
- rerun relevant evidence before continuing.
Do not mistake a fluent retrospective for a durable decision record.
A plausible explanation is not verification.
Model confidence is not evidence. Human confidence is not evidence. Agreement between several agents is not evidence. Multiple intelligent systems can repeat the same unsupported assumption with impressive consistency.
Useful evidence includes:
- executable tests and static-analysis results,
- reproducible commands and bounded output,
- current source, configuration, and schema inspection,
- targeted Git-history inspection,
- controlled runtime observations,
- read-only database or API probes,
- before-and-after measurements,
- logs with sufficient non-sensitive context,
- authoritative product or operational sources.
History is evidence, not authority. Tests are evidence, not proof. Documentation is evidence, not immunity from drift.
Stronger conclusions require corroboration across the sources that matter for the task.
Label material claims honestly:
- Verified — directly supported by reproducible evidence.
- Inferred — derived from verified facts, but not directly observed.
- Assumed — needed to proceed and not yet verified.
- Blocked — cannot be verified because access, tooling, data, or approval is unavailable.
- Contradicted — disproved by stronger evidence.
Never turn “I could not verify this” into “this should work.”
Learning without curation becomes contamination.
Not every observation deserves permanent memory. A task-local workaround, stale command, accidental implementation detail, or environment-specific fact becomes dangerous when recalled later as a general rule.
A durable learning needs:
- the observed problem and evidence,
- a root cause or carefully bounded conclusion,
- scope and non-applicable cases,
- confidence and source,
- review, expiry, or supersession trigger,
- an owner or approval path where it changes shared practice.
Valid outcomes include:
- a durable learning,
- a local task note,
- a temporary assumption,
- no durable learning.
Preserve the last option.
Review, supersede, and remove obsolete guidance deliberately. Memory becomes an engineering asset only when forgetting is part of its design.
Agents execute; humans remain accountable for purpose and consequence.
An agent can responsibly own exploration, mechanical implementation, candidate generation, repetitive refactoring, test drafting, analysis, and evidence collection within explicit boundaries.
Humans retain ownership of:
- business intent and prioritization,
- scope approval and risk acceptance,
- ambiguous policy decisions,
- destructive operations and irreversible migrations,
- permission expansion and production authority,
- security exceptions,
- acceptance of known gaps,
- durable organizational rules.
Define approval boundaries before execution. An agent must stop before consequential actions rather than treating an available credential, command, or network path as permission.
Human review should focus on intent, architecture, risk, behavior, and evidence. “Review every generated line” is neither the only nor the most scalable form of accountability.
Safe and observable behavior must be the normal path.
Security is not a final checklist. Observability is not an apology written after the first incident.
Systems should:
- validate at trust boundaries,
- use least privilege,
- protect secrets and personal data,
- fail explicitly instead of falling back silently,
- log actionable, non-sensitive context,
- keep critical decisions auditable,
- distinguish retryable failure from partial state and permanent rejection,
- reject ambiguous or malformed input.
Agents working on local computers need the same boundaries. Technical access is not task relevance. A coding task does not authorize browsing unrelated personal files, shell history, credentials, other repositories, or production systems.
Every meaningful failure path should make it possible to answer:
- What failed?
- Where did it fail?
- Which safe identifiers were involved?
- Does partial state exist?
- Is retry safe?
- What action comes next?
Do not expose secrets or personal data because debugging without discipline feels faster.
The developer is not an agent’s typist, and the agent is not an authority.
The productive relationship is an engineering control loop, not a one-shot prompt:
- The developer frames intent, constraints, stakes, and approval boundaries.
- The agent maps the current system and exposes assumptions.
- The developer corrects domain misunderstandings and selects risk boundaries.
- The agent implements a small reviewable slice and collects evidence.
- The developer and agent review behavior, evidence, and remaining uncertainty.
- Both decide what should survive as project knowledge.
The developer contributes domain judgment, taste, responsibility, prioritization, and the ability to recognize a solution that is technically valid but operationally wrong.
The agent contributes breadth of inspection, persistence across repetitive work, fast hypothesis generation, structured comparison, and disciplined evidence collection when required to provide it.
Give agents concrete seams and observable outcomes. Require them to distinguish facts from hypotheses, inspect the real consumer, preserve existing work, and show the proof path.
Treat explanations, reviews, and proposed code as useful hypotheses until repository, runtime, historical, or test evidence supports them.
Do not outsource ownership. Do not waste the agent by using it only for autocomplete.
A change is incomplete until its behavior has been verified.
Verification is part of the deliverable. It is not a hopeful activity performed after the code “looks finished.”
Use the smallest reliable combination that fits the risk:
- source and configuration inspection,
- syntax, formatting, and static analysis,
- focused unit and integration tests,
- realistic end-to-end or local runtime checks,
- read-only probes against the actual target,
- performance measurements before and after optimization,
- manual verification where the behavior is genuinely human-facing.
Verification should move from narrow to broad. A fast targeted regression test is usually a better first check than an enormous suite. A shared behavioral change may still require the enormous suite afterward. Human beings have somehow managed to turn both extremes into dogma.
The final result states:
- what changed,
- why it changed,
- what remained intentionally unchanged,
- the commands and observations used to verify it,
- what failed or could not run,
- the remaining risk.
“Implemented successfully” without this information is an agent-shaped opinion.
These are recurring lessons from real agent-assisted engineering work, not abstract preferences.
The reported symptom can live in a request payload, a consumer, a background job, generated integration code, or a database row shape, not in the file that first looks suspicious.
We repeatedly got better outcomes by starting with the real runtime path and tracing outward than by applying a familiar pattern to a similarly named component.
Practice: Reproduce or inspect the reported path first. Map definitions, callers, data flow, templates, configuration, generated sources, and tests before changing a symbol.
A structural test may pass while a user-facing flow remains broken because the test did not reach the request payload, state transition, integration boundary, or real consumer that owns the defect.
Practice: For a bug fix, add the smallest regression that reproduces the actual failure mechanism. Then add only the broader checks justified by shared dependencies and risk.
In one investigation, multiple agents reached the same plausible mapping conclusion. A small read-only probe against the target system disproved it.
The failure was not lack of intelligence. It was treating inference as proof.
Practice: When operational data, external mappings, permissions, schemas, or production-shaped behavior matter, prefer a narrow reproducible read-only observation over accumulated confidence.
Git history can reveal reverts, compatibility branches, previous incidents, renamed concepts, and why a workaround exists. It can also be stale, incomplete, local-only, or superseded by current behavior.
Practice: Inspect bounded history when changing existing behavior, touching a workaround, restoring old behavior, removing an apparently redundant condition, or encountering conflicting tests and code.
Use git log, path history, git show, git blame, and semantic searches such as git log -S or git log -G as targeted evidence. Corroborate historical conclusions with current code and observable behavior.
A local agent may have access to toolchains, containers, services, logs, generated files, fixtures, and Git history. That can produce stronger validation than source inspection alone.
It may also have access to credentials, personal files, unrelated repositories, browser data, and destructive commands.
Practice: Use locally available evidence only when it is relevant, bounded, safe, and permitted. Technical access does not create authorization.
Small coherent slices make it easier to identify a regression, run focused checks, preserve unrelated work, and reverse a decision. They are not merely a review preference.
Practice: Avoid opportunistic cleanup. In a dirty working tree, identify relevant existing changes, preserve unowned files, and keep commits, staging, handoffs, and validation scoped to the behavioral slice.
Agents readily extract a shared helper or generic layer because two fragments look alike. Similar syntax does not establish shared domain rules, lifecycle, or reasons to change.
Practice: Keep intentional duplication when it is clearer. Introduce an abstraction only when shared behavior and a shared reason to change have been verified.
A failed command may indicate a defect, unsupported runtime, stale generated output, missing service, unrelated baseline issue, or the wrong execution environment.
Practice: Preserve the command and relevant output, identify the cause, fix it only when it is in scope, and rerun the smallest proving check. Never describe an unavailable check as a pass.
A previous plan, summary, or checkpoint may no longer match the branch, working tree, dependency state, or target system.
Practice: On resume, compare stored task state with the live repository. Revalidate assumptions, inspect intervening commits, and rerun the evidence that still matters before continuing.
Using several agents can broaden exploration, but it does not guarantee independent evidence. Agents may share the same repository snapshot, documentation error, hidden assumption, or fashionable abstraction.
Practice: Use multiple agents to create competing hypotheses and review surfaces. Use primary evidence to decide between them.
The principles become useful when they shape the work.
DEFINE → MAP CONTEXT → RECALL CONSTRAINTS → PLAN → EXECUTE
↑ ↓
CURATE MEMORY ← LEARN ← REVIEW ← VERIFY ←───────────────┘
Establish the problem, intended outcome, acceptance criteria, constraints, non-goals, risk, evidence requirements, and approvals.
Exit condition: Success and failure are distinguishable.
Locate the real behavior path:
- code and consumers,
- data flows and schemas,
- configuration and templates,
- tests and fixtures,
- generated sources,
- deployment and runtime boundaries,
- current working-tree changes,
- relevant Git history,
- authoritative documentation.
History inspection should be bounded and question-driven. Do not pour the entire commit graph into context like a developer trying to solve uncertainty with volume.
Exit condition: The implementation surface, current state, historical constraints, and important unknowns are explicit.
Load only applicable repository rules, ADRs, incidents, security requirements, validated learnings, and task-specific approvals.
Record rejected, stale, conflicting, or irrelevant guidance as such.
Exit condition: The task is constrained without being drowned in historical text.
State:
- expected files and behavior,
- behavior that must remain unchanged,
- assumptions,
- alternatives considered,
- validation steps,
- rollback or reversal path,
- approval boundaries,
- stopping conditions.
Connect each acceptance criterion to planned evidence.
Exit condition: A reviewer can understand the intended change and proof path before implementation begins.
Implement in small coherent steps.
Protect existing work, avoid unrelated cleanup, preserve reversibility, and update the task record when a material fact changes the plan.
Stop when new findings cross scope, risk, or approval boundaries.
Exit condition: The requested slice is complete without silent scope expansion.
Run checks from narrow to broad:
- inspect the diff,
- run syntax and structural checks,
- run focused regression tests,
- run relevant static analysis and formatting checks,
- run integration or repository-wide checks,
- inspect local runtime behavior,
- use approved read-only target probes where risk requires them.
Record failed and blocked validation honestly.
Exit condition: Claims about the result are tied to reproducible evidence, and gaps are explicit.
Review:
- intent alignment,
- compatibility,
- security,
- failure behavior,
- operational impact,
- rollback behavior,
- evidence quality,
- historical regression risk,
- unnecessary complexity.
Treat review feedback, including agent feedback, as a hypothesis until checked.
Exit condition: Remaining decisions and risks are visible to the human owner.
Extract only reusable lessons.
Give them evidence, scope, confidence, ownership, and a review trigger. Otherwise retain a local note or explicitly record that no durable learning resulted.
Remove or supersede guidance that no longer holds.
Exit condition: Future work receives signal, not accumulated noise.
The required evidence level depends on risk. A local naming cleanup does not require a production probe. A migration driven by undocumented external data probably does.
| Level | Evidence | What it establishes |
|---|---|---|
| 0 | Hypothesis | A possible explanation worth testing, not a conclusion. |
| 1 | Static inspection | Current source, configuration, schema, documentation, or bounded history supports the claim. |
| 2 | Automated verification | Deterministic tests, analysis, linting, build checks, or local execution support the claim. |
| 3 | Integrated verification | Controlled dependencies exercise the relevant components together. |
| 4 | Live read-only evidence | A bounded non-destructive observation confirms target behavior or data. |
| 5 | Controlled consequential evidence | Approved, monitored, auditable production or write execution confirms the result. |
Level 5 requires explicit approval, rollback planning, observability, and a defined owner. No agent should infer permission from technical capability.
Evidence levels are not badges. A higher number is not automatically better. Use the lowest level that reliably addresses the actual risk.
Good input supplies:
- the expected outcome,
- exact symptom or target,
- constraints,
- acceptance criteria,
- scope,
- evidence expectations,
- approval boundaries.
It does not prescribe every implementation detail before the repository has been mapped.
Useful prompts ask the agent to identify:
- the owning path,
- current behavior,
- callers and consumers,
- relevant tests,
- historical constraints,
- available validation.
This is especially important for migrations, security, permission logic, compatibility work, and legacy integrations.
Ask what command, test, query, source, commit, or runtime observation supports a claim.
Ask what could not be validated.
A concise evidence table is more useful than a theatrical account of internal reasoning.
When the developer has a better runtime fact, request payload, customer constraint, or business rule, it should replace weaker inference.
The agent should re-map the work, not defend its first explanation.
Ask it to look for:
- compatibility regressions,
- unsafe input handling,
- missing failure paths,
- stale tests,
- generated-file ownership,
- historical reverts,
- false abstractions,
- unverified assumptions,
- validation gaps.
Then verify high-impact findings against primary repository or runtime evidence.
Before editing, the agent should inspect branch, commit, staged changes, unstaged changes, and untracked files.
It must not silently overwrite, reset, stash, stage, reformat, or commit unrelated work.
A dirty working tree is not permission to clean someone else’s desk with a flamethrower.
History questions should be concrete:
- When was this condition introduced?
- Was this behavior previously reverted?
- Which test accompanied the change?
- Was the symbol renamed?
- Which compatibility requirement shaped the implementation?
Use targeted history inspection to answer those questions. Do not treat age, authorship, or commit-message confidence as correctness.
Do not send an agent across an approval boundary merely because it has a tool.
The best collaboration is fast within a safe envelope and explicit at the edge of that envelope.
Projects should define language and platform profiles. The cross-language baseline is simpler.
- Prefer explicit contracts over ambiguous bags of data.
- Model important domain constraints rather than asserting them only in comments.
- Keep interfaces narrow and behavior-focused.
- Do not claim guarantees that runtime behavior or static analysis cannot establish.
- Prefer immutable state where it reduces ambiguity and invalid intermediate states.
- Do not ignore failures or use silent fallback for unexpected state.
- Distinguish absence, invalid input, external failure, and internal inconsistency.
- Preserve meaningful context without leaking secrets or personal data.
- Make retry and partial-state behavior explicit.
- Avoid generic catch-and-continue behavior that turns one visible failure into several invisible ones.
- Validate at trust boundaries.
- Use safe APIs, parameterized queries, allowlists, and least privilege.
- Derive storage and execution properties from verified content, not user-controlled names.
- Keep secrets out of source, logs, generated reports, and agent handoffs.
- Treat local credentials as capability, not authorization.
- Test observable behavior, including failure paths.
- Keep normal tests deterministic.
- Use fakes for external transports where appropriate.
- Test real adapters separately and intentionally.
- Use static analysis and formatting as gates, not substitutes for behavioral verification.
- Preserve failing commands and relevant output when validation uncovers a problem.
- Correctness and clarity come first.
- Measure before optimizing and retain the measurement evidence.
- Confirm that an improvement does not weaken compatibility, security, or operations.
- Do not optimize a toy benchmark while the actual bottleneck remains unmeasured.
- Prefer small reversible changes.
- Understand consumers before changing contracts.
- Inspect relevant history before removing compatibility behavior.
- Use migrations, rollout plans, and rollback paths for stateful changes.
- Preserve enough operational context to diagnose a failure without reconstructing the entire change history.
- Use history to understand intent, incidents, reversions, migrations, and compatibility constraints.
- Keep history searches bounded to the question being answered.
- Treat
git blameas a pointer to a commit, not a psychological profile of the author. - Corroborate historical conclusions with current behavior.
- Label reflog or other local-only evidence as local and non-portable.
- Store decisions and evidence in project-visible artifacts where appropriate.
- Separate task state from durable learning.
- Record why guidance was selected, rejected, superseded, or expired.
- Do not convert conversation fragments into permanent rules without review.
Codelight rejects:
- vague tasks without observable outcomes,
- context dumping as a substitute for understanding,
- agent confidence or consensus presented as fact,
- unverified claims of success,
- destructive or consequential action without approval,
- silent failure and unsafe fallback behavior,
- security by convention,
- optimization without measurements,
- generic abstractions without verified shared behavior,
- tests that accidentally depend on real infrastructure,
- permanent memory without scope, evidence, or curation,
- chat history treated as durable project state,
- Git history treated as unquestionable intent,
- unrelated cleanup hidden inside a feature change,
- local access treated as permission,
- an agent treated either as an unquestioned authority or as an expensive autocomplete box.
A non-trivial change is done when:
- The problem, outcome, and non-goals are explicit.
- Relevant context, constraints, history, and existing work were inspected.
- Assumptions are visible and verified where practical.
- The implementation is scoped, reviewable, and reversible.
- Unrelated working-tree changes were preserved.
- Changed behavior has targeted regression coverage where appropriate.
- Relevant syntax, static analysis, formatting, and tests have run.
- Security, failure behavior, compatibility, and operations were reviewed.
- Runtime or read-only target evidence was obtained when risk requires it.
- Commands, results, gaps, and remaining risks are recorded.
- Consequential actions received the required approval.
- The final diff contains no accidental or unrelated changes.
- Durable learning was curated, superseded, or explicitly declined.
- Another developer or agent can continue from the recorded state.
Good engineering is not measured by how quickly code appears.
It is measured by how reliably intent becomes behavior, and by whether another person can understand, verify, operate, and safely change that behavior later.
Coding agents do not remove this responsibility.
They make the absence of discipline scale faster.
Define the outcome. Map the real seam. Constrain the work. Preserve the state. Demand evidence. Verify the result. Learn selectively.