Skip to content

Commit 019dfe5

Browse files
authored
DNQ 0.9.32: Fix 8 issues + release (#116)
DNQ 0.9.32: Fix 8 issues + release
2 parents a222cae + 5c718c4 commit 019dfe5

21 files changed

Lines changed: 375 additions & 22 deletions

File tree

.shipyard/HISTORY.md

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,34 @@
11
# Shipyard History
22

3+
## 2026-04-16 — Phase 1 Build Complete (DNQ 0.9.32 Release)
4+
5+
- **Action:** `/shipyard:build 1`
6+
- **Plans executed:** 4/4 (PLAN-1.1, PLAN-1.2, PLAN-1.3, PLAN-2.1)
7+
- **Commits:** 4 (ISSUE-016 fix, ISSUE-017 test, ISSUE-020 comment, release bump)
8+
- `54477f41` — eliminate redundant Redis round-trip (ISSUE-016)
9+
- `ac91a41e` — add DidNotReceive assertion for CompletedUtc (ISSUE-017)
10+
- `4a474f10` — document double-dispose safety (ISSUE-020)
11+
- `7f13b391` — bump version to 0.9.32 + CHANGELOG
12+
- **Reviews:** All 4 plans PASS (0 critical, 0 important across all reviews)
13+
- **Verification:** 2,361 tests green (896 core + 190 Redis + 57 Memory integration + transport suites)
14+
- **Security audit:** PASS — no findings
15+
- **Simplification:** Clean — zero findings
16+
- **ISSUES.md:** 016, 017, 019, 020 moved to Resolved
17+
- **Status:** Phase 1 complete, ready for PR + release
18+
19+
## 2026-04-16 — Phase 1 Planned (DNQ 0.9.32 Release)
20+
21+
- **Action:** `/shipyard:plan 1`
22+
- **Scope:** 4 open issues + release commit (4 of 8 original issues already resolved)
23+
- **Decisions:**
24+
- Plan covers only open issues (016, 017, 019, 020) + release commit
25+
- ISSUE-020: researcher investigated ICreationScope idempotency (confirmed safe)
26+
- **Plans:** 4 plans across 2 waves
27+
- Wave 1 (parallel): PLAN-1.1 (Redis perf + test), PLAN-1.2 (LiteDb comment), PLAN-1.3 (archive artifact)
28+
- Wave 2 (sequential): PLAN-2.1 (version bump + CHANGELOG)
29+
- **Critique verdict:** READY
30+
- **Status:** Planned, ready for build
31+
332
## 2026-03-26 — Project Initialized
433

534
- **Action:** `/shipyard:init`
@@ -794,3 +823,11 @@
794823
- [2026-04-15T16:52:50Z] Phase 4: Phase 4 build complete in worktree; pending CI validation on push (success criteria #1, #2) (complete)
795824
- [2026-04-15T16:53:01Z] Phase 4: Phase 4 build complete in worktree; pending CI validation on push (complete)
796825
- [2026-04-15T19:26:38Z] Phase 4: TaskScheduler 0.4.0 milestone shipped via PR #115 (shipped)
826+
- [2026-04-15T21:27:32Z] Phase 1: Project definition captured, ready for planning (ready)
827+
- [2026-04-16T14:13:15Z] Session ended during build (may need /shipyard:resume)
828+
- [2026-04-16T14:14:19Z] Session ended during build (may need /shipyard:resume)
829+
- [2026-04-16T14:14:29Z] Session ended during build (may need /shipyard:resume)
830+
- [2026-04-16T14:16:14Z] Session ended during build (may need /shipyard:resume)
831+
- [2026-04-16T14:17:51Z] Session ended during build (may need /shipyard:resume)
832+
- [2026-04-16T14:41:52Z] Session ended during build (may need /shipyard:resume)
833+
- [2026-04-16T14:42:44Z] Session ended during build (may need /shipyard:resume)

.shipyard/ISSUES.md

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -65,20 +65,22 @@
6565
### ISSUE-019: Missing SUMMARY-1.1.md artifact for Plan 1.1 (LiteDb history tests)
6666
- **Severity:** Important
6767
- **Source:** Plan 1.1 Review
68-
- **Status:** Open
68+
- **Status:** Resolved — Phase 1 PLAN-1.3, 2026-04-16
6969
- **Files:**
70-
- `.shipyard/phases/1/results/SUMMARY-1.1.md` (missing)
70+
- `.shipyard/phases/1/results/SUMMARY-1.1.md` (created)
7171
- **Description:** The builder did not deposit a SUMMARY artifact before review. The test-run output (pass count, duration, LiteDB bug fix rationale) is unrecorded, breaking the audit trail. The results directory contains `REVIEW-1.2.md` but no `SUMMARY-1.1.md`.
7272
- **Remediation:** Create `.shipyard/phases/1/results/SUMMARY-1.1.md` documenting the test run output and the LiteDB `Get()` workaround rationale.
73+
- **Resolution:** Created SUMMARY-1.1.md documenting all 19 LiteDb history tests and the FindAll+LINQ workaround.
7374

7475
### ISSUE-020: LiteDbHistoryEnabledTests.CleanupAsync may double-dispose _scope after _creation.Dispose()
7576
- **Severity:** Important
7677
- **Source:** Plan 1.1 Review
77-
- **Status:** Open
78+
- **Status:** Resolved — commit 4a474f10, 2026-04-16
7879
- **Files:**
7980
- `Source/DotNetWorkQueue.Dashboard.Api.Integration.Tests/Tests/LiteDbHistoryTests.cs` (lines 208-254, CleanupAsync)
8081
- **Description:** `_scope` is assigned from `_creation.Scope`. `CleanupAsync` calls `_creation.Dispose()` then `_scope.Dispose()` independently. If `ICreationScope` is not idempotent on dispose, this is a double-dispose. The same pattern appears in MemoryHistoryTests, so it is likely safe, but it is unconfirmed.
8182
- **Remediation:** Verify `ICreationScope.Dispose()` is idempotent. If confirmed, add a comment. If not, null `_scope` after `_creation.Dispose()` to guard against double-dispose.
83+
- **Resolution:** Confirmed LiteDb CreationScope.Dispose() is idempotent (guarded by `_disposedValue`). Added 3-line clarifying comment in CleanupAsync.
8284

8385
### ISSUE-014: RelationalDatabase RecordComplete WHERE clause blocks DurationMs=0 write when StartedUtc IS NULL
8486
- **Severity:** Important
@@ -102,20 +104,22 @@
102104
### ISSUE-016: Redundant Redis round-trip in orphan path of PurgeMessageHistoryHandler
103105
- **Severity:** Important
104106
- **Source:** Plan 1.2 Review
105-
- **Status:** Open
107+
- **Status:** Resolved — commit 54477f41, 2026-04-16
106108
- **Files:**
107109
- `Source/DotNetWorkQueue.Transport.Redis/Basic/PurgeMessageHistoryHandler.cs` (Purge method, loop body)
108110
- **Description:** `rawCompleted` is read unconditionally before the `!rawStatus.HasValue` guard. When the hash is absent (orphan case), this is a wasted Redis round-trip returning `RedisValue.Null` that is immediately discarded by `continue`. In bulk orphan scans this doubles Redis calls in the hot path.
109111
- **Remediation:** Move `var rawCompleted = db.HashGet(...)` inside the `rawStatus.HasValue` branch, after the orphan `continue`.
112+
- **Resolution:** Moved `rawCompleted` HashGet from before the orphan guard to after it, eliminating one Redis round-trip per orphan record.
110113

111114
### ISSUE-017: Orphan test does not assert CompletedUtc is never read (fragile test)
112115
- **Severity:** Important
113116
- **Source:** Plan 1.2 Review
114-
- **Status:** Open
117+
- **Status:** Resolved — commit ac91a41e, 2026-04-16
115118
- **Files:**
116119
- `Source/DotNetWorkQueue.Transport.Redis.Tests/Basic/PurgeMessageHistoryHandlerTests.cs` (`Purge_Handles_Missing_Hash_Gracefully`)
117120
- **Description:** The orphan test stubs `Status` to `RedisValue.Null` but does not stub `CompletedUtc` and does not assert it is never called. NSubstitute silently returns default `RedisValue.Null` for the unstubbed call. If the read order changes or the guard moves, the test passes against the wrong code path. Once ISSUE-016 is fixed, add `db.DidNotReceive().HashGet(Arg.Any<RedisKey>(), Arg.Is<RedisValue>("CompletedUtc"), Arg.Any<CommandFlags>())` to make the contract explicit.
118121
- **Remediation:** After applying ISSUE-016 fix, add the `DidNotReceive` assertion for `CompletedUtc` in the orphan test.
122+
- **Resolution:** Added `db.DidNotReceive().HashGet(..., "CompletedUtc", ...)` assertion in `Purge_Handles_Missing_Hash_Gracefully` test.
119123

120124
### ISSUE-018: No test for Enqueued status in PurgeMessageHistoryHandler
121125
- **Severity:** Suggestion

.shipyard/PROJECT.md

Lines changed: 119 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,119 @@
1+
# Project: DNQ Open-Issue Cleanup Milestone (0.9.32)
2+
3+
**Captured:** 2026-04-15
4+
**Branch:** `cleanup-all-open-issues` (off master @ `a222cae0`)
5+
**Shipping target:** DNQ NuGet `0.9.32` (Phase 1) + polish PR (Phase 2)
6+
7+
## Description
8+
9+
Resolve 24 DNQ-local issues accumulated across prior milestones in `.shipyard/ISSUES.md`. The hero is a real correctness bug in `DotNetWorkQueue.Transport.RelationalDatabase.RecordComplete` (ISSUE-014) that silently mis-writes message-history completion metadata when `DurationMs = 0` and `StartedUtc IS NULL`. That alone justifies a point release. Alongside that, ship two performance wins — compile the `ValidateQueueName` regex (ISSUE-002) and eliminate a redundant Redis round-trip in `PurgeMessageHistoryHandler`'s orphan path (ISSUE-016) — as part of a DNQ NuGet `0.9.32` release, published via manual `dotnet nuget push` from the local deploy directory (DNQ's current publishing flow — tag-triggered GH Actions publishing is not wired up for DNQ yet and is deferred to a future milestone).
10+
11+
After the release PR merges, a follow-up polish PR lands the remaining 16 Suggestion-level issues covering unused usings, stale XML doc, shipyard artifact backfills, dead local functions, empty NETFULL shell files, and an `OpenTelemetry.TracerProvider` leak in a shared test fixture.
12+
13+
The milestone is scoped to DNQ-local code only. 6 issues in the sibling `TaskScheduler` repo (025, 026, 027, 028, 029, 030) are explicitly out of scope and tracked separately. (ISSUE-027 test helper DRY and ISSUE-029 GH Actions Node.js 20 deprecation were initially thought DNQ-local but audit confirmed both are tagged `Repo: DotNetWorkQueue.TaskScheduling.Distributed.TaskScheduler`.)
14+
15+
## Goals
16+
17+
1. Ship DNQ NuGet `0.9.32` to nuget.org containing the 8 Important-severity fixes via manual `dotnet nuget push` from the local deploy directory (DNQ's current release pattern — `.nupkg` and `.snupkg` co-pushed from `deploy/`). A GH Actions tag-triggered publish workflow is a nice-to-have deferred to a future milestone.
18+
2. Burn down the remaining 16 Suggestion-level issues in one follow-up PR without requiring another release.
19+
3. Preserve full test-suite green state throughout: 896/896 core unit tests, 57/57 Memory integration tests, plus all relational transport integration suites that Jenkins has live services for.
20+
4. Confirm Jenkins full 14-stage parallel matrix + GH Actions `build-and-test` job stay green on every phase PR before merge.
21+
5. Close 24 DNQ-local entries from `.shipyard/ISSUES.md` (Open → Resolved), leaving only the 6 sibling-repo entries and any new discoveries surfaced mid-milestone.
22+
23+
## Non-Goals
24+
25+
1. **Sibling `TaskScheduler` repo issues** — all 6 carry a `Repo: DotNetWorkQueue.TaskScheduling.Distributed.TaskScheduler` tag in `ISSUES.md`:
26+
- **ISSUE-025** RunPoller start race on fast `Start() → Dispose()` cycles
27+
- **ISSUE-026** `NetMqQueueApiProbeTests.cs` design-time scaffolding to delete
28+
- **ISSUE-027** Test helper DRY (`XunitLogger` / `NextPort` / `BeaconInterface` copied across 4 test files)
29+
- **ISSUE-028** `Start()` `<remarks>` XML doc (apparently closed in Phase 2 release commit but not removed from Open section)
30+
- **ISSUE-029** GH Actions deprecated Node.js 20 actions in the sibling's `ci.yml`
31+
- **ISSUE-030** Sibling README uses wrong named arg `udpBroadcastPort:` instead of `broadCastPort:`
32+
33+
Out of scope; tracked for a separate sibling-repo PR whenever there's a reason to ship `TaskScheduler 0.5.0`.
34+
2. **Architectural refactors** — no API changes, no namespace moves, no new abstractions. Fix / perf / cleanup in place only.
35+
3. **New features or API surface** — no new public types, no new methods, no new extension points. Behavioral corrections and test-side improvements only.
36+
4. **Pre-existing `SYSLIB0012` warnings** in `LiteDB.IntegrationTests/ConnectionString.cs` and `SQLite.Integration.Tests/ConnectionString.cs` — flagged previously as a cleanup target, but explicitly NOT in the 30 tracked issues. Let them linger one more cycle.
37+
5. **Major version bump**`0.9.31 → 0.9.32` patch release only, not `0.10.0`.
38+
6. **Phase 1 feature additions** — the release PR is strictly correctness + perf + test fixes. No scope creep into "while I'm here, let me add X."
39+
40+
## Requirements (Functional, Grouped by Phase)
41+
42+
### Phase 1 — 0.9.32 Release (8 Important issues + release commit)
43+
44+
One wave, one PR, one release. Each issue lands as its own atomic commit on branch `cleanup-all-open-issues`. Final commit in Phase 1 is the release commit.
45+
46+
**Correctness / Release-critical trio**
47+
- **ISSUE-014** — Fix `RelationalDatabase.RecordComplete` WHERE clause so `DurationMs = 0` rows write correctly when `StartedUtc IS NULL`. Add a regression test that reproduces pre-fix failure and passes post-fix. Verify fix applies across SqlServer, PostgreSQL, SQLite transports.
48+
- **ISSUE-002** — Compile the `ValidateQueueName` regex across all relational transports (use `RegexOptions.Compiled` or `[GeneratedRegex]`). Verify no existing test asserts the regex is non-compiled.
49+
- **ISSUE-016** — Eliminate redundant Redis round-trip in orphan path of `PurgeMessageHistoryHandler`. Confirm via existing test that behavior is unchanged; add assertion if the test currently doesn't catch the round-trip.
50+
51+
**Test reliability**
52+
- **ISSUE-017** — Add `CompletedUtc` non-read assertion in the orphan test so it's not fragile.
53+
- **ISSUE-020** — Fix `LiteDbHistoryEnabledTests.CleanupAsync` double-dispose of `_scope` after `_creation.Dispose()`.
54+
- **ISSUE-022** — Fix the no-op `dynamic=true` test case in PostgreSQL `JobSchedulerTests` so it actually asserts the dynamic code path.
55+
56+
**Housekeeping**
57+
- **ISSUE-001** — Remove unused `fixture` variable in `QueueCreatorTests` post Plan 1.1 refactor.
58+
- **ISSUE-019** — Backfill the missing `SUMMARY-1.1.md` shipyard artifact for the LiteDb history tests phase (archive-only, no code impact).
59+
60+
**Release commit (final commit of Phase 1)**
61+
- Bump `<Version>` in `Source/Directory.Build.props` from `0.9.31` to `0.9.32`.
62+
- Add `## 0.9.32 (2026-04-XX)` section to `CHANGELOG.md` summarizing the 8 issues with ISSUE-* references.
63+
- Update `README.md` if it carries a "current version" mention.
64+
- After the PR merges to master, build Release locally with `-p:CI=true`, pack to `deploy/`, manually publish via `dotnet nuget push "deploy/*.nupkg" --api-key <KEY> --source https://api.nuget.org/v3/index.json` (the CLI auto-picks up the matching `.snupkg`). Then tag `v0.9.32` locally and push the tag for release traceability. There is no tag-triggered GH Actions publish workflow for DNQ today — that's deferred to a future milestone.
65+
66+
### Phase 2 — Polish & Cleanup (16 issues)
67+
68+
One PR against updated master. No release. Architect splits into waves by file cluster at plan time.
69+
70+
- **Suggestion (16):** ISSUE-003 through 013 (unused usings, stale XML doc, `DisposeAsync` patterns, log message text, parens clarity, missing SUMMARYs), 015 (dead local function in `RecordComplete_WithoutStartedUtc_PassesDurationZero` test), 018 (missing test for Enqueued status in `PurgeMessageHistoryHandler`), 021 (empty NETFULL shell files), 023 (blank line artifacts from NETFULL removal), 024 (`OpenTelemetry.TracerProvider` leak in `SharedSetup.CreateTrace`).
71+
72+
## Non-Functional Requirements
73+
74+
- **Test green:** no regression in 896/896 core unit tests + 57/57 Memory integration tests on every PR.
75+
- **CI green:** Jenkins 14-stage parallel matrix + GH Actions `build-and-test` job green on every PR before merge.
76+
- **Release integrity:** `0.9.32` ships with deterministic Source Link paths (`-p:CI=true`), Symbols visible on nuget.org, version ordering preserved (`0.9.31 < 0.9.32`).
77+
- **Per-issue atomic commits:** each ISSUE-NNN resolution is a single commit so git blame / bisect can attribute individual fixes. Commit messages reference the ISSUE-NNN ID.
78+
- **No scope creep:** if the planner / researcher discovers related issues mid-plan, they go into `.shipyard/ISSUES.md` as new entries, NOT into the current milestone's plan.
79+
80+
## Success Criteria
81+
82+
1. **Phase 1:** DNQ NuGet `0.9.32` live on nuget.org with green Symbols + deterministic Source Link badges. Tag `v0.9.32` on master. CHANGELOG contains the 8-issue release notes. All 8 Important ISSUE-* refs moved from Open to Resolved in `.shipyard/ISSUES.md`.
83+
2. **Phase 1:** Jenkins + GH Actions green on the PR before merge; 896/896 core unit tests + 57/57 Memory integration tests + all Jenkins relational transport stages green.
84+
3. **Phase 2:** 16 remaining DNQ-local issues resolved and moved from Open to Resolved. Jenkins + GH Actions green on the PR. No release. No regressions.
85+
4. **Milestone:** `.shipyard/ISSUES.md` Open section contains only the 6 sibling-repo entries (025, 026, 027, 028, 029, 030) plus any entries opened mid-milestone from new discoveries. Everything DNQ-local that was open at milestone start is closed.
86+
87+
## Constraints
88+
89+
**Technical**
90+
- net10.0 / net8.0 multi-target preserved for library projects; integration test projects remain `net10.0`-only (CLAUDE.md convention).
91+
- Central Package Management (CPM) pattern preserved — no direct `Version=` attributes on `PackageReference`.
92+
- `-p:CI=true` required on the Release build so Source Link paths are deterministic.
93+
- Push `.nupkg` + `.snupkg` together from the deploy directory — `.snupkg` can't be pushed separately after the `.nupkg` is live.
94+
- Phase 1 tag format: `v0.9.32` (annotated, unsigned, matching prior release convention).
95+
- API key stays in GH Secrets. No local `dotnet nuget push`.
96+
97+
**Workflow**
98+
- Jenkins is PR-triggered, not branch-triggered. Each phase MUST open a draft PR to trigger CI. Merge only after both CI surfaces go green.
99+
- Per-issue atomic commits; per-phase single PR; per-milestone two PRs total (release PR + polish PR).
100+
- Pre-release local pack + `.nupkg` inspection pre-tag to catch Source Link / Symbols red badges before they hit nuget.org.
101+
102+
**Scope**
103+
- Sibling `TaskScheduler` repo is out of scope. Any fix that requires touching `/mnt/f/Git/DotNetWorkQueue.TaskScheduling.Distributed.TaskScheduler/` goes into a separate Shipyard instance or `/shipyard:quick` in that repo.
104+
- No architectural refactors. Fix in place.
105+
106+
## Risks & Mitigations
107+
108+
| Risk | Mitigation |
109+
|---|---|
110+
| ISSUE-014 fix requires a schema assumption we can't hold. | Architect verifies during `/shipyard:plan 1` Research: trace `RecordStart → RecordComplete` in all 3 relational transports, confirm the WHERE clause is the right fix site. |
111+
| `0.9.32` release fails verification on nuget.org (Symbols red, Source Link red). | Mirror the TaskScheduler Phase 2 pre-flight pattern: local clean pack + `.nupkg` inspection before tagging. |
112+
| Phase 2's 16-issue PR too large to review. | Architect splits Phase 2 into waves by file cluster (unused-usings sweep, shipyard artifact backfills, OpenTelemetry leak fix). |
113+
| Version-ordering goof (e.g., tagging `v0.9.4`). | CLAUDE.md lesson captured; release plan task explicitly asserts the tag is `v0.9.32`. |
114+
| Integration tests for SqlServer/PostgreSQL/Redis/LiteDb/SQLite need external services the dev machine doesn't run. | Phase 1 local verification runs only in-memory suites (core unit + Memory integration). Jenkins handles external-service suites as the hard gate — matches the Phase 3 TaskScheduler milestone pattern. |
115+
116+
## Related Milestones
117+
118+
- **Prior milestone:** `TaskScheduler 0.4.0 + DNQ Integration Tests + CI Wiring` shipped via PR #115 as `190f1226` + ship commit `ddc8daf0` + cleanup commit `a222cae0`. 4 phases, 2 repos, resolved issues 028 and several others.
119+
- **Next milestone (tentative):** sibling `TaskScheduler 0.5.0` if/when the 4 sibling-repo issues justify another release.

0 commit comments

Comments
 (0)