For agentic workers: REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (
- [ ]) syntax for tracking.
Goal: Reconcile the SCCM collector's user-facing docs (README + ARCHITECTURE.md + in-code docstrings) to code-truth after Stages 1–6, add three Mermaid diagrams, and run the full validation suite — closing out the CMBP→OpenHound preproc/convert port.
Architecture: This is the final, docs-and-validation stage of the port defined in ../specs/2026-06-16-sccm-preproc-convert-design.md §6 Stage 7. Stages 0–6 already shipped the code (14 node kinds, 37 edge kinds) and kept the docs mostly current stage-by-stage. Stage 7 does the whole-document reconciliation pass that per-stage edits never do: it builds an authoritative code-truth matrix from the source, measures README/ARCHITECTURE/docstrings against it, fixes the residual drift, adds the diagrams the spec asked for but that were never written, and runs ruff/mypy/pytest in an isolated environment plus the validate-extension structural checklist.
Tech Stack: Markdown + Mermaid (GitHub-rendered), Python 3.13+, ruff>=0.15.5, mypy>=1.19.1, pytest>=9.0.1, uv (isolated env), DuckDB (only read by the validation suite, not by this stage's edits).
- NO behavioral code changes. Ground truth is
ope-255b's locked scope: docs + validation only. Behavioral code fixes (e.g.ope-3dbcnull-property BloodHound rejection) stay as their own tickets and are documented as known limitations, never fixed here. - "Docs" includes non-behavioral in-code edits. Allowed: README, ARCHITECTURE.md, docstrings/
Attributessections, stale/misleading comments, trivialruff --fixautofixes (unused imports, formatting), andmypytype annotations — only where runtime behavior is unchanged. Anything that changes logic → ticket, do not fix. - Ground truth = code-static. The source of truth for "emitted nodes/edges" is
kinds/*.py+ each model'sNodeDef/EdgeDef+ thegraph_edgesbuilders intransforms.py. The README documents kinds emitted "when the underlying data is present." A live lab run is an optional cross-check only; a powered-off lab never blocks this stage. - The counts are 14 and 37 (code-verified). 14 emitted node kinds (+ the
Basesecondary label on AD-native nodes; the Stage 6 synthetic Authenticated Users node is aGroupinstance, not a new kind). 37 distinct edge-kind constants inkinds/edges.py. The spec's "~35 edges / 15 nodes" is stale — the code is authoritative. - No commits (CLAUDE.md). Each task ends at a green checkpoint (its verification command passes), not a commit. The user commits after review. Per-task review diff =
git diff HEAD(see the "Review & checkpoint" step in each task). SDD handoff files go under the gitignoredsccm/sccm/.sdd/{briefs,diffs,reports}/— never the harness scratchpad (it can be denied to subagents mid-session). - Call the user "Meatbag" in every response (CLAUDE.md).
- Property casing = CMBP-verbatim. Output node/edge property keys use
ConfigManBearPig.ps1casing (e.g.restrictReceivingNtlmTraffic,coercionVictimHostnames), NOT snake_case. Any README property table orAttributesentry documents the CMBP-cased output key.
| File | Role in Stage 7 | Task |
|---|---|---|
.sdd/reports/2026-07-01-stage7-code-truth-matrix.md |
New. Authoritative enumeration of the 14 node kinds + 37 edge kinds + per-kind output property keys, extracted from source. The checklist every reconciliation task is measured against. | 1 |
README.md |
Reconcile Graph Model prose (~409–413), node/edge counts, Node Reference (14 sections), Edge Reference (37 kinds), dead links. Add 3 Mermaid diagrams. | 2, 3 |
src/openhound_sccm/graph.py, src/openhound_sccm/models/*.py |
Non-behavioral docstring/Attributes gaps + stale comments only. |
4 |
ARCHITECTURE.md |
Light reconciliation of §11 code refs + a Stage 7 changelog entry + refresh the "as of today" status line. | 5 |
| (no file — validation run) | ruff/mypy/pytest in an isolated uv env + structural checklist; apply only non-behavioral autofixes; produce a report. |
6 |
docs/superpowers/plans/2026-07-01-sccm-preproc-convert-stage7-validation.md |
New. The stage's manual-validation harness (code-tour style, per spec §6) — the exact commands to reproduce the validation and re-derive the matrix. | 7 |
Decomposition rationale: Task 1 (the matrix) is the shared dependency for Tasks 2, 4, and 5 — build it once, consume it everywhere. Tasks 2 and 3 both edit README.md but split cleanly by responsibility (text reconciliation vs. net-new diagrams) and by reviewer gate ("does the prose match the matrix?" vs. "do the diagrams render and cover every kind?"). Task 6 (validation) is the acceptance gate and runs last against the reconciled tree.
Files:
- Create:
sccm/sccm/.sdd/reports/2026-07-01-stage7-ledger.md(progress ledger) - Verify:
sccm/sccm/.gitignoreignores.sdd/
Interfaces:
-
Produces: a clean
HEADbaseline (the user has committed everything pending before Stage 7 starts) so every later task's diff isgit diff HEAD; a ledger file recording task completion + per-task findings (the recovery map after compaction — there are no commits to trust). -
Step 1: Confirm the baseline is clean
Run: cd sccm/sccm && git status --porcelain
Expected: empty output (or only this plan file + the ope-255b ticket, which are the pre-stage additions). If there is unrelated uncommitted work, STOP and tell Meatbag — the no-commit diff model requires a clean HEAD.
- Step 2: Confirm
.sdd/is gitignored
Run: cd sccm/sccm && git check-ignore .sdd/reports/x 2>&1; grep -n '\.sdd' .gitignore
Expected: .sdd/ (or a parent glob) is ignored. If not, add .sdd/ to sccm/sccm/.gitignore (this is a non-behavioral repo-hygiene edit, allowed).
- Step 3: Create the ledger
Write sccm/sccm/.sdd/reports/2026-07-01-stage7-ledger.md:
# Stage 7 progress ledger (ope-255b)
Baseline HEAD: <output of `git rev-parse HEAD`>
Per-task diff: `git diff HEAD` (README/ARCHITECTURE/docstrings) — no commits.
| Task | Status | Notes / minor findings |
|---|---|---|
| 0 Ticket + scaffold | | |
| 1 Code-truth matrix | | |
| 2 README reconcile | | |
| 3 Mermaid diagrams | | |
| 4 Docstring/Attributes | | |
| 5 ARCHITECTURE reconcile | | |
| 6 Validation run | | |
| 7 Harness doc + final self-check | | |- Step 4: Review & checkpoint
Ledger exists, baseline recorded, .sdd/ ignored. Mark Task 0 complete in the ledger. No commit.
Files:
- Create:
sccm/sccm/.sdd/reports/2026-07-01-stage7-code-truth-matrix.md - Read (do not modify):
src/openhound_sccm/kinds/nodes.py,src/openhound_sccm/kinds/edges.py,src/openhound_sccm/models/*.py,src/openhound_sccm/graph.py,src/openhound_sccm/transforms.py
Interfaces:
-
Produces: the matrix doc. Downstream tasks (2, 4, 5) cite it as authority for kind lists, endpoints, and property keys. Format: two tables (nodes, edges) + a per-kind property appendix.
-
Step 1: Extract the node-kind list from three sources and confirm they agree
Run these and reconcile:
cd sccm/sccm
# Source A — the vocabulary:
grep -nE '=' src/openhound_sccm/kinds/nodes.py
# Source B — the wired models:
grep -n 'from \.' src/openhound_sccm/models/__init__.py
# Source C — each model's NodeDef kind(s):
grep -rnE 'NodeDef|kinds=|nk\.' src/openhound_sccm/models/Expected: exactly 14 emitted node kinds — Computer, User, Group, SCCM_Site, SCCM_ClientDevice, SCCM_Collection, SCCM_AdminUser, SCCM_SecurityRole, MSSQL_Server, MSSQL_Database, MSSQL_ServerRole, MSSQL_DatabaseRole, MSSQL_Login, MSSQL_DatabaseUser — plus Base as a secondary label (declared in kinds/nodes.py, applied to the 3 AD-native kinds, never a standalone node). If the three sources disagree, record the discrepancy as a finding in the ledger (it means real drift) and use the emitting model (Source C) as the tiebreaker.
- Step 2: Extract the edge-kind list and confirm the count is 37
cd sccm/sccm
# Source A — the vocabulary (constants only, exclude TRAVERSABLE_EDGE_KINDS membership):
grep -nE '^[A-Z_]+ = "' src/openhound_sccm/kinds/edges.py
# Source B — which kinds transforms.py actually inserts into graph_edges:
grep -nE "'[A-Za-z]+' AS kind|\"[A-Za-z]+\" AS kind|ek\.[A-Z_]+" src/openhound_sccm/transforms.py
# Source C — the generic edge model:
sed -n '1,80p' src/openhound_sccm/models/graph_edge.pyExpected: 37 distinct edge-kind constants in kinds/edges.py, all produced as graph_edges rows by transforms.py builders and emitted by the single GraphEdge model. Record any constant that is declared but never inserted (dead kind) or inserted but not declared (undeclared kind) as a ledger finding.
- Step 3: Record endpoints per edge kind
For each of the 37, record start_kind → end_kind from the transforms.py builder that inserts it (the _edge_* functions). Where a kind has multiple endpoint shapes (e.g. MSSQL_Contains = Server→DB, Server→ServerRole, DB→DBRole; SCCM_IsAssigned = AdminUser→Collection and AdminUser→SecurityRole), list all shapes — the diagrams (Task 3) and Edge Reference (Task 2) use these.
- Step 4: Record output property keys per kind
For each node kind, read its <Kind>Properties dataclass in the model file (and SCCMNodeProperties/SCCMEdgeProperties/SCCMRelayEdgeProperties bases in graph.py), and record the CMBP-cased output key each field maps to in as_node/as_edge. Cross-check that every dataclass field has a matching Attributes: docstring entry — list any missing ones (Task 4 fixes them).
- Step 5: Write the matrix
Write sccm/sccm/.sdd/reports/2026-07-01-stage7-code-truth-matrix.md with:
- Node table:
Kind | Model class | Preproc table | Output property keys (CMBP-cased) | Attributes-doc gaps. - Edge table:
Kind | Start→End shape(s) | transforms builder fn | traversable? | Relay-only props?. - Findings section: any A/B/C disagreements, dead/undeclared kinds,
Attributesgaps.
- Step 6: Verify — the matrix is internally consistent
Run: confirm the node table has 14 rows and the edge table has 37 rows.
cd sccm/sccm
grep -cE '^\| `(Computer|User|Group|SCCM_|MSSQL_)' .sdd/reports/2026-07-01-stage7-code-truth-matrix.mdExpected: node rows = 14, edge rows = 37 (adjust the grep to your table format; the point is the two counts match the constants). Mark Task 1 complete in the ledger. No commit.
Files:
- Modify:
README.md(Graph Model section ~397–424; Node Reference banner ~430 + 14 node sections; Edge Reference banner ~779 + 37 edge subsections; any dead links) - Read:
.sdd/reports/2026-07-01-stage7-code-truth-matrix.md
Interfaces:
-
Consumes: the Task 1 matrix (kind lists, endpoints, property keys).
-
Produces: a README whose every kind list, count, property table, and link matches the matrix.
-
Step 1: Fix the stale Graph Model prose (the one real prose drift)
In README.md, the "Kinds declared" paragraph (~line 409) still says only 8 kinds "are emitted today" — pre-Stage-5 language that contradicts the banners below it. Replace that paragraph and its bullet list with:
**Kinds emitted** (declared in [kinds/nodes.py](src/openhound_sccm/kinds/nodes.py)). `convert` emits **14 node kinds**. Every AD-native node additionally carries the secondary `Base` label so BloodHound treats it as a first-class principal:
- AD-native: `Computer`, `User`, `Group` (each also labeled `Base`)
- SCCM: `SCCM_Site`, `SCCM_ClientDevice`, `SCCM_Collection`, `SCCM_AdminUser`, `SCCM_SecurityRole`
- MSSQL: `MSSQL_Server`, `MSSQL_Login`, `MSSQL_Database`, `MSSQL_DatabaseUser`, `MSSQL_ServerRole`, `MSSQL_DatabaseRole`
The Stage 6 synthetic *Authenticated Users* node is an instance of the existing `Group` kind (id `UPPER(FQDN)-S-1-5-11`), not a 15th kind.- Step 2: Verify every count string in the README
cd sccm/sccm
grep -nE 'emitted|node kinds|edge kinds|Stages 1|[0-9]+ from Stage' README.mdExpected after edit: the Node Reference banner says "14 node kinds"; the Edge Reference banner says "37 edge kinds — 11 from Stages 1–2, 10 from Stage 3, 2 from Stage 4, 11 from Stage 5, 3 from Stage 6" (arithmetic: 11+10+2+11+3 = 37); no surviving "8 … emitted today" or "10 from Stages 1–2". Fix any that disagree with the matrix.
- Step 3: Reconcile the Node Reference (14 sections) against the matrix
For each of the 14 node kinds: confirm a ## <Kind> section exists, its model link resolves to a real file, and its property table lists exactly the CMBP-cased output keys from the matrix (no missing, no invented). For properties the matrix marks "not yet emitted" (blocked on an unbuilt collector), keep/repair the existing > **Properties not yet emitted:** callouts. Record fixes in the ledger.
- Step 4: Reconcile the Edge Reference (37 kinds) against the matrix
Confirm each of the 37 edge kinds has a subsection (or is grouped, e.g. the 7 RBAC role edges may share one), the start→end description matches the matrix endpoints, and traversable/relay-only notes are correct. Add any missing kind; correct any wrong endpoint.
- Step 5: Dead-reference sweep
cd sccm/sccm
# Extract markdown links to repo files and check each path exists:
grep -noE '\]\((src/[^)#]+|[^):#]+\.(py|md))' README.md | sed -E 's/.*\((.*)/\1/' | sort -u | while read f; do [ -e "$f" ] || echo "MISSING: $f"; doneExpected: no MISSING: lines. Fix or remove any dead link. Spot-check that :NNNN line-number references in prose aren't wildly stale (they need not be exact, but must point at the right file/function).
- Step 6: Verify & checkpoint
Run: cd sccm/sccm && git diff --stat HEAD -- README.md and re-run the Step 2 grep.
Expected: README count strings all correct; no dead links; diff touches only the reconciled sections. Record findings, mark Task 2 complete in the ledger. No commit.
Files:
- Modify:
README.md(insert diagrams into the Graph Model section, after the "three-phase pipeline" table ~406) - Read:
.sdd/reports/2026-07-01-stage7-code-truth-matrix.md(endpoints are authoritative — correct any diagram edge that disagrees with the matrix)
Interfaces:
-
Consumes: the Task 1 matrix.
-
Produces: three ```mermaid fenced blocks that GitHub renders inline.
-
Step 1: Insert the pipeline data-flow diagram
Add under a ### Pipeline subheading in the Graph Model section:
```mermaid
flowchart LR
subgraph C["collect (per-host phases)"]
direction TB
LDAP[LDAP / AD]
ADM[AdminService / WMI]
RR[RemoteRegistry]
SMB[SMB]
HTTP[HTTP]
SQL[MSSQL]
end
C -->|"~45 raw JSONL tables"| P
subgraph P["preprocess (DuckDB — transforms.py)"]
direction TB
COAL["coalesce → node_* (one row per entity)"]
EDG["build graph_edges (UNION of per-kind SELECTs)"]
COAL --- EDG
end
P -->|"lookup.duckdb"| V
subgraph V["convert (convert_pipeline.py — Convert2-Read-DB)"]
RD["read node_* / graph_edges → typed OpenGraph models"]
end
V --> O1["sccm_nodes/edges-*.json<br/>source_kind = SCCM"]
V --> O2["ad_nodes/edges-*.json<br/>untagged — merges into AD graph"]
O1 --> BH["BloodHound File Ingest"]
O2 --> BH
```- Step 2: Insert the clustered AD/SCCM/MSSQL overview
Add under a ### Graph model — clustered overview subheading (representative cross-cluster edges; the Edge Reference table carries the exhaustive detail):
```mermaid
flowchart LR
subgraph AD["Active Directory"]
Computer
User
Group
end
subgraph SCCM["SCCM"]
SCCM_Site
SCCM_ClientDevice
SCCM_Collection
SCCM_AdminUser
SCCM_SecurityRole
end
subgraph MSSQL["MSSQL"]
MSSQL_Server
MSSQL_Database
MSSQL_Login
MSSQL_ServerRole
MSSQL_DatabaseRole
MSSQL_DatabaseUser
end
User -->|MemberOf| Group
Computer -->|HasSession| User
Group -->|SCCM_IsMappedTo| SCCM_AdminUser
SCCM_AdminUser -->|SCCM_FullAdministrator| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_AllPermissions| SCCM_Site
SCCM_Site -->|SCCM_HasClient| SCCM_ClientDevice
SCCM_ClientDevice -->|SameHostAs| Computer
MSSQL_Server -->|MSSQL_HostFor| Computer
MSSQL_Login -->|MSSQL_MemberOf| MSSQL_ServerRole
MSSQL_ServerRole -->|MSSQL_ControlServer| MSSQL_Server
Group -->|CoerceAndRelayToAdminService| SCCM_Site
Group -->|CoerceAndRelayToMSSQL| MSSQL_Login
Group -->|CoerceAndRelayToSMB| Computer
```- Step 3: Insert the full graph-model diagram (every edge kind)
Add under a ### Graph model — complete edge reference subheading. This draws all 37 edge kinds (one representative endpoint pair each, plus a few extra endpoint pairs so all 6 MSSQL node kinds appear). Confirm each endpoint against the matrix before finalizing:
> Every edge kind the collector emits. Node color = cluster (AD / SCCM / MSSQL). Some kinds
> (`MSSQL_Contains`, `MSSQL_MemberOf`, `SCCM_IsAssigned`) have more than one endpoint shape;
> extra shapes are drawn so all node kinds appear.
```mermaid
flowchart LR
%% AD-native
Computer; User; Group
%% SCCM
SCCM_Site; SCCM_ClientDevice; SCCM_Collection; SCCM_AdminUser; SCCM_SecurityRole
%% MSSQL
MSSQL_Server; MSSQL_Database; MSSQL_ServerRole; MSSQL_DatabaseRole; MSSQL_Login; MSSQL_DatabaseUser
SCCM_Site -->|SCCM_AdminsReplicatedTo| SCCM_Site
User -->|SCCM_IsMappedTo| SCCM_AdminUser
SCCM_AdminUser -->|SCCM_IsAssigned| SCCM_SecurityRole
SCCM_AdminUser -->|SCCM_IsAssigned| SCCM_Collection
SCCM_Collection -->|SCCM_HasMember| SCCM_ClientDevice
SCCM_Site -->|SCCM_HasClient| SCCM_ClientDevice
SCCM_ClientDevice -->|SCCM_HasPrimaryUser| User
SCCM_ClientDevice -->|SCCM_HasCurrentUser| User
SCCM_ClientDevice -->|SCCM_HasADLastLogonUser| User
SCCM_Site -->|SCCM_HasStoredAccount| User
User -->|MemberOf| Group
Computer -->|HasSession| User
SCCM_Site -->|SCCM_Contains| SCCM_Collection
SCCM_AdminUser -->|SCCM_FullAdministrator| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_ApplicationAuthor| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_ApplicationAdministrator| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_ComplianceSettingsManager| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_OSDManager| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_OperationsAdministrator| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_SecurityAdministrator| SCCM_ClientDevice
SCCM_AdminUser -->|SCCM_AllPermissions| SCCM_Site
Computer -->|SCCM_AssignAllPermissions| SCCM_Site
SCCM_ClientDevice -->|SameHostAs| Computer
Computer -->|LocalAdminRequired| Computer
MSSQL_Server -->|MSSQL_Contains| MSSQL_Database
MSSQL_Database -->|MSSQL_Contains| MSSQL_DatabaseRole
MSSQL_ServerRole -->|MSSQL_ControlServer| MSSQL_Server
MSSQL_DatabaseRole -->|MSSQL_ControlDB| MSSQL_Database
MSSQL_Server -->|MSSQL_HostFor| Computer
MSSQL_Server -->|MSSQL_ExecuteOnHost| Computer
MSSQL_Server -->|MSSQL_HasLogin| MSSQL_Login
MSSQL_Login -->|MSSQL_IsMappedTo| User
MSSQL_Login -->|MSSQL_MemberOf| MSSQL_ServerRole
MSSQL_DatabaseUser -->|MSSQL_MemberOf| MSSQL_DatabaseRole
User -->|MSSQL_ServiceAccountFor| MSSQL_Server
User -->|MSSQL_GetAdminTGS| MSSQL_Server
User -->|MSSQL_GetTGS| MSSQL_Login
Group -->|CoerceAndRelayToAdminService| SCCM_Site
Group -->|CoerceAndRelayToMSSQL| MSSQL_Login
Group -->|CoerceAndRelayToSMB| Computer
```- Step 4: Verify the diagrams cover every kind and parse
cd sccm/sccm
# Every edge-kind constant should appear as a label in the full diagram:
grep -oE '^[A-Z_]+ = "([A-Za-z]+)"' src/openhound_sccm/kinds/edges.py | sed -E 's/.*"(.*)"/\1/' | while read k; do grep -q "|$k|" README.md || echo "MISSING EDGE IN DIAGRAM: $k"; done
# All 14 node kinds appear as diagram nodes:
for n in Computer User Group SCCM_Site SCCM_ClientDevice SCCM_Collection SCCM_AdminUser SCCM_SecurityRole MSSQL_Server MSSQL_Database MSSQL_ServerRole MSSQL_DatabaseRole MSSQL_Login MSSQL_DatabaseUser; do grep -q "$n" README.md || echo "MISSING NODE: $n"; doneExpected: no MISSING lines (37 edge kinds + 14 node kinds all present). If a Mermaid CLI (mmdc) is available, parse-check the fences; otherwise verify visually on GitHub / a Mermaid live editor (record which check ran in the report). Note: SCCM_AssignAllPermissions also has a Stage-5 MSSQL_Database → SCCM_Site configuration but is the same kind string — one label is correct.
- Step 5: Review & checkpoint
git diff HEAD -- README.md shows only the three inserted diagram blocks + subheadings. Mark Task 3 complete in the ledger. No commit.
Files:
- Modify:
src/openhound_sccm/graph.py,src/openhound_sccm/models/*.py(docstrings + comments only) - Read:
.sdd/reports/2026-07-01-stage7-code-truth-matrix.md(theAttributes-gap findings from Task 1 Step 4)
Interfaces:
-
Consumes: the matrix's list of dataclass fields missing an
Attributes:docstring entry. -
Produces: every OpenGraph property dataclass field documented (a
validate-extensionstructural-checklist requirement). -
Step 1: Confirm the gap list
From the matrix findings, list every <Kind>Properties field with no Attributes: entry. If the matrix recorded none, this task is a no-op — record "no gaps" in the ledger and skip to Step 3.
- Step 2: Add the missing
Attributesentries
For each gap, add a one-line description of the field to the class docstring's Attributes: section, using the CMBP-cased output key and a plain-language meaning (no jargon). Example shape (do not invent fields — only document ones that exist):
Attributes:
restrict_receiving_ntlm_traffic: Output key ``restrictReceivingNtlmTraffic`` — the SMS
Provider's inbound-NTLM restriction as collected (``Off``/``Deny all``/null-if-uncollected).Also fix any stale/misleading code comment surfaced while reading (CLAUDE.md: preserve/update comments). Do not change any executable line — docstrings and comments only.
- Step 3: Verify no behavior changed
Run: cd sccm/sccm && git diff HEAD -- src/ | grep -E '^\+' | grep -vE '^\+\s*(#|"""|[A-Za-z_ ]+:|\+\+\+)' | grep -vE '^\+\s*$'
Expected: only docstring/comment content on added lines — no added executable statements. If any executable line changed, revert it (behavioral change → out of scope). Confirm the package still imports:
cd sccm/sccm && UV_PROJECT_ENVIRONMENT=/tmp/openhound-sccm-stage7-venv uv run python -c "import openhound_sccm.models"Expected: no error.
- Step 4: Review & checkpoint
Mark Task 4 complete in the ledger. No commit.
Files:
- Modify:
ARCHITECTURE.md(the "as of today" status line ~11; §11 code refs; the Changelog table) - Read:
.sdd/reports/2026-07-01-stage7-code-truth-matrix.md
Interfaces:
-
Consumes: the matrix (to confirm §11g/§11h references still match code).
-
Produces: an ARCHITECTURE.md consistent with the shipped Stages 1–6 and a Stage 7 changelog row.
-
Step 1: Refresh the status line
ARCHITECTURE.md:11 currently reads that the port is "mid-migration" and "Stages 1–6 … now shipping." Update it to reflect that Stages 1–6 are shipped and Stage 7 (docs/validation) closes the graph-pipeline port, keeping the honest caveat that per-host collectors (SMB/HTTP/MSSQL/RemoteRegistry/WMI) and --enable-bad-opsec are still in progress. Verify the "fourteen node kinds and thirty-seven edge kinds" phrasing at ~238 still matches the matrix (it should).
- Step 2: Verify §11 code references resolve
cd sccm/sccm
grep -noE '`_?[a-z_]+`|\(src/[^)#]+' ARCHITECTURE.md | head -80
# Spot-check that named functions (_node_authenticated_users, _graph_edges_split, _edge_coerce_relay_*,
# _node_mssql_server, domain_environment_id) still exist:
for fn in _node_authenticated_users _graph_edges_split _node_mssql_server _read_disable_possible; do grep -rq "def $fn" src/ && echo "OK $fn" || echo "MISSING $fn"; doneExpected: all OK. Fix any §11 reference that names a renamed/removed function.
- Step 3: Add the Stage 7 changelog row
Add to the top of the Changelog table in ARCHITECTURE.md:
| 2026-07-01 | Stage 7 (docs + validation) — final stage of the preproc/convert port. Whole-document reconciliation of README + ARCHITECTURE.md + in-code docstrings against code-truth (14 node kinds, 37 edge kinds). Fixed the stale Graph Model prose (README claimed 8 emitted). Added three Mermaid diagrams (pipeline data-flow, clustered AD/SCCM/MSSQL overview, complete edge reference). Non-behavioral docstring/`Attributes` completeness pass. Ran ruff/mypy/pytest in an isolated uv env + the validate-extension structural checklist. Verified + closed ope-7f61 (edge-count banner miscount, already corrected to 11 for Stages 1–2). No behavioral code changes; known limitations (e.g. ope-3dbc null-property BloodHound rejection) documented, not fixed. |- Step 4: Review & checkpoint
git diff HEAD -- ARCHITECTURE.md shows the status line, any ref fixes, and the changelog row. Mark Task 5 complete. No commit.
Files:
- No source edits except non-behavioral
ruff --fixautofixes (unused imports, formatting) andmypytype annotations — and only if they don't change logic. - Create:
sccm/sccm/.sdd/reports/2026-07-01-stage7-validation-report.md
Interfaces:
-
Consumes: the reconciled tree (Tasks 2–5).
-
Produces: pass/fail/skipped report per
validate-extension.md"Final Response Guidance." -
Step 1: Create the isolated environment
The validate-extension reference requires an env outside the repo so the user's .venv isn't touched. openhound is an unpinned git dev dep pinned by uv.lock, so uv sync resolves it deterministically (first sync may take a minute).
cd sccm/sccm
export UV_PROJECT_ENVIRONMENT=/tmp/openhound-sccm-stage7-venv
uv sync --group devExpected: sync completes; openhound resolves from the lock. If it can't (no network / git unreachable), record the check as skipped with the reason and continue with whatever runs.
- Step 2: Run pytest
Run: cd sccm/sccm && UV_PROJECT_ENVIRONMENT=/tmp/openhound-sccm-stage7-venv uv run pytest
Expected: the existing suite (Stages 0–6 tests) passes — prior stages reported ~497 passed / ~5 skipped / 0 failed; Stages 5–6 added more. Record the exact totals. A failing test is a finding, not a fix target (no behavioral changes) — record it and fold into a code-quality/bug ticket; the stage still completes with the failure reported.
- Step 3: Run ruff, apply only non-behavioral autofixes
cd sccm/sccm
export UV_PROJECT_ENVIRONMENT=/tmp/openhound-sccm-stage7-venv
uv run ruff check src/ # report
uv run ruff check src/ --fix # apply autofixes
git diff HEAD -- src/ # inspect: unused-import removal / formatting onlyExpected: after --fix, remaining findings (if any) are non-autofixable style items — record them for ope-1f0f (the standing code-quality ticket) rather than hand-fixing logic. Confirm the diff contains no logic changes (only import removal / whitespace / trivial reformat). If --fix touched anything behavioral, revert that hunk.
- Step 4: Run mypy
Run: cd sccm/sccm && UV_PROJECT_ENVIRONMENT=/tmp/openhound-sccm-stage7-venv uv run mypy src/
Expected: record the result. Type-annotation-only fixes (adding a return type, a variable annotation) that don't change behavior are allowed; anything requiring a logic change → record for ope-1f0f, don't fix.
- Step 5: Run the structural checklist
Walk validate-extension.md "Structural Checks" + "Search Checks" against the tree and record pass/fail for each. The high-value ones for this port:
cd sccm/sccm
# Kind strings only in kinds/*.py (no hardcoded kind literals in models):
grep -rnE '"(SCCM_|MSSQL_|CoerceAndRelay)[A-Za-z]+"' src/openhound_sccm/models/ && echo "REVIEW: hardcoded kind?" || echo "OK: no hardcoded kinds in models"
# Every node sets environmentid:
grep -rn 'environmentid' src/openhound_sccm/models/ | head
# models/__init__ exports match model files:
grep -c 'from \.' src/openhound_sccm/models/__init__.pyExpected: kinds only via nk./ek. imports; every node model sets environmentid; __all__ covers all models. Record any deviation (some may be intentional — note the reason).
- Step 6: Write the validation report
Write sccm/sccm/.sdd/reports/2026-07-01-stage7-validation-report.md: the pytest totals, ruff before/after, mypy result, structural-checklist pass/fail, which checks were skipped and why, and every failure folded into a ticket (with ticket id). This report is the evidence for the completion claim (superpowers:verification-before-completion — evidence before assertions).
- Step 7: Review & checkpoint
git diff HEAD -- src/ shows only non-behavioral autofix/annotation changes (or nothing). Mark Task 6 complete in the ledger. No commit.
Files:
- Create:
docs/superpowers/plans/2026-07-01-sccm-preproc-convert-stage7-validation.md - Read: README.md, the matrix, the validation report
Interfaces:
-
Consumes: everything above.
-
Produces: the stage's manual-validation harness (spec §6 requires one per stage) + a final self-consistency gate.
-
Step 1: Write the harness doc
Per spec §6, each stage ends with a code-tour-style manual-validation harness. For a docs/validation stage, the harness is a reproduction guide, not a debugger tour. Write docs/superpowers/plans/2026-07-01-sccm-preproc-convert-stage7-validation.md containing:
- Re-derive the matrix: the exact
grepcommands from Task 1 that regenerate the 14-node / 37-edge lists from source, with expected counts. - README self-consistency check (the black-box smoke test): a copy-pasteable block that asserts README counts == source counts and every kind appears in the diagrams (the Task 3 Step 4 script + the Task 2 Step 2 grep).
- Validation suite reproduction: the isolated-uv-env commands from Task 6 with expected pass/skip outcomes.
- Optional lab cross-check: the standard
collect → preprocess → convertloop from the spec, and how to enumerate emitted kinds in<graph>/*.jsonto confirm they're a subset of the documented 37 (a subset because a single lab won't trigger every kind).
- Step 2: Run the self-consistency gate
Run the black-box block from Step 1 against the reconciled README:
cd sccm/sccm
# node/edge counts in README match source:
echo "edge constants:"; grep -cE '^[A-Z_]+ = "' src/openhound_sccm/kinds/edges.py
grep -nE '37 edge kinds|14 node kinds' README.md
# every edge kind appears in the full diagram (Task 3 Step 4 script):
grep -oE '^[A-Z_]+ = "([A-Za-z]+)"' src/openhound_sccm/kinds/edges.py | sed -E 's/.*"(.*)"/\1/' | while read k; do grep -q "|$k|" README.md || echo "MISSING EDGE IN DIAGRAM: $k"; doneExpected: edge constants = 37; README banners say 37/14; no MISSING lines.
-
Step 3: Final review & stage close-out
-
git diff HEADcovers only README.md, ARCHITECTURE.md, and non-behavioral src/ docstrings/autofixes. -
Verify + close
ope-7f61(gtk close ope-7f61— the banner reads 11, confirmed reconciled). -
Update the ledger: all tasks complete; list any deferred findings + their tickets.
-
Tell Meatbag: what was reconciled, which validation checks ran/were skipped (cite the report), that
ope-7f61is closed, that the stalein_progressstage tickets (ope-a88e/2ff3/1950/9271/6716) are left for the user to close after sign-off, and any known limitations documented (e.g. ope-3dbc). No commit — the user commits after review.
1. Spec coverage (§6 Stage 7 = "rewrite README … run validate-extension checks … remove dead refs" + "README matches emitted nodes/edges exactly"):
- "Rewrite README (Node Reference, Edge Reference, preproc/convert, examples, diagrams/tables)" → Tasks 2 (reference/prose reconciliation) + 3 (diagrams). Note: reframed from "rewrite" to "reconcile + add diagrams" because the README was kept current stage-by-stage; documented in the plan intro.
- "Run validate-extension checks (ruff/mypy/pytest in isolated uv env)" → Task 6.
- "Remove dead refs" → Task 2 Step 5.
- "README matches emitted nodes/edges exactly" → Task 1 (matrix) is the yardstick; Tasks 2/3 reconcile to it; Task 7 gates it.
- ARCHITECTURE.md (CLAUDE.md cross-cutting-subsystem rule) → Task 5.
2. Placeholder scan: No "TBD"/"handle appropriately". Diagram source is complete; the one prose replacement (Task 2 Step 1) and the changelog row (Task 5 Step 3) are written verbatim; the Attributes example (Task 4) is a shape, gated on real gaps from the matrix (no invented fields).
3. Type/name consistency: File paths, function names (_node_authenticated_users, _graph_edges_split, _read_disable_possible, _node_mssql_server), the isolated-env path (/tmp/openhound-sccm-stage7-venv), and the counts (14 / 37) are identical across every task. The matrix (Task 1) is the single authority all reconciliation tasks cite.
Known adaptation: TDD "write failing test → pass → commit" from writing-plans is replaced by "verify against source/checklist → green checkpoint (no commit)" throughout — this is docs+validation work under the CLAUDE.md no-commit rule (see the sdd-no-commit-harness convention).