A Dual-Assistant Workflow for Engineering AI-Assisted Programming

    AI coding assistants can draft plans, generate code, and help debug, but a single continuous chat often drifts on requirements, loses context, and produces inconsistent style. This article describes a more controlled engineering workflow that separates planning, implementation, review, and testing across complementary assistant roles. It covers how to converge ambiguous intent into a written task outline, enforce project-level constraints, implement in small modules, run cross-audits, finalize test plans before execution, and keep human decision rights over scope, risk, UI behavior, and sign-off. The goal is not faster code generation at any cost, but fewer regressions, clearer evidence, and more reviewable delivery as projects grow.

    A Dual-Assistant Workflow for Engineering AI-Assisted Programming

    Intelligent coding assistants can already draft solutions, generate code, and help troubleshoot defects. But a single chat thread can easily lose fidelity: requirements drift, context is forgotten, and style becomes inconsistent. A more reliable approach is to place two complementary assistant roles inside one engineering process. One role is good at turning vague intent into an executable specification. The other is good at implementing against that specification and accepting audit. Neither role replaces human judgment. Instead, they separate discussion, constraints, implementation, review, and testing so that each step produces an artifact that can be checked.

    Why a Single Conversation Is a Weak Foundation

    When one assistant continuously “thinks and writes” in the same context, four common problems appear.

    First, requirements are silently rewritten during the chat. The final code may match the last prompt while drifting away from the original business boundary. Second, there is no module split. A single generation becomes too large, and errors are hard to locate. Third, there is no independent review. The generator and the reviewer share the same context, which makes self-justification easy. Fourth, tests are added after the fact. Interface contracts become unstable, and UI states and exception paths are missed.

    A more engineering-oriented division of labor is: create written constraints first, implement by module, run an adversarial review after each module, and finalize the test plan as a written artifact. Humans always retain three decision rights: requirement trade-offs, risk acceptance, and final confirmation of UI and business rules.

    Converge Requirements Before Allowing Code

    Before work begins, use only planning or discussion mode. Do not allow direct repository changes. The output should be an “implementation constraints document,” which can be called a task outline. It is not a summary of the chat log. It is the single basis for all later generation and audit.

    The task outline should at least specify:

    • Goals and non-goals: the user path to be delivered in this task, and the scope explicitly excluded.
    • Inputs and outputs: data sources, field meanings, and visible behavior for null values and exceptions.
    • Module boundaries: each module’s responsibility, dependency direction, and rules that prohibit cross-layer calls.
    • Quality gates: readability, error handling, logging, externalized configuration, and no hardcoded secrets.
    • Acceptance scenarios: normal cases, boundary cases, failures, concurrency or duplicate submission, and other cases that must be covered.

    A useful discussion rhythm is this: Assistant A drafts the outline from the original idea. Assistant B only raises objections and does not write code, pointing out ambiguity, omissions, and overdesign. Assistant A then responds item by item, accepting, rejecting, or rewriting. Several rounds may be needed until objections converge. The sign of convergence is not that “both sides say it is fine.” It is that the outline no longer contains contradictory statements, and every acceptance scenario maps to a module.

    Use Project-Level Constraint Files to Bound Generation

    Before implementation starts, write a separate project constraint file for the implementation assistant. It should be shorter than the task outline and should emphasize long-term rules: directory conventions, naming style, error return conventions, which directories are read-only, and the self-check items that generated code must include. Constraints should be executable, not vague slogans. Instead of saying “code should be robust,” say “external input must be validated; on failure, return a clear error message and do not swallow exceptions; configuration must be read from environment variables or configuration files.”

    The relationship between the constraint file and the task outline is simple: the outline describes what to do for this task, while the constraints describe how this repository is always handled. When they conflict, a human should make a decision first and then update the documents. Do not let the assistant compromise on its own inside the code.

    Implement by Module, Not in One Shot

    Develop from the bottom up according to dependencies: first data and pure logic, then service orchestration, and finally UI or entry points. When starting each module, give the implementation assistant only the outline sections and constraints relevant to that module. This reduces interference from unrelated context.

    A practical definition of done has four parts: the interface and types are implemented according to the outline; major branches have corresponding handling; the module does not introduce external dependencies not authorized by the outline; and the change notes list assumptions and open questions. A short module completion check can look like this:

    1. Module name:
    2. Outline clauses referenced:
    3. Public interface:
    4. Behavior on failure:
    5. Uncovered items (human confirmation required):

    The smaller the module, the easier it is for audit to catch code that “looks runnable” but actually deviates from the contract. If a module repeatedly fails to converge, the usual cause is not insufficient model capability. It is more likely that the outline still contains ambiguity. Return to discussion instead of piling on patches.

    Cross-Audit: Assign Another Assistant to Find Errors

    The implementer should not also be the final reviewer. After each module is complete, give the diff, the relevant outline clauses, and the constraints to a review assistant. Require it to output only an issue list, ordered by severity: behavior deviation, security and data risk, maintainability, and style. Audit comments must reference specific behavior or code locations. Vague evaluations are not acceptable.

    Then let the implementation assistant rebut or modify each item. A rebuttal needs a reason: the outline allows this behavior, the audit misread it, or the suggestion would expand scope. After several rounds, keep only three kinds of conclusions: modified, explicitly rejected with the reason recorded, or escalated to a human decision. Open items must not be silently dropped. The value of this adversarial process is that it breaks confirmation bias inside a single context; it is not about making the two assistants agree word for word.

    During audit, focus on high-frequency failure points: null values and duplicate requests, permissions and sensitive information written to logs, errors converted into misleading success, modules quietly expanding their responsibilities, and comments that do not match actual branches.

    Finalize the Test Plan Before Execution

    After functional code passes audit, a planning-oriented assistant should draft an automated test plan based on the acceptance scenarios. The implementation assistant is responsible only for implementing it and must not delete scenarios on its own. The plan should distinguish pure logic that can be unit tested, external dependencies that should use test doubles, and UI or end-to-end tests that cover only critical paths. Each case should state precondition data, actions, expected results, and how to determine on failure whether the issue is a product defect or a test error.

    Execute only after the plan is finalized. Classify failures first: contract errors go back to the outline, implementation errors go back to the relevant module, and environment problems are recorded separately. Do not substantially change both requirements and code when tests fail; otherwise, it becomes impossible to identify the source of a regression.

    What Humans Still Must Cover

    Assistants are weak at final UI feel, misleading copy, and real-environment permissions and data quality. Human supplemental testing should focus on key buttons and empty states, whether error messages are understandable, whether slow operations provide feedback, and whether refresh and back navigation break state. When a defect is found, first determine which acceptance scenario it belongs to. If the scenario itself is wrong, update the outline before changing code. This avoids verbal fixes accumulating into patches that cannot be traced.

    When non-specialist developers use this workflow, their personal time is mainly spent reviewing documents and the UI, not writing every line from scratch. This does not reduce responsibility. Before merging, they still need to understand the control flow, confirm that no secrets were committed, and confirm that tests do not merely assert “no exception was thrown.”

    A Practical Cadence

    For a small feature, complete the full cycle before starting the next feature: discussion convergence, constraint check, single-module implementation, cross-audit, test finalization, and human supplemental testing. If the business goal changes midway, update the outline version first, then allow code changes, and record obsolete clauses in the change notes. Documents should live in the repository and be reviewed with the code, not exist only in a chat window.

    Measure the method by whether rework decreases, not by generation speed. Are the same defects repeating? Can audit catch behavior deviations before merge? Do tests actually correspond to acceptance scenarios? Once these three points are stable, consider increasing the number of parallel modules. Before that, moving more slowly and writing clearly usually saves total time compared with repeatedly asking an assistant to “keep improving.”

    Treat intelligent coding assistants as constrained collaborators, not one-shot code factories. Delivery quality then remains controllable as the project grows. The task outline, project constraints, module diffs, audit conclusions, and finalized tests form a reviewable evidence chain. Humans are responsible for trade-offs and sign-off. This method is not tied to any specific tool form. As long as planning, implementation, and review can be separated, the same cadence can be applied.