nc_program_manager/.paul/phases/11-refactor/11-02-PLAN.md
dtrentin bd17ea46e5 feat(11-refactor): extract ArgParser, fix error handling, remove dead code
Phase 11 complete:
- ArgParser.cs: CLI parsing extracted from Program.cs (pure static method)
- Program.cs: no static state; Scarica/Invia take explicit InputArgs param
- Program.cs: try/finally with connected flag — fixes connection leak on exception
- Program.cs: PrintErrors helper surfaces CncResult.Errors on every failure
- AesCrypt.cs: deleted (dead code, zero references)
- NcProgramManager.csproj: AesCrypt.cs → ArgParser.cs

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-05-18 00:01:32 +02:00

153 lines
5.1 KiB
Markdown

---
phase: 11-refactor
plan: 02
type: execute
wave: 2
depends_on: ["11-01"]
files_modified:
- Program.cs
autonomous: true
---
<objective>
## Goal
Fix `Scarica` and `Invia` in Program.cs: use `try/finally` for guaranteed disconnect and print `CncResult.Errors` on every failure.
## Purpose
Current code manually calls `DisconnectAsync` before each error return — but the `catch` block has no disconnect, so any unhandled exception after connect leaks the connection. Also, `CncResult.Errors` is never printed; operators get a numeric code with no machine-level detail.
## Output
- `Program.cs` — both methods refactored: single `try/catch/finally` with `connected` flag; all `CncResult` failures print errors before returning
- Private helper `PrintErrors(IReadOnlyList<CncError>)` added
</objective>
<context>
@.paul/PROJECT.md
@Program.cs
@.paul/phases/11-refactor/11-01-SUMMARY.md
</context>
<acceptance_criteria>
## AC-1: Disconnect always runs after connect
```gherkin
Given Scarica or Invia successfully calls ConnectAsync
When any exception is thrown during the operation
Then DisconnectAsync is called exactly once (via finally block)
```
## AC-2: No manual disconnect before early returns
```gherkin
Given Scarica or Invia
When read
Then no call to DisconnectAsync exists inside the try block — only in the finally
```
## AC-3: CncResult errors printed on failure
```gherkin
Given any CncResult<T> with Success == false and non-empty Errors list
When the result is checked and found failed
Then each error is printed via CncError.ToString() before VediErrore is called
```
</acceptance_criteria>
<tasks>
<task type="auto">
<name>Task 1: Add PrintErrors helper + refactor Scarica</name>
<files>Program.cs</files>
<action>
Add private static helper at bottom of Program class:
```
static void PrintErrors(System.Collections.Generic.IReadOnlyList<CncError> errors)
{
if (errors == null) return;
foreach (var e in errors)
Console.WriteLine(" " + e.ToString());
}
```
Refactor Scarica:
1. Declare `bool connected = false;` before try.
2. Wrap ALL operation logic in a single `try { } catch (Exception ex) { } finally { }`.
3. After `ConnectAsync` succeeds, set `connected = true`.
4. Remove ALL `macchina.DisconnectAsync().GetAwaiter().GetResult()` calls from inside the try block.
5. In `finally`: `if (connected) macchina.DisconnectAsync().GetAwaiter().GetResult();`
6. After every `!result.Success` check, call `PrintErrors(result.Errors)` before `VediErrore`.
Early-return on `!connResult.Success` does NOT set connected=true, so finally skips disconnect — correct.
Avoid: catching and swallowing the disconnect exception in finally (let it propagate naturally).
</action>
<verify>
Read Program.cs Scarica method:
- `connected` flag declared
- `finally { if (connected) macchina.DisconnectAsync()... }` present
- Zero `DisconnectAsync` calls inside try block
- `PrintErrors` called before each `VediErrore(-2XX)` that follows a CncResult check
</verify>
<done>AC-1, AC-2, AC-3 satisfied for Scarica</done>
</task>
<task type="auto">
<name>Task 2: Refactor Invia with same pattern</name>
<files>Program.cs</files>
<action>
Apply identical refactor to Invia:
1. Declare `bool connected = false;` before try.
2. Single `try/catch/finally` — set `connected = true` after `ConnectAsync` succeeds.
3. Remove ALL `macchina.DisconnectAsync()` calls from inside try.
4. `finally { if (connected) macchina.DisconnectAsync().GetAwaiter().GetResult(); }`
5. Call `PrintErrors(result.Errors)` before each `VediErrore` that follows a failed CncResult.
Note: `File.ReadAllText` in Invia can throw IOException — the finally will still run and disconnect safely.
</action>
<verify>
Read Program.cs Invia method:
- Same structure as Scarica post-refactor
- Zero `DisconnectAsync` calls inside try
- `PrintErrors` called on all failed CncResult checks
</verify>
<done>AC-1, AC-2, AC-3 satisfied for Invia</done>
</task>
</tasks>
<boundaries>
## DO NOT CHANGE
- ArgParser.cs
- InputArgs.cs
- Cnc/* — all machine implementation files
- Licensing/*
- VediErrore content or error codes
- MostraAiuto
- NcProgramManager.Tests/*
## SCOPE LIMITS
- Only Program.cs touched
- Do not change method signatures
- Do not add new error codes
- Do not change the connection/operation logic — only the cleanup and error display
</boundaries>
<verification>
Before declaring plan complete:
- [ ] grep "DisconnectAsync" Program.cs — all results inside `finally` blocks only
- [ ] grep "PrintErrors" Program.cs — called before every VediErrore that follows a CncResult check
- [ ] `connected` flag declared in both Scarica and Invia
- [ ] Project compiles (no new errors)
</verification>
<success_criteria>
- Both Scarica and Invia use try/catch/finally with connected flag
- DisconnectAsync called only in finally
- PrintErrors called on every CncResult failure
- Zero compilation errors
</success_criteria>
<output>
After completion, create `.paul/phases/11-refactor/11-02-SUMMARY.md`
</output>