From 294da8b2db9b6a824517622a2fdf6b0739649f17 Mon Sep 17 00:00:00 2001 From: Joseph Doherty Date: Fri, 19 Jun 2026 05:36:24 -0400 Subject: [PATCH] docs(sphistorianclient): implementation plan + task tracking --- docs/plans/2026-06-19-sphistorianclient.md | 569 ++++++++++++++++++ ...2026-06-19-sphistorianclient.md.tasks.json | 13 + 2 files changed, 582 insertions(+) create mode 100644 docs/plans/2026-06-19-sphistorianclient.md create mode 100644 docs/plans/2026-06-19-sphistorianclient.md.tasks.json diff --git a/docs/plans/2026-06-19-sphistorianclient.md b/docs/plans/2026-06-19-sphistorianclient.md new file mode 100644 index 0000000..969c029 --- /dev/null +++ b/docs/plans/2026-06-19-sphistorianclient.md @@ -0,0 +1,569 @@ +# ZB.MOM.WW.SPHistorianClient Implementation Plan + +> **For Claude:** REQUIRED SUB-SKILL: Use superpowers-extended-cc:executing-plans (or subagent-driven-development) to implement this plan task-by-task. + +**Goal:** Repackage the proven, pure-managed .NET 10 `AVEVA.Historian.Client` SDK from the migration bundle as the family-branded shared library `ZB.MOM.WW.SPHistorianClient`, following the same conventions as the other `ZB.MOM.WW.*` libraries in this repo. + +**Architecture:** This is a **port + rebrand**, not a rewrite. Copy the SDK `src/` and `tests/` into a new `ZB.MOM.WW.SPHistorianClient/` directory, rewrite the C# root namespace `AVEVA.Historian.Client` → `ZB.MOM.WW.SPHistorianClient` (leaving the proto-generated `ArchestrA.Grpc.Contract.*` wire contracts untouched), adopt ZB conventions (`Directory.Build.props` / `Directory.Packages.props` central package management, `.slnx`, `CLAUDE.md`/`README.md`), drop the non-shippable reverse-engineering tooling and proprietary decompilations, add one ZB-idiomatic DI extension, then build/test/pack. + +**Tech Stack:** .NET 10, C# (net10.0), WCF/MDAS (`System.ServiceModel.*`, Windows-only transports), gRPC (`Grpc.Net.Client` + `Grpc.Tools`, cross-platform 2023 R2 transport), xUnit. Central package management. + +**Design doc:** `docs/plans/2026-06-19-sphistorianclient-design.md` + +**Branch:** `feat/sphistorianclient` (already created; design doc already committed at `bbb7942`). + +--- + +## Source bundle location (read-only inputs) + +The SDK source lives in an extracted bundle under `/tmp`: + +- Extracted root: `/tmp/histsdk/extracted/histsdk-migration/histsdk/` +- SDK source: `…/histsdk/src/AVEVA.Historian.Client/` — **74 `.cs` + 6 `.proto`** +- SDK tests: `…/histsdk/tests/AVEVA.Historian.Client.Tests/` — **25 `.cs`** +- Re-extract fallback (if `/tmp` was cleaned): `cd /tmp/histsdk && unzip -o -q histsdk-migration.zip -d extracted` + +**Never copy:** `tools/` (RE harnesses, .NET Framework + native AVEVA refs), `analysis-2023r2/decompiled/` (proprietary, non-redistributable), `scripts/`, `docs/reverse-engineering/` (identity-bearing captures), `bin/`/`obj/`, the bundle's `.git/`, and the bundle's original `.csproj` files (we author fresh ZB ones). + +**Gotchas baked into this plan (from prior repo experience):** +- Do **not** set `TreatWarningsAsErrors` — the WCF/SSPI code carries `[SupportedOSPlatform("windows")]` and will emit CA platform warnings on macOS that must stay warnings. +- Central package management means **no inline `Version=` on any `PackageReference`** (that is `NU1008`). All versions live in `Directory.Packages.props`. +- `Microsoft.Data.SqlClient` may surface an `NU1903` advisory on restore. Without `TreatWarningsAsErrors` it is a warning. If a restore ever hard-fails on it, add `-p:NuGetAudit=false` to the build/test command. +- macOS `sed -i` requires an explicit empty backup arg: `sed -i '' 's/…/…/g'`. + +--- + +## Task 1: Scaffold the library skeleton + +**Classification:** standard +**Estimated implement time:** ~4 min +**Parallelizable with:** none (every later task depends on this) + +**Files:** +- Create: `ZB.MOM.WW.SPHistorianClient/Directory.Build.props` +- Create: `ZB.MOM.WW.SPHistorianClient/Directory.Packages.props` +- Create: `ZB.MOM.WW.SPHistorianClient/.gitignore` +- Create: `ZB.MOM.WW.SPHistorianClient/ZB.MOM.WW.SPHistorianClient.slnx` +- Create: `ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/ZB.MOM.WW.SPHistorianClient.csproj` +- Create: `ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj` + +**Step 1: `Directory.Build.props`** (mirrors `ZB.MOM.WW.Telemetry/Directory.Build.props`) + +```xml + + + + net10.0 + enable + enable + latest + 0.1.0 + true + + + +``` + +**Step 2: `Directory.Packages.props`** (versions lifted verbatim from the bundle's two `.csproj` files) + +```xml + + + + true + + + + + + + + + + + + + + + + + + + + + + + + + + +``` + +**Step 3: `.gitignore`** + +```gitignore +bin/ +obj/ +# identity-bearing / non-redistributable — never commit +*.ndjson +current/ +aveva-install-*/ +``` + +**Step 4: `ZB.MOM.WW.SPHistorianClient.slnx`** + +```xml + + + + + + + + +``` + +**Step 5: `src/ZB.MOM.WW.SPHistorianClient/ZB.MOM.WW.SPHistorianClient.csproj`** + +(Derived from the bundle's `AVEVA.Historian.Client.csproj`: inline versions removed for central +management; ZB package metadata added; `InternalsVisibleTo` retargeted to the ZB test assembly and +the `…ReverseEngineering` one dropped; proto glob uses forward slashes for cross-platform MSBuild.) + +```xml + + + + true + ZB.MOM.WW.SPHistorianClient + ZB.MOM.WW + Pure-managed .NET 10 client for AVEVA System Platform Historian (Wonderware) for the ZB.MOM.WW SCADA family. The wire protocol is reverse-engineered and re-implemented in C# — no native AVEVA runtime dependency. Surfaces history reads (raw / aggregate / at-time / event), tag browse + metadata, status, and tag create/delete over the WCF/MDAS transports (Windows) plus a cross-platform gRPC transport for 2023 R2. + aveva;wonderware;historian;system-platform;scada;timeseries;grpc;wcf;zb-mom-ww + https://gitea.dohertylan.com/dohertj2/zb-mom-ww-sphistorianclient + https://gitea.dohertylan.com/dohertj2/zb-mom-ww-sphistorianclient + + + + + + + + + + + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + + + + + + + <_Parameter1>ZB.MOM.WW.SPHistorianClient.Tests + + + + +``` + +**Step 6: `tests/ZB.MOM.WW.SPHistorianClient.Tests/ZB.MOM.WW.SPHistorianClient.Tests.csproj`** + +```xml + + + + false + + + + + + + + + + + + + + + + + + + +``` + +**Step 7: Verify the skeleton is well-formed (build will fail — no sources yet — that is expected)** + +Run: `cd ZB.MOM.WW.SPHistorianClient && dotnet restore ZB.MOM.WW.SPHistorianClient.slnx` +Expected: restore **succeeds** (proves the props/csproj XML and central package versions resolve). A +follow-up `dotnet build` would fail only because no `.cs` exist yet — do not build here. + +**Step 8: Commit** + +```bash +cd /Users/dohertj2/Desktop/scadaproj +git add ZB.MOM.WW.SPHistorianClient/ +git commit -m "feat(sphistorianclient): scaffold shared library skeleton (props, csprojs, slnx)" +``` + +--- + +## Task 2: Port source + tests with namespace rewrite + +**Classification:** standard +**Estimated implement time:** ~4 min +**Parallelizable with:** none +**Blocked by:** Task 1 + +**Files:** +- Create (scripted copy): `ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/**/*.{cs,proto}` (74 `.cs` + 6 `.proto`) +- Create (scripted copy): `ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/**/*.cs` (25 `.cs`) + +This task is a deterministic copy + namespace rewrite — run the script, then verify counts. + +**Step 1: Copy + rewrite (single script)** + +```bash +set -euo pipefail +BUNDLE=/tmp/histsdk/extracted/histsdk-migration/histsdk +DEST=/Users/dohertj2/Desktop/scadaproj/ZB.MOM.WW.SPHistorianClient + +# Guard: re-extract if /tmp was cleaned +if [ ! -d "$BUNDLE/src/AVEVA.Historian.Client" ]; then + cd /tmp/histsdk && unzip -o -q histsdk-migration.zip -d extracted +fi + +# --- src: copy .cs + .proto, preserving subdirs (NOT the old .csproj) --- +SRC="$DEST/src/ZB.MOM.WW.SPHistorianClient" +cd "$BUNDLE/src/AVEVA.Historian.Client" +find . \( -name '*.cs' -o -name '*.proto' \) | while read -r f; do + mkdir -p "$SRC/$(dirname "$f")" + cp "$f" "$SRC/$f" +done + +# --- tests: copy .cs only (NOT the old .csproj) --- +TST="$DEST/tests/ZB.MOM.WW.SPHistorianClient.Tests" +cd "$BUNDLE/tests/AVEVA.Historian.Client.Tests" +find . -name '*.cs' | while read -r f; do + mkdir -p "$TST/$(dirname "$f")" + cp "$f" "$TST/$f" +done + +# --- namespace rewrite in .cs ONLY (proto wire contracts stay ArchestrA.Grpc.Contract.*) --- +find "$SRC" "$TST" -name '*.cs' -print0 \ + | xargs -0 sed -i '' 's/AVEVA\.Historian\.Client/ZB.MOM.WW.SPHistorianClient/g' +``` + +**Step 2: Verify counts and that the rename is total** + +```bash +DEST=/Users/dohertj2/Desktop/scadaproj/ZB.MOM.WW.SPHistorianClient +echo "src cs: $(find "$DEST/src" -name '*.cs' | wc -l) (expect 74)" +echo "src proto: $(find "$DEST/src" -name '*.proto' | wc -l) (expect 6)" +echo "test cs: $(find "$DEST/tests" -name '*.cs' | wc -l) (expect 25)" +echo "leftover AVEVA.Historian.Client in .cs: $(grep -rl 'AVEVA\.Historian\.Client' "$DEST" --include='*.cs' | wc -l) (expect 0)" +echo "proto namespace preserved: $(grep -l 'ArchestrA.Grpc.Contract' "$DEST"/src/ZB.MOM.WW.SPHistorianClient/Grpc/Protos/*.proto | wc -l) (expect 6)" +``` + +Expected: `74`, `6`, `25`, `0`, `6`. If "leftover" is non-zero, inspect those files — the only legitimate +remaining mentions would be inside comments/strings that happen to differ in casing/spacing; a clean +port should show `0`. + +**Step 3: Commit** + +```bash +cd /Users/dohertj2/Desktop/scadaproj +git add ZB.MOM.WW.SPHistorianClient/src ZB.MOM.WW.SPHistorianClient/tests +git commit -m "feat(sphistorianclient): port SDK source + tests, rebrand namespace to ZB.MOM.WW.SPHistorianClient" +``` + +--- + +## Task 3: Build + test green + +**Classification:** high-risk +**Estimated implement time:** ~5 min (plus restore/build wall-time) +**Parallelizable with:** none +**Blocked by:** Task 2 + +This is the integration gate. The port must compile and the offline test suite must pass on this macOS host. + +**Files:** +- Modify (only if the build surfaces a defect): any ported file under `ZB.MOM.WW.SPHistorianClient/src` or `…/tests`, or the two `.csproj`. + +**Step 1: Build** + +Run: `cd ZB.MOM.WW.SPHistorianClient && dotnet build ZB.MOM.WW.SPHistorianClient.slnx` +Expected: **Build succeeded.** Platform-compatibility (CAxxxx `[SupportedOSPlatform("windows")]`) warnings +are acceptable and must remain warnings. If restore hard-fails on `NU1903`, re-run with +`-p:NuGetAudit=false`. + +**Step 2: Test** + +Run: `dotnet test ZB.MOM.WW.SPHistorianClient.slnx` +Expected: all tests pass; the live integration tests (`HistorianClientIntegrationTests`, +`HistorianGrpcIntegrationTests`, `RemoteTcpIntegrationTests`) **skip cleanly** because no `HISTORIAN_*` +env vars are set. The bundle's `MIGRATION-README.md` documents ~188 tests passing on macOS with the +live ones skipped — treat a comparable count with **zero failures** as success. + +**Step 3: Triage rules (if not green)** +- Compile error referencing `AVEVA.Historian.Client` → a file was missed by the rewrite; re-run the + Task 2 sed on that file. +- `NU1008` (version on PackageReference) → an inline `Version=` slipped into a `.csproj`; remove it + (version belongs in `Directory.Packages.props`). +- Missing generated gRPC type (e.g. `ArchestrA.Grpc.Contract.*` not found) → confirm the `` + glob in the src `.csproj` resolves the 6 `Grpc/Protos/*.proto` and that `Grpc.Tools` restored. +- A genuine test failure (not a skip) → this is a real port defect; fix the ported code, do **not** + delete/weaken the test. + +**Step 4: Commit (only if Step 3 required edits)** + +```bash +git add -A ZB.MOM.WW.SPHistorianClient/ +git commit -m "fix(sphistorianclient): resolve port build/test fallout" +``` + +--- + +## Task 4: Add the `AddZbSpHistorianClient` DI extension (TDD) + +**Classification:** standard +**Estimated implement time:** ~4 min +**Parallelizable with:** Task 5 +**Blocked by:** Task 3 + +`HistorianClientOptions` uses `required` + `init`-only properties, so the extension takes a fully-built +options instance (not an `Action` configurator). It depends only on +`Microsoft.Extensions.DependencyInjection.Abstractions`. + +**Files:** +- Create: `ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/DependencyInjection/ZbSpHistorianClientServiceCollectionExtensions.cs` +- Test: `ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/DependencyInjectionTests.cs` + +**Step 1: Write the failing test** + +```csharp +using Microsoft.Extensions.DependencyInjection; +using ZB.MOM.WW.SPHistorianClient; + +namespace ZB.MOM.WW.SPHistorianClient.Tests; + +public class DependencyInjectionTests +{ + [Fact] + public void AddZbSpHistorianClient_resolves_client_and_options() + { + var services = new ServiceCollection(); + var options = new HistorianClientOptions { Host = "localhost" }; + + services.AddZbSpHistorianClient(options); + + using var sp = services.BuildServiceProvider(); + Assert.Same(options, sp.GetRequiredService()); + Assert.NotNull(sp.GetRequiredService()); + } + + [Fact] + public void AddZbSpHistorianClient_throws_when_host_missing() + { + var services = new ServiceCollection(); + var options = new HistorianClientOptions { Host = "" }; + + Assert.Throws(() => services.AddZbSpHistorianClient(options)); + } + + [Fact] + public void AddZbSpHistorianClient_throws_on_null_options() + { + var services = new ServiceCollection(); + Assert.Throws(() => services.AddZbSpHistorianClient(null!)); + } +} +``` + +**Step 2: Run — verify it fails to compile** (`AddZbSpHistorianClient` not defined) + +Run: `dotnet test ZB.MOM.WW.SPHistorianClient.slnx --filter "FullyQualifiedName~DependencyInjectionTests"` +Expected: FAIL (does not compile / method missing). + +**Step 3: Implement** + +```csharp +using Microsoft.Extensions.DependencyInjection; + +namespace ZB.MOM.WW.SPHistorianClient; + +/// +/// ZB.MOM.WW DI registration for . Mirrors the family's +/// AddZb* convention. Because is required/ +/// init-only, callers pass a fully-built options instance (bind it from configuration in the +/// consuming app, e.g. config.GetSection("Historian").Get<HistorianClientOptions>()). +/// +public static class ZbSpHistorianClientServiceCollectionExtensions +{ + public static IServiceCollection AddZbSpHistorianClient( + this IServiceCollection services, + HistorianClientOptions options) + { + ArgumentNullException.ThrowIfNull(services); + ArgumentNullException.ThrowIfNull(options); + if (string.IsNullOrWhiteSpace(options.Host)) + { + throw new ArgumentException( + "HistorianClientOptions.Host must be set.", nameof(options)); + } + + services.AddSingleton(options); + // HistorianClient opens a fresh channel per operation and has a no-op DisposeAsync, + // so transient is safe and avoids assuming the shared dialect is concurrency-safe. + services.AddTransient(); + return services; + } +} +``` + +**Step 4: Run — verify pass** + +Run: `dotnet test ZB.MOM.WW.SPHistorianClient.slnx --filter "FullyQualifiedName~DependencyInjectionTests"` +Expected: PASS (3/3). + +**Step 5: Commit** + +```bash +git add ZB.MOM.WW.SPHistorianClient/src/ZB.MOM.WW.SPHistorianClient/DependencyInjection \ + ZB.MOM.WW.SPHistorianClient/tests/ZB.MOM.WW.SPHistorianClient.Tests/DependencyInjectionTests.cs +git commit -m "feat(sphistorianclient): add AddZbSpHistorianClient DI extension" +``` + +--- + +## Task 5: Author `CLAUDE.md` + `README.md` + +**Classification:** small +**Estimated implement time:** ~4 min +**Parallelizable with:** Task 4 +**Blocked by:** Task 3 + +Sanitized docs only — **no hostnames, credentials, customer tag names, or capture data.** Model the +structure on `ZB.MOM.WW.Telemetry/CLAUDE.md` (overview, package table, build/test/pack commands, +status) but adapt to a single-package library. + +**Files:** +- Create: `ZB.MOM.WW.SPHistorianClient/CLAUDE.md` +- Create: `ZB.MOM.WW.SPHistorianClient/README.md` + +**`CLAUDE.md` must cover:** +- One-paragraph overview: pure-managed .NET 10 AVEVA System Platform Historian client, no native AVEVA + dependency, reverse-engineered wire protocol. Ported from the `histsdk` migration bundle. +- The supported operation surface table (copy the README table from the bundle: + `ProbeAsync`, `ReadRawAsync`, `ReadAggregateAsync` (16 modes), `ReadAtTimeAsync`, `ReadEventsAsync`, + `BrowseTagNamesAsync`, `GetTagMetadataAsync`, `GetConnectionStatusAsync`, + `GetStoreForwardStatusAsync`, `GetSystemParameterAsync`, `EnsureTagAsync`, `DeleteTagAsync`). +- Transport matrix: `LocalPipe` / `RemoteTcpIntegrated` / `RemoteTcpCertificate` (WCF, Windows-only, + live-verified) vs `RemoteGrpc` (2023 R2, cross-platform, **not yet live-verified**). +- Out of scope: writing samples (`AddS2` architecturally blocked), discrete/string tag creation. +- DI: the `AddZbSpHistorianClient(options)` extension + the bind-from-config note. +- Build/test/pack commands (from this dir): + `dotnet build ZB.MOM.WW.SPHistorianClient.slnx` / `dotnet test …` / + `dotnet pack ZB.MOM.WW.SPHistorianClient.slnx -c Release -o ./artifacts`. +- Live integration tests gated by `HISTORIAN_*` env vars (skip cleanly when unset). List the env vars. + +**`README.md`:** a trimmed public-facing version — overview, quick-start snippet (the bundle's +`HistorianClient` usage example, namespace updated to `ZB.MOM.WW.SPHistorianClient`), supported surface +table, build/test commands. + +**Commit:** + +```bash +git add ZB.MOM.WW.SPHistorianClient/CLAUDE.md ZB.MOM.WW.SPHistorianClient/README.md +git commit -m "docs(sphistorianclient): add CLAUDE.md + README.md" +``` + +--- + +## Task 6: Pack verification + +**Classification:** small +**Estimated implement time:** ~3 min +**Parallelizable with:** none +**Blocked by:** Task 4, Task 5 + +**Files:** +- Create (build output): `ZB.MOM.WW.SPHistorianClient/artifacts/ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg` + +**Step 1: Full green build + test once more, then pack** + +```bash +cd /Users/dohertj2/Desktop/scadaproj/ZB.MOM.WW.SPHistorianClient +dotnet test ZB.MOM.WW.SPHistorianClient.slnx +dotnet pack ZB.MOM.WW.SPHistorianClient.slnx -c Release -o ./artifacts +``` + +Expected: tests pass (live ones skip); pack produces `artifacts/ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg`. + +**Step 2: Sanity-check the package contents** + +```bash +unzip -l artifacts/ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg | grep -E 'ZB.MOM.WW.SPHistorianClient.dll|.nuspec' +``` +Expected: the lib DLL and nuspec are present. + +**Step 3: Commit the nupkg** (matches the family convention — `ZB.MOM.WW.Telemetry` commits its `artifacts/*.nupkg`) + +```bash +cd /Users/dohertj2/Desktop/scadaproj +git add -f ZB.MOM.WW.SPHistorianClient/artifacts/ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg +git commit -m "build(sphistorianclient): pack 0.1.0 nupkg" +``` + +> **Do NOT push or publish** to the Gitea feed. Per repo experience, "published/adopted" claims must +> not be made without explicit user direction + feed verification. + +--- + +## Task 7: Index the new library in the umbrella `CLAUDE.md` (optional) + +**Classification:** trivial +**Estimated implement time:** ~2 min +**Parallelizable with:** none +**Blocked by:** Task 6 + +**Files:** +- Modify: `CLAUDE.md` (repo root umbrella index) + +Add a short reference so the umbrella index reflects the newly-hosted library (the intro paragraph +that enumerates the hosted `ZB.MOM.WW.*` sources, and/or a one-line pointer near the component table +noting `ZB.MOM.WW.SPHistorianClient` is a net-new shared library — **not** a component normalization). + +> **Caveat:** repo-root `CLAUDE.md` already has **pre-existing uncommitted edits** (unrelated to this +> work). Before editing, run `git diff CLAUDE.md` and make sure your commit message reflects that it +> may bundle those edits — or stage only the hunks you add. If this risks entangling unrelated changes, +> skip this task and leave it for the user. + +**Commit:** + +```bash +git add CLAUDE.md +git commit -m "docs: index ZB.MOM.WW.SPHistorianClient in umbrella CLAUDE.md" +``` + +--- + +## Done criteria + +- `ZB.MOM.WW.SPHistorianClient/` exists with `src/`, `tests/`, props, `.slnx`, `CLAUDE.md`, `README.md`. +- `dotnet build` + `dotnet test` are green on macOS (live integration tests skip cleanly). +- `AddZbSpHistorianClient` DI extension present + tested. +- `artifacts/ZB.MOM.WW.SPHistorianClient.0.1.0.nupkg` produced. +- All work committed on `feat/sphistorianclient`. Not pushed/published. diff --git a/docs/plans/2026-06-19-sphistorianclient.md.tasks.json b/docs/plans/2026-06-19-sphistorianclient.md.tasks.json new file mode 100644 index 0000000..f135cf4 --- /dev/null +++ b/docs/plans/2026-06-19-sphistorianclient.md.tasks.json @@ -0,0 +1,13 @@ +{ + "planPath": "docs/plans/2026-06-19-sphistorianclient.md", + "tasks": [ + {"id": 1, "subject": "Task 1: Scaffold library skeleton", "status": "pending"}, + {"id": 2, "subject": "Task 2: Port source + tests with namespace rewrite", "status": "pending", "blockedBy": [1]}, + {"id": 3, "subject": "Task 3: Build + test green", "status": "pending", "blockedBy": [2]}, + {"id": 4, "subject": "Task 4: Add AddZbSpHistorianClient DI extension (TDD)", "status": "pending", "blockedBy": [3]}, + {"id": 5, "subject": "Task 5: Author CLAUDE.md + README.md", "status": "pending", "blockedBy": [3]}, + {"id": 6, "subject": "Task 6: Pack verification", "status": "pending", "blockedBy": [4, 5]}, + {"id": 7, "subject": "Task 7: Index new lib in umbrella CLAUDE.md (optional)", "status": "pending", "blockedBy": [6]} + ], + "lastUpdated": "2026-06-19" +}