diff --git a/AGENTS.md b/AGENTS.md index a42482035..f62211cc5 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -32,6 +32,21 @@ Before making significant changes: Prefer small, safe, targeted changes consistent with existing conventions. Avoid unrelated refactoring unless it is required for the task. +## Engineering standards (mandatory reading) + +Cross-repository standards live in this repository's `docs/`. Read the applicable one before you change +the matching code, in any SAM-BIM repository: + +- Diagnostics (any `Diagnostic`/`Diagnostics`, severity, detail level, `Diagnostics`/`log` output, or an adapter + around a producer's own messages): `docs/DIAGNOSTICS_STANDARD.md`. +- Grasshopper components (names, GUIDs, parameters, descriptions, versions, diagnostic inputs/outputs): + `docs/GH_COMPONENT_STANDARDS.md` (section K is the new-versus-legacy quick reference for input/output + naming, `Name` versus `NickName`, descriptions and compatibility). + +Both describe implemented behaviour; where they and the code disagree, the code wins and the document needs a fix. +A producer's authoritative severity is preserved, Warnings and Errors survive filtering, and detail never changes +an engineering result. + ## Grasshopper components `docs/GH_COMPONENT_STANDARDS.md` is the canonical SAM-BIM standard for Grasshopper component diff --git a/docs/DIAGNOSTICS_STANDARD.md b/docs/DIAGNOSTICS_STANDARD.md new file mode 100644 index 000000000..66d5b6010 --- /dev/null +++ b/docs/DIAGNOSTICS_STANDARD.md @@ -0,0 +1,154 @@ +# SAM-BIM Unified Diagnostics Standard + +**Status:** canonical. Describes behaviour that is implemented and merged; nothing here is aspirational unless +labelled **Open**. Changes go through a reviewed PR in `SAM-BIM/SAM`. +**Applies to:** every SAM-BIM repository that reports engineering diagnostics (warnings, skipped input, +assumptions, refusals) to a user, a log or a Grasshopper component. +**Sibling:** [GH_COMPONENT_STANDARDS.md](GH_COMPONENT_STANDARDS.md) owns Grasshopper naming, compatibility and versioning +(its section E points back here). The Grasshopper balloon rule in section 8 below is the only part +of Grasshopper behaviour this document states. + +Source of truth is the code. If this text and the code disagree, the code wins and this text is a defect. + +## 1. The API (`SAM.Core`) + +| Type | File | Members | +|---|---|---| +| `DiagnosticSeverity` | `SAM/SAM.Core/Enums/DiagnosticSeverity.cs` | `Info` 0, `Warning` 1, `Error` 2 | +| `DiagnosticDetail` | `SAM/SAM.Core/Enums/DiagnosticDetail.cs` | `Summary` 0 (default), `Standard` 1, `Detailed` 2 | +| `Diagnostic` | `SAM/SAM.Core/Classes/Diagnostics/Diagnostic.cs` | immutable; `Severity`, `Detail`, `Code`, `Subject`, `SourceIndex`, `Message`, `Value`, `Limit`, `Unit` | +| `Diagnostics` | `SAM/SAM.Core/Classes/Diagnostics/Diagnostics.cs` | `Detail`, `Count`, `HasErrors`, `Wants(detail)`, `Add(Diagnostic)`, lazy `Add(severity, detail, subject, Func, ...)`, `Filter(detail)`, `Format()`; enumerable | +| `Query.DiagnosticMessage` | `SAM/SAM.Core/Query/DiagnosticMessage.cs` | the one-line text format | +| `Query.TryGetDiagnosticDetail` | `SAM/SAM.Core/Query/TryGetDiagnosticDetail.cs` | `int` to `DiagnosticDetail`; returns false and `Summary` for an undefined value | +| `Query.Plural` | `SAM/SAM.Core/Query/Plural.cs` | count-aware wording | + +Enum values are published. Never renumber or reorder them. + +`Diagnostic` and `Diagnostics` are **runtime values**: not `IJSAMObject`, not serialised, independent of `Log` / +`LogRecord` / `LogRecordType` (which are unchanged). A null `message` becomes `""`. `Code`, `Value`, `Limit`, +`Unit` and `SourceIndex` are structured data and are **not** part of the text line. + +## 2. Semantics that every producer must keep + +1. **The producer owns the severity.** Choose the severity where the situation is detected, as an enum + value. Never derive it from message text, code names or wording. (OCCT replaced a hard-coded code list + and `Contains` text matching; TAS reads only its producers' own structure or marker constant.) A + severity the producer never stated is not an error: TAS maps `LogRecordType.Undefined` to Warning rather than + inventing a failure. +2. **Warnings and Errors survive every filter.** `Detail` applies to `Info` only. `Add` keeps every Warning and + Error at any collection level; `Filter(level)` returns Info up to `level` plus *all* Warnings and Errors. + Meaning: Info is what was done, Warning is input that was skipped or changed (or an approximation that can + change results), Error is a run that could not complete. +3. **Detail never affects engineering results.** The level is a presentation concern. No geometry, + classification, solver, simulation or numeric path may read it. Each adapter PR proved this with a + same-input comparison at every level (panels identical at levels 0/1/2; TAS `.t3d`/`.tbd` and all TSD + series identical; OCCT panels, cluster and `Successful` identical). +4. **A higher level includes every lower level** (Detailed ⊇ Standard ⊇ Summary). +5. **Do not hide a material Info at Summary.** Put a line at Summary when it records an approximation, + fallback, assumption, dropped input or data source that can change results. OpenStudio's review found + that a coarse per-code detail table hid such lines; the table was revised against every call site. + Lower an Info only when it is known inert, and make unknown or reworded text stay visible. +6. **Drop-on-add is intentional.** `Add` discards Info above the collection's `Detail`, so `Filter` can only + narrow. A caller that needs several views collects at `Detailed` and filters. +7. **Lazy messages.** Use `Wants(detail)` or the `Func` overload for anything costly (lists, formatting + many values) so a dropped record costs nothing. +8. **Default `Summary` changes default output.** Per-item Info lines that were once always shown now need level 1 + or 2. This was owner-accepted in Phases 2 and 3 and is recorded so it is not reported as a regression. A + consumer that parses Info lines must request the level it needs (OCCT: level 2 equals the pre-change list). + +## 3. Message and identity conventions + +- Line format: `Severity: Subject: Message` via `Query.DiagnosticMessage` (`Severity` is `Info`, `Warning` or `Error`; the + subject is omitted for summaries). Message: full sentences, first letter capitalised, units on every value. +- `Subject` identifies the source deterministically (`Solid #3 'Name'`). `SourceIndex` is the index in the + producer's own input or record list so a structured record maps back to its legacy line. +- `Code` is stable and machine-readable. Released codes are never renamed, reused or renumbered; new codes may be added. + Existing families: `PBS###` / `AGS###` / `SNP###` / `BYS###` (`SAM.Analytical.BySolidsDiagnosticCode`, where, for BySolids, 00x are Warnings, + x00 totals, 1xx Standard Info and 2xx Detailed Info), `SAM-OS-*` / `SAM-OSI-*` (OpenStudio), `TAS-*` + (`TasDiagnosticCodes`), `OPT###` (optimisation), OCCT `DiagnosticCode` names. +- `Value` / `Limit` / `Unit` carry the measured number, the threshold and the unit when a comparison produced the + message. They are optional and must not replace the units written in the message. +- Group repeats rather than flooding: BySolids merges repeats of one reason within one plane; Grasshopper runtime messages group + repeats (section 8). Grouping must not remove the full text from the Diagnostics output. + +## 4. Four producer situations, four adapter shapes + +The unified types did not replace the producers' own diagnostics; each repository wrote an additive adapter around what it had. +Choose the shape that matches the producer, in this order of preference: + +| Producer situation | Example | Shape | Rule learned | +|---|---|---|---| +| Producer you control, text-only output | SAM `PanelsBySolid`, `SnapToJunctions`, `AperturesBySolids` (SAM#202) | New overloads take a `Core.Diagnostics`; the legacy `out List` overloads stay and delegate at `Detailed` | Keep the legacy signature and text. The legacy list equals the golden baseline. | +| Typed severity already exists, several mechanisms | OCCT (SAM_OCCT#78): `OcctDiagnostic`, `SolverDiagnostic`, plain strings | `SAM.Core.OCCT` adapters: `DiagnosticRecord` pairs the shared `Diagnostic` with the exact legacy text; severity maps one to one; one record per line where the line is produced | Where severity existed, map it. Where a line was unclassified legacy text, it is Info rather than an invented severity. | +| One typed diagnostic with a code and a severity | OpenStudio (SAM_OpenStudio#28): `OpenStudioDiagnostic` | `OpenStudioDiagnosticRecord` (record validated against its source so it cannot be inconsistent); `ToDiagnostics(detail = Detailed)` | Default adapter detail is `Detailed` so nothing the legacy output showed is dropped. The detail table is pinned by tests against every call site. | +| Many unrelated mechanisms, mostly strings | TAS (SAM_Tas#101): eight producer mechanisms; six get an adapter or the funnel edit, one (raw native return text) is consumed through the evidence object, one (about 30 `out string refusal` queries) is deliberately not adapted; two further non-message producers are out of scope | One adapter per mechanism, plus one funnel edit (`AddNote`/`AddNotes`) that writes the same legacy text and a structured record side by side | Derive severity from structure the producer already has (a refusal list, a `Skipped` list, a documented marker constant). Where there is none, do not adapt it; document why. | + +Adapter rules common to all four: + +- **Additive.** No removed or changed public member. Legacy text, order and codes are unchanged. Verify with a public + API diff (TAS: 0 removed, 0 changed) and, when a funnel was edited, an IL comparison of the changed methods. +- **No producer algorithm change.** Geometry, solver, simulation and numeric results are byte- or bit-identical to + baseline; the PR proves it on real input, not only with unit tests. +- **Undefined enum values** are mapped deliberately and documented (OpenStudio: Error structurally, while its legacy + `IsValid` ignores it; TAS: Warning). State the choice where the mapping lives. +- **Record vs diagnostic consistency.** If a record pairs a `Diagnostic` with legacy text, the constructor should reject a mismatch. + +## 5. Legacy compatibility + +- Legacy `out List` / string-list outputs keep their text and order. They correspond to `Detailed` unless the owner decided otherwise + (BySolids and OCCT outputs default to Summary by owner decision, see 2.8). +- Structured-only records may exist with no legacy line (`PBS007`) when they are *additional* Warnings that cannot hide a failure. +- A new Grasshopper input is a published-contract change. OpenStudio and TAS added none, by owner decision; see 8 and GH-E3 in the + Grasshopper standard. + +## 6. Error handling + +- Failure paths carry an Error. If a failure path ends without one, add a component-level Error (OCCT does for Solve3D and CreateShells). +- Do not swallow exceptions into silent success. Existing catch sites were preserved; uncaught exceptions still propagate. +- Null input to an adapter: either skip nulls (`FormatDiagnostics`) or throw `ArgumentNullException` with the parameter name, and say which in the XML comment. TAS kept an + `AddNotes(null)` throw to match the prior `AddRange(null)` behaviour. +- Fallback to unfiltered legacy output as Info (nothing hidden) if structured records ever disagree with the legacy list (OCCT Solve3D). + +## 7. Testing rules + +Each integration PR carried the same classes of evidence; reuse them. + +1. **Severity preserved:** a test per producer mechanism that a Warning/Error stays Warning/Error at every level. +2. **Survival under filtering:** Warnings and Errors present at `Summary`. +3. **Mutation checks:** break the adapter on purpose (filter drops Warnings; Warning mapped to Info; Errors merged by code) and show tests fail (OCCT: 5, 5, 1 failures). +4. **Pinned tables:** every released code and every detail assignment (OpenStudio `DiagnosticDetailTableTests`). +5. **Real objects:** tests run the real converters, evidence objects and gates, not stubs. +6. **Engineering invariance:** same input at levels 0, 1, 2 gives identical engineering outputs; base vs head comparison for any real-input path. +7. **Grasshopper:** contract tests assert published input names and defaults; headless Rhino 8 (`NUnit` + `Rhino.Testing`) loads a saved definition and checks wires survive. Interactive canvas balloons are *not* proven by headless runs; record them as not run. +8. Record local vs hosted CI honestly, with the commands and the validated SHA (see AGENTS.md). + +## 8. Grasshopper reporting (summary only) + +Implemented in SAM BySolids and SAM_OCCT components: severity maps to the runtime message level (Info: `Diagnostics`/`log` output only; +Warning: GH Warning; Error: GH Error). OCCT shows up to 3 Warnings per code individually and then one "N more ... see Diagnostics" message; Errors are not merged unless text is identical. +Components that expose detail use the hidden optional integer `diagnosticDetail_` (0 default); an invalid value raises a Warning and falls back to Summary +(`Query.TryGetDiagnosticDetail`). OpenStudio and TAS components expose no detail input and raise no new balloons: their adapters are library API only. + +**Open:** whether new components should spell the input `_diagnosticDetail_` (GH-E3), and wiring the TAS adapters into TAS Grasshopper components, +are owner decisions per component. This standard does not prescribe new inputs or balloon behaviour universally. + +## 9. Checklist before changing Diagnostics + +1. Read this document and the phase record of the repository you touch (`documentation/Diagnostics-Phase*-PR.md`). +2. Identify the producer's authoritative severity. If it has none, stop and ask. +3. Keep legacy signatures, text and order; add, do not replace. +4. Assign detail per section 2.5; add the pinned test. +5. Prove engineering outputs unchanged on real input. +6. If a Grasshopper contract (input, output, GUID, version) changes, follow GH_COMPONENT_STANDARDS.md instead of improvising. + +## Appendix: evidence + +| Phase | PR | Record | +|---|---|---| +| 1 Core | [SAM#201](https://github.com/SAM-BIM/SAM/pull/201), merge `293eb645` | `documentation/Diagnostics-Phase1-PR.md` | +| 2 BySolids | [SAM#202](https://github.com/SAM-BIM/SAM/pull/202), merge `fca3d4d4` | `documentation/Diagnostics-Phase2-BySolids-PR.md` | +| 3 OCCT | [SAM_OCCT#78](https://github.com/SAM-BIM/SAM_OCCT/pull/78), merge `de2744a7` | SAM_OCCT `documentation/Diagnostics-Phase3-OCCT-PR.md` | +| 4A OpenStudio | [SAM_OpenStudio#28](https://github.com/SAM-BIM/SAM_OpenStudio/pull/28), merge `f2235d45` | SAM_OpenStudio `documentation/Diagnostics-Phase4A-OpenStudio-PR.md` | +| 4B TAS | [SAM_Tas#101](https://github.com/SAM-BIM/SAM_Tas/pull/101), merge `ba9c95c8` | SAM_Tas `documentation/Diagnostics-Phase4B-TAS-PR.md` | + +Test counts and acceptance results live in those records and in each repository's `PROJECT_PROGRESS.md`; they are not repeated here. diff --git a/docs/GH_COMPONENT_STANDARDS.md b/docs/GH_COMPONENT_STANDARDS.md index d540fd8bd..1d9227449 100644 --- a/docs/GH_COMPONENT_STANDARDS.md +++ b/docs/GH_COMPONENT_STANDARDS.md @@ -1,6 +1,6 @@ # SAM-BIM Grasshopper Component Engineering Standard -**Status:** proposed canonical specification in draft PR #204 into `sow/2026-Q4`, not merged. +**Status:** canonical; merged via [SAM#204](https://github.com/SAM-BIM/SAM/pull/204) into `sow/2026-Q4`. Revised 2026-10-15 after two independent reviews. The validator certifies a deliberately bounded source subset; incomplete extraction or discovery blocks compatibility acceptance. **Owner:** SAM-BIM. Changes to this document go through a reviewed PR in `SAM-BIM/SAM`. @@ -181,7 +181,24 @@ the parameter description while applying the fallback in code. VALIDATED (forbidden).** Those three are published behaviour. Changing one is a behavioural change (section H) and is not a formatting fix. -### `ParamVisibility` semantics — VALIDATED +### GH-B7 — Execution triggers are inputs, not a fourth marker — **VALIDATED (observed), PROPOSED for new components** + +An execution trigger is a Boolean input that makes the component do its work (or not). It uses the same +marker as any input, according to whether the component works without it. Observed in the source (counts are +`Name = "…"` initializers under `SAM/Grasshopper` and the SAM_OCCT, SAM_Tas_Grasshopper and SAM_OpenStudio +Grasshopper folders): + +| Spelling | Meaning in practice | Example | +|---|---|---| +| `_run` (278 initializers) | Required trigger. `Param_Boolean`, `ParamVisibility.Binding`, persistent `false`: nothing runs until the user connects a toggle. | `SAMAnalyticalCheck`: Description "Connect a boolean toggle to run."; `SolveInstance` does `IndexOfInputParam("_run")` and returns when it is false | +| `run_` (8) | Optional trigger. `Optional = true`; when unset the component still returns something (usually its input). | `SAMAnalyticalMergeSpacesByZones`: "If boolean set to false input Analytical Object will be return" | +| `_run_` (2) | Trigger with a defined default. | `GeometryPolycurveLoop2D` | + +Rules: a trigger is never counted as an engineering input in a description, its description says what happens +when it is false, and for a new component that is cheap to compute prefer no trigger at all. An existing trigger is never +renamed to fit a spelling: `_run` is a published `Name` (section F). The markers `_`/`_x_`/`x_` of GH-B still apply +and are as unenforced as for any input (GH-B3). + `ParamVisibility` is a `[Flags]` enum (`Grasshopper/SAM.Core.Grasshopper/Enums/ParamVisibility.cs`) evaluated by `GH_SAMVariableOutputParameterComponent`: @@ -272,6 +289,9 @@ bump (section H) so the *Update component* offer becomes visible. ## E. Engineering Diagnostics +The canonical Diagnostics specification is [DIAGNOSTICS_STANDARD.md](DIAGNOSTICS_STANDARD.md) (API, severity and +detail semantics, adapters, testing). This section keeps only the Grasshopper-facing contract questions. + ### E1 — What is implemented today — **VALIDATED** Three layers exist and are committed: @@ -330,8 +350,8 @@ regression. ### GH-E6 — Scope limit — **OPEN** -The unified Diagnostics architecture is under active development (Phase 4, OpenStudio/TAS adapters, is -not started). This document describes what exists and the direction for new work. It does not +The unified Diagnostics roadmap (SAM#201, SAM#202, SAM_OCCT#78, SAM_OpenStudio#28, SAM_Tas#101) is complete; +the OpenStudio and TAS adapters are library API and added no Grasshopper input. This document describes what exists and the direction for new work. It does not authorise a Diagnostics redesign, and nothing here should be read as describing a capability that is not in the code above. @@ -584,6 +604,25 @@ the name behaviour has, so: - **do not** change a nickname as a way to change what a person sees while claiming the name is untouched, without stating the compatibility reasoning in the PR. +### GH-I3a — `Name` versus `NickName`: what each is for — **VALIDATED (Name), OPEN (NickName)** + +Both are properties of the Grasshopper parameter (`IGH_Param.Name`, `IGH_Param.NickName`) and both are written +into the saved `ParameterData` chunk beside `Description`, `Optional`, `Access` and persistent data +(`SAM_Psychrometrics/docs/GH-Component-Metadata-Standard.md` §2, measured in Rhino 8). + +| | `Name` | `NickName` | +|---|---|---| +| Grasshopper role | the parameter's full name (tooltip, identity-like label) | the short label drawn on the canvas; a user can edit it on an instance | +| SAM use | the lookup key: `Params.IndexOfInputParam("…")` / `IndexOfOutputParam("…")` in `SolveInstance`; the SAM updater (`Modify.DuplicateComponent`) recovers wires and values by name | display; the SAM lookups inspected (`SolveInstance`, `Modify.DuplicateComponent`) use `Name`, not `NickName` | +| On reopen | the file's name wins over the class's declared name; a renamed declaration silently returns -1 and the component reports Invalid data | also restored from the file; effect of changing it on an existing definition **not independently measured** (GH-I3) | +| SAM-BIM convention | published, frozen | equal to `Name` for every new parameter; every inspected component sets `Name == NickName` (for example `_run`, `_relativeHumidity_`) | + +Consequences: for a **new** component set `NickName = Name`, so a person sees the same text that code looks up. For an +**existing** parameter change neither; a differing nickname is an intentional legacy value, kept. A user editing a +nickname on the canvas is harmless to SAM because lookups use `Name`; a code change to `Name` is not. Any `Name` +change needs the migration of section G, a saved `.gh`/`.ghx` roundtrip test, and a check that `SolveInstance` still +resolves every parameter after reopening (GH-G7). + ### GH-I4 — Order is identity-adjacent — **VALIDATED** Order is asserted by the contract and by the runtime tests. Appending a new voluntary parameter at the @@ -670,6 +709,29 @@ parameter whose type cannot be determined, and a baseline that is missing or its --- +## K. Quick reference — new components versus legacy components + +The detailed rules and their status labels are in sections A to J; this table only separates what is required of a +**newly created** component from what is a **recommendation for, or a freeze on,** a legacy one. + +For a newly created component the Ladybug-style input markers (GH-B) and capitalised PascalCase output names (GH-C1) +are the standing SAM-BIM convention; PROPOSED means no tool enforces the input markers, not that a new +component may ignore them. For an existing component the published parameter `Name`s are protected (GH-B2, GH-C3, +GH-F3) and are never bulk-renamed to satisfy either rule. + +| Topic | New component | Legacy component | +|---|---|---| +| Input names (GH-B1, B7) | camelCase with the marker that matches the design: `_x` required, `_x_` optional with default, `x_` optional without default, `_run` / `run_` trigger | keep the published name; never rename to fit | +| Requiredness (GH-B4, B6) | decided by `Optional`, `ParamVisibility`, registered default and code fallback; the marker records intent | frozen: never change `Optional`, visibility or default | +| Output names (GH-C1, C2) | capitalised, PascalCase, no underscores: `Panels`, `AnalyticalModel`, `Diagnostics` | camelCase outputs stay until an approved migration (GH-C3) | +| NickName (GH-A2, I3a) | `NickName = Name` | keep; do not overwrite blindly | +| Descriptions (GH-D) | purpose, SI units, defaults and fallbacks, optional behaviour, limitations, mutual exclusions | improve freely as a metadata change with a patch version bump (GH-D, H) | +| GUID, Name, order, access, type (GH-A5, I1, I4) | choose once; append new optional inputs last | frozen | +| Renaming a published `Name` | not applicable | only with a declared `ParameterRename`, saved `.gh`/`.ghx` roundtrip tests and a `SolveInstance` check (G, GH-G7) | +| Diagnostics output / input | follow [DIAGNOSTICS_STANDARD.md](DIAGNOSTICS_STANDARD.md); whether to add `diagnosticDetail_` / `_diagnosticDetail_` is a per-component decision (GH-E3) | add nothing universally | + +--- + ## Appendix 1 — Evidence index | Rule area | Source of truth | diff --git a/documentation/Unified-Engineering-Standards-Q4.md b/documentation/Unified-Engineering-Standards-Q4.md new file mode 100644 index 000000000..576a09359 --- /dev/null +++ b/documentation/Unified-Engineering-Standards-Q4.md @@ -0,0 +1,36 @@ +# Unified engineering standards consolidation - PR record + +Base: `sow/2026-Q4` (`6167d474`). Branch: `docs/unified-engineering-standards-q4`. Documentation only. +Draft; merge needs owner approval (AGENTS.md). `PROJECT_PROGRESS.md` is not touched (post-merge closeout only). + +## Status +Documents written and checked locally. No source, test, build or Grasshopper contract change. Not merged. + +## Investigation (what already existed) +- The Grasshopper guide **was already promoted** to SAM: `docs/GH_COMPONENT_STANDARDS.md` (SAM#204, 892 lines, status-labelled rules), with the Psychrometrics pilot records + (`SAM_Psychrometrics/docs/GH-Component-Metadata-Standard.md`, SAM `documentation/GH-Parameter-Rename-Migration-Q4.md`, `GH-Component-Standards-Q4.md`). No second Grasshopper guide was created. +- No Diagnostics standard existed. Its content was spread over four phase records and section E of the GH standard, which still said Phase 4 was "not started" and the document itself "draft PR #204, not merged". +- Phase records: SAM `documentation/Diagnostics-Phase1-PR.md`, `Diagnostics-Phase2-BySolids-PR.md`; SAM_OCCT `Diagnostics-Phase3-OCCT-PR.md`; SAM_OpenStudio `Diagnostics-Phase4A-OpenStudio-PR.md`; SAM_Tas `Diagnostics-Phase4B-TAS-PR.md`. + +## Work completed +- New `docs/DIAGNOSTICS_STANDARD.md`, written from the merged source (`SAM.Core` `Diagnostic`, `Diagnostics`, enums, `Query.DiagnosticMessage`, `TryGetDiagnosticDetail`) and the phase records. +- `docs/GH_COMPONENT_STANDARDS.md` (additive): status line corrected (merged); section E points to the Diagnostics standard and the Phase 4 statement is corrected; new GH-B7 (execution triggers `_run` / `run_` / `_run_`, with counts and examples read from source), GH-I3a (`Name` versus `NickName`), and section K (new components versus legacy quick reference). +- `AGENTS.md`: short mandatory "Engineering standards" section linking both documents. + +## Decisions and assumptions +- Two documents were requested; the Grasshopper one already exists, so it was extended rather than duplicated, keeping its stable rule identifiers. +- `_diagnosticDetail_` vs `diagnosticDetail_` (GH-E3) and wiring TAS adapters into components remain OPEN owner decisions; the documents prescribe neither. +- `PROJECT_PROGRESS.md` lacks closeouts for Phases 3, 4A and 4B in SAM. AGENTS.md forbids editing it on a PR branch, so the cross-reference is deferred to the post-merge closeout; the standard's appendix carries the PR and merge-SHA table meanwhile. +- No test counts are repeated; they stay in the phase records. + +## Files changed +`docs/DIAGNOSTICS_STANDARD.md` (new), `docs/GH_COMPONENT_STANDARDS.md`, `AGENTS.md`, this record. + +## Validation (local) +- API names, enum values, signatures and defaults in the Diagnostics standard compared with `SAM/SAM.Core/**` source. +- Trigger spellings and counts from `grep` of `Name = "…"` in the Grasshopper projects; examples read in `SAMAnalyticalCheck.cs`, `SAMAnalyticalMergeSpacesByZones.cs`, `GeometryPolycurveLoop2D.cs`, `SAMPsychrometricsPsychrometrics.cs`. +- Name/NickName wording compared with `SAM_Psychrometrics/docs/GH-Component-Metadata-Standard.md` §2. +- Markdown relative links and rule cross-references checked; `git diff --stat` shows only the files above. Not run: the Python validator tests (no Python on this machine); they are unaffected by a Markdown change. + +## Risks and next step +Duplicate statements may drift between the two standards; each defers to the other for its own area. After merge: post-merge closeout including the Phase 3/4 cross-reference. Then per-repository `AGENTS.md` links (separate PRs): SAM_OCCT, SAM_OpenStudio, SAM_Tas, SAM_Tas_Grasshopper, SAM_Psychrometrics.