Stateful Pull-Push Engine, SQLite Persistence & Chaining
Once a workflow passes its pre-flight readiness gates, pni501 (v0.1.4) transitions into a deterministic stateful execution loop orchestrated by src/pni/_workflows/engine.py, state_db.py, aggregator.py, and _migration_engine.py. Rather than trusting an AI agent to track multi-step progress in volatile chat memory or self-grade code paths without verification, the CLI acts as an authoritative state machine: executing static and container analyzers across all 6 languages (python, java, php, go, typescript, javascript) exactly once per step, synthesizing context-rich prompts on get-action, validating Pydantic schemas and repository path grounding on push-action, and chaining completed As-Is audit facts directly into downstream To-Be migration workflows.
Source Code
from drawlib.canvas import save, setup
from drawlib.diagrams.sequence import Participant, PhosphorIcon, SequenceDiagram
from drawlib.styles import Styles
d = SequenceDiagram(
node_style=Styles.PrimaryBold,
node_text_style=Styles.DarkBold.patch(text_size=10.5),
edge_style=Styles.DarkBold,
edge_text_style=Styles.DarkBold.patch(text_size=10.0),
node_card_style=Styles.Neutral.patch(
text_color=Styles.Dark.text_color, shape_r=1.6
),
autonumber=True,
col_width=49.0,
step_y=7.4,
margin=5.0,
)
agent = d.add(
Participant("AI Agent", width=25.0, height=15.5, icon=PhosphorIcon.ROBOT, icon_size=5.8)
)
engine = d.add(
Participant(
"pni Engine\n(CLI / engine.py)",
width=32.0,
height=15.5,
icon=PhosphorIcon.GEAR_SIX,
icon_size=5.8,
style=Styles.WhiteBold,
text_style=Styles.WhiteBold.patch(text_size=10.2),
card_style=Styles.PrimaryFlat.patch(shape_r=1.6),
)
)
analyzers = d.add(
Participant(
"Analyzers &\nGrounding",
width=29.0,
height=15.5,
icon=PhosphorIcon.MAGNIFYING_GLASS,
icon_size=5.8,
card_style=Styles.PrimaryNeutral.patch(shape_r=1.6),
)
)
db = d.add(
Participant(
"workflow_state.db\n(SQLite)",
width=36.0,
height=15.5,
icon=PhosphorIcon.DATABASE,
icon_size=5.8,
card_style=Styles.SecondaryNeutral.patch(shape_r=1.6),
)
)
cache = d.add(
Participant(
"Cache &\nReports",
width=26.0,
height=15.5,
icon=PhosphorIcon.FILE_TEXT,
icon_size=5.8,
)
)
agent.request(engine, "workflows start <id>")
engine.request(analyzers, "Preflight: web_pentest & based_on")
with d.opt("supersede"):
engine.request(db, "Cancel active runs -> cancelled")
engine.request(db, "INSERT wf-<8hex> & act-01..NN (pending)")
engine.reply(agent, "Return run_id (wf-<8hex>)")
with d.loop("act"):
agent.request(engine, "get-action <run_id>")
engine.request(db, "SELECT earliest pending/in_progress")
with d.opt("1st"):
engine.request(analyzers, "Run inspect/ast/run/sec/pentest/mig")
engine.request(db, "Store deterministic_output_json")
engine.request(cache, "Write .pni/cache/<wf>/facts.json")
engine.reply(agent, "Prompt: facts + rules + digest + Schema")
agent.request(engine, "push-action <run> <act> -f json")
engine.request(analyzers, "Check act_id, Pydantic & grounded paths")
with d.alt("err"):
engine.reply(agent, "Error detail (stays in_progress)")
with d.else_("ok"):
engine.request(cache, "Write arch/score/plan.json | check PDF>0B")
engine.request(db, "Save agent_input_json (act = completed)")
engine.reply(agent, "Advance -> Next act-YY")
engine.request(db, "SET workflow_runs.status = completed")
engine.reply(agent, "Workflow COMPLETED")
dw, dh = d.get_size()
setup(width=int(dw) + 8, height=int(dh) + 6, dpi=150)
d.draw(xy=(4.0, 3.0))
save()
1. Chronological Pull-Push Protocol & Grounding Verification
Every stateful workflow run progresses through three CLI phases in src/pni/_workflows/engine.py. For workflow YAML authoring and pre-flight readiness gates, see Workflows & Pre-Flight Gates.
Phase 1: Session Initialization (start_workflow_run)
- Loads
.pni/workflows/<id>.yamlwithbased_ontarget inheritance and.pni/pni.tomldefaults resolved. - Enforces pre-flight gates (
inspect_base_audit_statusandinspect_pentests_prerequisite), raisingWorkflowPrerequisiteErrorbefore touching SQLite if any prerequisite is unmet. - Normalizes composite steps into flat
PrimitiveStepentries (act-01..act-NN) across the 15 composite actions and resolves effectiveWorkflowOutputs(facts,score,report). - When
supersede=True(the default instart_workflow_run, or via--supersede), callsdb.cancel_active_runs_for_workflow(workflow_id)to mark older unfinished runs and their remaining stepscancelled. - Inserts a new
workflow_runsrow (run_id = wf-<8hex>,status = in_progress, recordingbased_onandbased_on_run_idwhen applicable) andNsequentialaction_runsrows (status = pending).
Phase 2: Deterministic Execution & Prompt Synthesis (get_workflow_action)
When the agent invokes pni workflows get-action <run_id>:
- Idempotent Single-Shot Analyzer Execution:
_execute_deterministic_step_if_needed()checksaction.deterministic_output_json is None. For deterministic primitives (project_inspect,ast_analyze,run_check,security_scan,web_pentest, andmigration_analyze), the engine runs the analyzer once, stores the serialized Pydantic JSON inaction_runs.deterministic_output_jsonalongsidestatus = in_progress, and writes the mergedWorkflowFactsBundleto.pni/cache/<workflow_id>/facts.json. Callingget-action <run_id>again after an interruption or validation retry re-renders the prompt instantaneously from SQLite without re-running Docker builds, Trivy scans, or HTTP probes. - Context-Complete Markdown Synthesis (
_format_action_guide): The CLI assembles and prints:- Step header (
run_id,action_id, sequenceseq/total,primitive_type,source_action, andbased_on_run_id). - Task-specific directive body (
render_action_task_body), including per-aspect instructions onrecord_architecture, mandatory per-rule verdict checklists onai_interpret, and pre-computedReportBlueprint/MigrationReportBlueprintlayouts onwrite_drawlib_report. - Accumulated deterministic facts (
ProjectFacts,AstFacts,RuntimeFacts,SecurityFacts,PentestFacts,MigrationFacts,ArchitectureRecord, and inheritedScoreResult). - Full Markdown bodies of active engineering rules (
_render_active_rules). - Prior
ai_interpretsummaries and—immediately beforeemit_score—the deterministicCompiledAuditDigest(compile_workflow_digestinaggregator.py), which pre-deduplicates findings, computes module hotspot density, aggregates rule compliance matrices, and suggests weighted scores across all 7 quality scoring axes (see 7-Axis Scoring Model). - Exact
push-actionCLI snippet and Pydantic JSON Schema (get_expected_input_schema_json).
- Step header (
Phase 3: Schema Validation, Grounding Gates & Side Effects (push_workflow_action)
When the agent submits pni workflows push-action <run_id> <action_id> --file payload.json:
- Strict Sequence Guard: Verifies
action_id == current.action_id; out-of-order pushes fail immediately without mutating SQLite. - Pydantic Validation & Anti-Hallucination Grounding (
_verify_grounded_paths): Every submitted payload is parsed against its canonical Pydantic v2 model and cross-checked againstAstFacts.module_inventoryand real workspace files (_is_grounded_repo_pathstrips:symbol/#anchorsuffixes and prevents path traversal outsideproject_root/target_root):
| Primitive Type | Validated Pydantic Model | Grounding & Deliverable Side Effects on Valid Push |
|---|---|---|
Deterministic (project_inspect .. migration_analyze) |
DeterministicAckInput ({"acknowledged": true}) |
Confirms analyzer output was reviewed and advances to the paired reasoning step |
record_architecture |
SystemProfile / StructureMap / ExecutionFlowMap / ArchitectureRecord |
Verifies layers[].paths, modules[].path, entry_points, and execution_flows[].involved_files; writes .pni/cache/<wf>/architecture.json and updates facts.json |
ai_interpret |
InterpretationInput |
Verifies findings[].file_path and action_items[].target_files; normalizes rule_checks against active step rules |
emit_score |
ScoreResult |
Verifies finding/action paths; enriches ScoreResult across the 7 scoring axes with CompiledAuditDigest, score_to_grade(), and metrics_snapshot; writes reports/<target>_score.json |
emit_migration_plan |
MigrationPlanResult |
Verifies target_axes[].affected_files, component_mappings[].source_file, decision_log[].file_path, and safe relative pr_wbs[].target_files; attaches MigrationFacts and writes reports/<target>_migration_plan.json |
write_drawlib_report |
ReportCompletionInput |
Verifies every path in generated_files (e.g., reports/<target>.pdf) exists inside workspace and has st_size > 0 bytes |
- Self-Correction Without State Corruption: If Pydantic parsing fails, a hallucinated file path is detected, or a PDF deliverable is missing/empty,
push_workflow_action()returns(False, error_detail)while keeping the step inin_progress—allowing the AI agent to fix the JSON payload or re-compile the report and retry cleanly. Once valid,complete_action()storesagent_input_json, marks the stepcompleted, and transitionsworkflow_runs.statustocompletedwhen the final step finishes.
# Execute the deterministic stateful pull-push loop
pni workflows start full_audit --supersede
pni workflows get-action wf-1a2b3c4d
pni workflows push-action wf-1a2b3c4d act-01 '{"acknowledged": true}'
2. SQLite Persistence Schema & 4-State RunStatus Lifecycle
All stateful workflow sessions are stored in .pni/cache/workflow_state.db (WorkflowStateDB in src/pni/_workflows/state_db.py, configured with timeout=30.0 and automatic lightweight ALTER TABLE schema migrations for backward compatibility):
Source Code
from drawlib.canvas import save, setup
from drawlib.diagrams.er import Entity, ERDiagram
from drawlib.diagrams.state import FinalState, InitialState, State, StateDiagram
from drawlib.icons import phosphor
from drawlib.smartarts import Panel
from drawlib.styles import Colors, Styles
setup(width=184, height=102, dpi=150)
left_panel = Panel(
"SQLite Schema (.pni/cache/workflow_state.db)",
header_variant="filled",
header_height=8.5,
style=Styles.Neutral.patch(shape_r=2.0),
header_style=Styles.PrimaryFlat,
text_style=Styles.WhiteBold.patch(text_size=11.0),
icon=phosphor.database,
icon_style=Styles.WhiteBold,
)
left_panel.draw(xy=(3.0, 3.0), width=79.0, height=96.0)
erd = ERDiagram(
node_style=Styles.PrimaryNeutral.patch(
text_color=Styles.Dark.text_color, text_size=10.0
),
header_style=Styles.PrimaryNeutral.patch(text_size=10.5),
edge_style=Styles.DarkBold,
edge_text_style=Styles.PrimaryBold.patch(text_size=10.0),
)
wf_ent = erd.add(
Entity(
name="workflow_runs",
width=70.0,
header_height=4.8,
row_height=3.1,
header_style=Styles.PrimaryFlat.patch(text_color=Colors.White, text_size=10.5),
),
xy=(42.5, 69.0),
)
wf_ent.add_column("run_id", type="TEXT", pk=True)
wf_ent.add_column("workflow_id", type="TEXT", nullable=False)
wf_ent.add_column("category", type="TEXT", nullable=False)
wf_ent.add_column("based_on", type="TEXT")
wf_ent.add_column("based_on_run_id", type="TEXT")
wf_ent.add_column("status", type="TEXT", nullable=False)
wf_ent.add_column("targets_json", type="TEXT", nullable=False)
wf_ent.add_column("outputs_json", type="TEXT", nullable=False)
wf_ent.add_column("updated_at", type="TEXT", nullable=False)
act_ent = erd.add(
Entity(
name="action_runs",
width=70.0,
header_height=4.8,
row_height=3.1,
header_style=Styles.SecondaryNeutral.patch(
text_color=Styles.Dark.text_color, text_size=10.5
),
),
xy=(42.5, 21.0),
)
act_ent.add_column("run_id", type="TEXT", pk=True, fk=True)
act_ent.add_column("action_id", type="TEXT", pk=True)
act_ent.add_column("seq", type="INTEGER", nullable=False)
act_ent.add_column("primitive_type", type="TEXT", nullable=False)
act_ent.add_column("source_action", type="TEXT")
act_ent.add_column("status", type="TEXT", nullable=False)
act_ent.add_column("deterministic_output_json", type="TEXT")
act_ent.add_column("agent_input_json", type="TEXT")
wf_ent.connect(
act_ent,
cardinality="1:1..*",
start_side="bottom",
end_side="top",
label="1..N steps",
)
erd.draw(xy=(0.0, 0.0))
right_panel = Panel(
"4-State RunStatus Lifecycle (Action & Run FSM)",
header_variant="filled",
header_height=8.5,
style=Styles.Neutral.patch(shape_r=2.0),
header_style=Styles.PrimaryNeutral,
text_style=Styles.PrimaryBold.patch(text_size=11.0),
icon=phosphor.git_commit,
icon_style=Styles.PrimaryBold,
)
right_panel.draw(xy=(86.0, 3.0), width=95.0, height=96.0)
sd = StateDiagram(
node_style=Styles.SecondaryNeutral.patch(
text_color=Styles.Dark.text_color, shape_r=1.8, text_size=10.0
),
edge_style=Styles.DarkBold,
edge_text_style=Styles.DarkBold.patch(text_size=10.0),
)
s_init = sd.add(InitialState(), xy=(102.0, 76.5))
s_pend = sd.add(
State(
"pending",
shape="box",
entry="INSERT act-01..NN",
width=35.0,
style=Styles.SecondaryNeutral.patch(
text_color=Styles.Dark.text_color, shape_r=1.8, text_size=10.0
),
),
xy=(134.0, 76.5),
)
s_prog = sd.add(
State(
"in_progress",
shape="box",
entry="run analyzer once",
do="validate push payload",
width=39.0,
style=Styles.PrimaryNeutral.patch(shape_r=1.8, text_size=10.0),
),
xy=(134.0, 49.5),
)
s_done = sd.add(
State(
"completed",
shape="box",
entry="save agent_input_json",
width=35.0,
style=Styles.SuccessNeutral.patch(shape_r=1.8, text_size=10.0),
),
xy=(110.0, 20.5),
)
s_canc = sd.add(
State(
"cancelled",
shape="box",
entry="abort remaining",
width=33.0,
style=Styles.DangerNeutral.patch(shape_r=1.8, text_size=10.0),
),
xy=(156.0, 20.5),
)
s_final = sd.add(FinalState(), xy=(110.0, 7.5))
sd.connect(s_init, s_pend, start_side="right", end_side="left")
sd.connect(
s_pend,
s_prog,
event="get-action",
bend=-0.12,
start_side="bottom",
end_side="top",
text_style=Styles.DarkBold.patch(text_size=10.0, halign="left"),
)
sd.connect(s_prog, s_prog, side="left", event="invalid")
sd.connect(
s_prog,
s_done,
event="valid push",
bend=0.14,
start_side="bottom",
end_side="top",
style=Styles.SuccessBold,
text_style=Styles.DarkBold.patch(text_size=10.0, halign="right"),
)
sd.connect(
s_pend,
s_canc,
event="supersede",
bend=-0.36,
start_side="right",
end_side="top",
style=Styles.DangerBold,
text_style=Styles.DarkBold.patch(text_size=10.0, halign="left"),
)
sd.connect(
s_prog,
s_canc,
event="cancel",
bend=0.14,
start_side="bottom",
end_side="top",
style=Styles.DangerBold,
text_style=Styles.DarkBold.patch(text_size=10.0, halign="right", valign="top"),
)
sd.connect(s_done, s_final, start_side="bottom", end_side="top")
sd.draw(xy=(0.0, 0.0))
save()
Key Table Responsibilities, Rewind & Cancellation Semantics
workflow_runs: Stores session-level provenance, includingrun_id(wf-<8hex>),workflow_id,category(audit,migration, orprogress),based_on(parent workflow ID),based_on_run_id(the exactwf-<8hex>audit run pinned atstarttime), frozentargets_json(WorkflowTargets), and resolvedoutputs_json(WorkflowOutputs). New workflow runs start immediately inin_progress.action_runs: Stores the ordered1..Nflattened primitive steps (PRIMARY KEY (run_id, action_id)), capturingrules_json,params_json,prompt,deterministic_output_json(populated on firstget-action), andagent_input_json(populated on validpush-action).- Four-State Lifecycle (
RunStatus) & Rewind:pending->in_progress->completedforms the standard forward progression for everyaction_runsrow.pni workflows rewind <run_or_workflow> <action> [--pull](alias:retry-action) rewinds a run back to<action>, re-syncs.pni/workflows/<id>.yamlparameters, and clears downstream step outputs for clean re-execution without restarting fromact-01.- Both
pendingandin_progresssteps (along with their parentin_progressworkflow run) can transition tocancelledvia three mechanisms:pni workflows cancel <run_id>: Explicitly aborts an abandonedin_progresssession (completedruns are immutable and raiseValueErrorif cancellation is attempted).pni workflows cancel --superseded [-w <workflow_id>]: Queriesworkflow_runsfor unfinished runs that have been overtaken by a newer non-cancelled run of the sameworkflow_idand marks themcancelled.pni workflows start <workflow_id> --supersede: Proactively cancels any active runs of<workflow_id>before inserting the newwf-<8hex>session.
3. Two-Stage Audit-to-Migration Workflow Chaining (based_on & based_on_run_id)
Large-scale modernization plans fail when an AI agent plans architectural refactoring from scratch without grounded As-Is diagnostics—or when every migration planning iteration has to re-build containers, re-run Trivy vulnerability scans, and re-execute live HTTP penetration probes. pni501 solves both problems through two-stage SQLite workflow chaining:
Source Code
from drawlib.canvas import save, setup
from drawlib.diagrams.architecture import ArchitectureDiagram, Node, NodeGroup, PhosphorIcon
from drawlib.styles import Styles
setup(width=168, height=98, dpi=150)
diag = ArchitectureDiagram(
node_style=Styles.PrimaryBold,
node_text_style=Styles.DarkBold.patch(text_size=10.0),
edge_style=Styles.DarkBold,
edge_text_style=Styles.DarkBold.patch(text_size=10.0),
node_card_style=Styles.PrimaryNeutral.patch(
text_color=Styles.Dark.text_color, shape_r=1.5
),
)
g_stage1 = diag.add(
NodeGroup(
title="Stage 1: Base Audit Workflow (category: audit, wf-da941741 = COMPLETED)",
width=162.0,
height=31.5,
style=Styles.Neutral.patch(shape_r=2.0),
text_style=Styles.PrimaryBold.patch(text_size=10.5, halign="left", valign="top"),
),
xy=(3.0, 62.5),
)
s1_static = g_stage1.add(
Node(
"ProjectFacts &\nAstFacts (6 Langs)",
width=34.0,
height=18.0,
icon=PhosphorIcon.TREE_STRUCTURE,
icon_size=5.4,
),
xy=(21.5, 12.5),
)
s1_dyn = g_stage1.add(
Node(
"Runtime, Security &\nPentestFacts",
width=36.0,
height=18.0,
icon=PhosphorIcon.SHIELD_WARNING,
icon_size=5.4,
),
xy=(61.0, 12.5),
)
s1_arch = g_stage1.add(
Node(
"ArchitectureRecord\n(3 Grounded Aspects)",
width=36.0,
height=18.0,
icon=PhosphorIcon.GRAPH,
icon_size=5.4,
),
xy=(102.0, 12.5),
)
s1_score = g_stage1.add(
Node(
"7-Axis ScoreResult &\nAudit Report PDF",
width=34.0,
height=18.0,
icon=PhosphorIcon.MEDAL,
icon_size=5.4,
card_style=Styles.SecondaryNeutral.patch(shape_r=1.5),
),
xy=(141.5, 12.5),
)
n_bridge = diag.add(
Node(
"workflow_state.db Link (based_on -> based_on_run_id)\n_assemble_facts_bundle() Inherits All Parent As-Is Facts",
width=96.0,
height=13.5,
icon=PhosphorIcon.DATABASE,
icon_size=5.4,
style=Styles.WhiteBold,
text_style=Styles.WhiteBold.patch(text_size=10.2),
card_style=Styles.PrimaryFlat.patch(shape_r=1.6),
),
xy=(84.0, 49.0),
)
g_stage2 = diag.add(
NodeGroup(
title="Stage 2: Migration Workflow (category: migration, based_on: <audit_id>)",
width=162.0,
height=31.5,
style=Styles.SecondaryNeutral.patch(shape_r=2.0),
text_style=Styles.DarkBold.patch(text_size=10.5, halign="left", valign="top"),
),
xy=(3.0, 4.0),
)
s2_mig = g_stage2.add(
Node(
"migration_analyze\nWaves 1..4, SCC Cuts,\nContracts & DEC-XX",
width=45.0,
height=18.5,
icon=PhosphorIcon.GIT_DIFF,
icon_size=5.4,
),
xy=(27.5, 12.5),
)
s2_plan = g_stage2.add(
Node(
"emit_migration_plan\nValidate Grounding &\n*_migration_plan.json",
width=46.0,
height=18.5,
icon=PhosphorIcon.CHECK_SQUARE_OFFSET,
icon_size=5.4,
card_style=Styles.PrimaryNeutral.patch(shape_r=1.5),
),
xy=(81.5, 12.5),
)
s2_pdf = g_stage2.add(
Node(
"write_migration_report\n7-Chapter Migration\nExecution Plan PDF",
width=45.0,
height=18.5,
icon=PhosphorIcon.FILE_PDF,
icon_size=5.4,
style=Styles.SuccessBold,
card_style=Styles.SuccessNeutral.patch(shape_r=1.5),
),
xy=(135.5, 12.5),
)
diag.connect(s1_static, s1_dyn, routing="orthogonal")
diag.connect(s1_dyn, s1_arch, routing="orthogonal")
diag.connect(s1_arch, s1_score, routing="orthogonal")
diag.connect(g_stage1, n_bridge, routing="orthogonal", style=Styles.PrimaryBold)
diag.connect(n_bridge, g_stage2, routing="orthogonal", style=Styles.PrimaryBold)
diag.connect(s2_mig, s2_plan, routing="orthogonal")
diag.connect(s2_plan, s2_pdf, routing="orthogonal", style=Styles.SuccessBold)
diag.draw(xy=(0.0, 0.0))
save()
End-to-End Chaining Mechanics (engine.py & _migration_engine.py)
- Pinning
based_on_run_idat Session Start:- When
pni workflows start <migration_id>executes,inspect_base_audit_status()locates the most recentlycompletedrun ofworkflow_def.based_oninworkflow_state.db(e.g.,wf-da941741) and records its exact identifier inworkflow_runs.based_on_run_id. - Pinning
based_on_run_idguarantees full reproducibility: even if someone later starts a new in-progress audit run, the active migration session remains bound to the exact completed audit snapshot it was validated against.
- When
- Automatic Fact Bundle Reconstruction (
_assemble_facts_bundle):- On every
get-actionandpush-actioninside Stage 2,_assemble_facts_bundle(run, actions, db=db)first extracts any facts produced in the current migration run, then queriesdb.get_actions(run.based_on_run_id)to hydrate:bundle.project_facts,bundle.ast_facts,bundle.runtime_facts,bundle.security_facts, andbundle.pentest_factsfrom the parent audit'sdeterministic_output_jsoncolumns.bundle.architecture_recordreconstructed across the parent audit's completedrecord_architecturesteps (purpose_and_stack,layers_and_modules,flows_and_state).bundle.base_score_resultdeserialized from the parent audit'semit_scoreagent_input_json, rendered directly into Stage 2 prompts as the Baseline Audit Score Summary (overall_score, 7-axis category scores, and top inherited findings).
- On every
- Deterministic Migration Intelligence (
migration_analyze):- Consuming the inherited
ProjectFacts,AstFacts, andRuntimeFactsalongside the pinned YAMLMigrationOptions(brief,migration_strategy,api_compatibility,bug_handling_policy,codemod_tool),execute_migration_analyze_json()computesMigrationFacts:- Topological Refactoring Waves (
Waves 1..4) & SCC Cycle Cuts: Orders internal modules from zero-dependency leaves (Wave 1) up to ingress/orchestrators (Wave 4) and computes minimum-impactcycle_cutsacross strongly connected components. - Implicit Endpoint & Environment Contracts: Extracts HTTP/CLI request and response keys (
implicit_contracts) plus scattered environment variables and defaults (env_contracts) to seed Phase 0 characterization tests. - Stateful, Database & HTTP Boundary Touchpoints: Locates process-local mutable state, raw SQL calls, and outbound RPCs (
boundary_touchpoints) paired with recommended Clean Architecture Port/Adapter targets. - Mechanical Codemod Recipes &
DEC-XXAmbiguity Candidates: Matches stack-appropriate OpenRewrite, Rector, Ruff, gofix, or jscodeshift recipes (mechanical_codemods) and isolates Hyrum's Law legacy quirks—such as loose equality auth checks, MD5 digests, unsafe cookie deserialization, or hidden debug keys—into numberedDEC-01..NNspec_vs_bug_candidates.
- Topological Refactoring Waves (
- Consuming the inherited
- Grounded Plan Persistence & 7-Chapter Visual Compilation:
- On
emit_migration_plan,validate_and_persist_migration_plan()verifies that every As-Is path referenced intarget_axes[].affected_files,target_architecture.component_mappings[].source_file, anddecision_log[].file_pathexists in the inheritedAstFacts.module_inventoryor workspace, checks thatpr_wbs[].target_filesare safe relative paths, embedsMigrationFacts, and writesreports/<target>_migration_plan.json. - Finally,
write_migration_report(write_drawlib_reportconfigured withreport_kind: "migration") generates.pni/cache/<migration_id>/migration_blueprint.jsonand guides the agent through compiling the 7-chapter (00_cover.md..06_*.md) Drawlib Migration Execution Plan PDF (reports/<target>.pdf, see Drawlib Visual Reporter), verified non-empty beforeworkflow_runs.statustransitions tocompleted.
- On