SKILL.md v7 sustainability
EPD Build Library
Extract EPD fields from PDFs and write the EPD Library workbook with declared and slab-scaled rows.
No reviews yet 0 installs
epdlibraryextractsustainabilitylca
Versions
- v7 # Changelog ## 0.10.0 - A row whose attribution is `unconfirmed` is graded one confidence step lower (High → Medium → Low) and says why in its remarks. A missing `attribution` is not unconfirmed: artifacts written before the field grade exactly as they did. - `supplier_mismatch` rows explain which holder the downloaded documents declared and that no value was taken from them. ## 0.9.0 - Library workbook gains a `Document Type` column (`EPD` / `PCF` / `LCA` / `Industrial EPD`) immediately after `Manufacturer/Supplier` and before `Confidence Factor` (`document_type_label`), naming which tier produced the number, for the new `vault_epd`, `vault_pcf_lca`, `downloaded_pcf_lca` and `downloaded_industrial_proxy` statuses alongside the existing `downloaded`. - A PCF/LCA row's boundary is **re-gated from build's own extraction** (`boundary_of` on the text build itself extracted, not collect's earlier verdict): when the boundary is not A1-A3 or cradle-to-gate, the GWP columns are left blank and the remark names the boundary TEXT that was actually found (via the new `boundary_detail_of`, e.g. `"A1-C4"` or `"cradle-to-grave"`), not the bare internal enum, rather than trusting a verdict collect reached on a possibly different extraction pass. The re-gate's head-text window is now a named `HEAD_CHARS = 8000`, matching `epd-scan-vault`'s and `epd-collect-artifacts`'s windows exactly (was a bare `4000` literal) — a boundary statement sitting between char 4000 and 8000 was previously admitted upstream but invisible to this re-gate. - `tier_remark` names an online or vault PCF/LCA row's document kind correctly from its `documentType` ("LCA report" vs "product carbon footprint") instead of unconditionally saying "product carbon footprint" — the PCF/LCA tier explicitly searches for LCA reports too. - `confidence_for_entry` extended for the new tiers: vault EPD rows follow the existing family-declaration rule (High unless family/unknown); vault PCF/LCA rows are Medium; online PCF/LCA rows are Medium when found on the manufacturer's own site and **Low** when found via an open-web search result (`pcfSource`), since that is the only tier whose attribution to the manufacturer rests on a search result rather than the manufacturer's own domain or the user's own vault folder. - `incomplete_row_remark` now names every tier that was actually attempted: vault (`vaultStatus`), product PCF/LCA (`pcfStatus`), and industrial (`industrialStatus`) — a blank row states which of the four tiers were tried and why each failed, instead of reading as though only one ran. It also reports a non-preferred or otherwise-unused product-specific vault document (`vaultRejected`, carried from collect — every such document, not only boundary-rejected ones), naming the attached vault source and the actual recorded reason it went unused, so a row says the vault was consulted even when the vault tier itself never ran for that document (a document that does not short-circuit never sets `vaultStatus`). Note that `vaultStatus` and `vaultRejected` CAN both be set on one entry: a preferred document short-circuits and sets `vaultStatus` on a later failed copy, while a non-preferred sibling for the same product lands in `vaultRejected` at the same time. - An industrial-proxy row applied from a vault category document (new in `epd-collect-artifacts`) gets a vault-shaped `Source URL` and a remark naming the attached vault source, instead of a blank Source URL. - `tier_remark` adds provenance sentences for vault rows (naming the attached vault source and that no internet search ran for that product) and for online PCF/LCA rows (naming whether the source was the manufacturer's site or an open-web result, and that the document is not an EN 15804 declaration). - `SKILL.md` updated: the `Document Type` column, the boundary re-gate, the extended confidence rubric, and the four-tier incomplete-row remark. ## 0.8.0 - Build reads `searchHealth` / collect `health` and refuses to write the workbook when they report degradation (exit 4, vendored `environment_health.py`). The gate mirrors each upstream stage's verdict predicate exactly; missing health blocks on pre-contract artifacts are treated as unknown, never degraded. ## 0.7.1 - A failed row's remark now states when the industrial/sector tier was also attempted and failed (`industrialStatus` on the download entry) — without it a row read as though the mandated industrial fallback never ran. ## 0.7.0 - Library workbook gains three columns after `Final Unit`: `Manufacturer/Supplier` (supplier name; the literal `Industrial EPD` for industrial-proxy rows), `Confidence Factor` (deterministic High/Medium/Low rubric; the reason is in the remarks) and `Source URL` (registry page, else PDF URL). The `.md` summary mirrors the columns. - Extraction asks for `validFrom`/`validTo`; every data row's remarks carry a validity sentence. An expired EPD used because nothing newer exists is flagged `**EXPIRED EPD — validity ended <to>; no newer EPD found.**`; when no validity period is found the remark says expiry was not checked. - Industrial-proxy remarks now name the base material and the confidence rationale from the material rule. ## 0.6.0 - Library columns gain `kgCO2e/final unit (A1-A3 fossil)`, `tCO2e/final unit (A1-A3 fossil)`, `kgCO2e/final unit (A1-A3 LULUC)` and `tCO2e/final unit (A1-A3 LULUC)`. Extraction now asks for GWP-fossil and GWP-luluc when an EPD (e.g. EN 15804+A2) separately declares them; both are reported exactly as declared, never summed into or substituted for GWP-total. When neither is declared (typically EN 15804+A1), the columns stay empty and the remark notes the total may include biogenic carbon (IStructE HTCEC §2.2.2.5). The one re-extract retry remains keyed on the total A1–A3 GWP only. ## 0.5.0 - Process `downloaded_industrial_proxy` downloads; remarks state industrial/sector proxy applied. ## 0.4.0 - Emit per-product progress JSON on stdout during `build_library` (flushed) for long runs. ## 0.3.0 - Library columns now include `kgCO2e/final unit (A1-A3)` immediately before `tCO2e/final unit (A1-A3)` (declared-unit and slab rows; both null on miss). - When a downloaded PDF's first extract misses A1–A3, extract is retried once before accepting the miss, so a flaky wine2o2 pass cannot leave Product/EPD filled with blank GWP and blank Remarks on first run. ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 10/13/2026
- v6 # Changelog ## 0.9.0 - Library workbook gains a `Document Type` column (`EPD` / `PCF` / `LCA` / `Industrial EPD`) immediately after `Manufacturer/Supplier` and before `Confidence Factor` (`document_type_label`), naming which tier produced the number, for the new `vault_epd`, `vault_pcf_lca`, `downloaded_pcf_lca` and `downloaded_industrial_proxy` statuses alongside the existing `downloaded`. - A PCF/LCA row's boundary is **re-gated from build's own extraction** (`boundary_of` on the text build itself extracted, not collect's earlier verdict): when the boundary is not A1-A3 or cradle-to-gate, the GWP columns are left blank and the remark names the boundary TEXT that was actually found (via the new `boundary_detail_of`, e.g. `"A1-C4"` or `"cradle-to-grave"`), not the bare internal enum, rather than trusting a verdict collect reached on a possibly different extraction pass. The re-gate's head-text window is now a named `HEAD_CHARS = 8000`, matching `epd-scan-vault`'s and `epd-collect-artifacts`'s windows exactly (was a bare `4000` literal) — a boundary statement sitting between char 4000 and 8000 was previously admitted upstream but invisible to this re-gate. - `tier_remark` names an online or vault PCF/LCA row's document kind correctly from its `documentType` ("LCA report" vs "product carbon footprint") instead of unconditionally saying "product carbon footprint" — the PCF/LCA tier explicitly searches for LCA reports too. - `confidence_for_entry` extended for the new tiers: vault EPD rows follow the existing family-declaration rule (High unless family/unknown); vault PCF/LCA rows are Medium; online PCF/LCA rows are Medium when found on the manufacturer's own site and **Low** when found via an open-web search result (`pcfSource`), since that is the only tier whose attribution to the manufacturer rests on a search result rather than the manufacturer's own domain or the user's own vault folder. - `incomplete_row_remark` now names every tier that was actually attempted: vault (`vaultStatus`), product PCF/LCA (`pcfStatus`), and industrial (`industrialStatus`) — a blank row states which of the four tiers were tried and why each failed, instead of reading as though only one ran. It also reports a non-preferred or otherwise-unused product-specific vault document (`vaultRejected`, carried from collect — every such document, not only boundary-rejected ones), naming the attached vault source and the actual recorded reason it went unused, so a row says the vault was consulted even when the vault tier itself never ran for that document (a document that does not short-circuit never sets `vaultStatus`). Note that `vaultStatus` and `vaultRejected` CAN both be set on one entry: a preferred document short-circuits and sets `vaultStatus` on a later failed copy, while a non-preferred sibling for the same product lands in `vaultRejected` at the same time. - An industrial-proxy row applied from a vault category document (new in `epd-collect-artifacts`) gets a vault-shaped `Source URL` and a remark naming the attached vault source, instead of a blank Source URL. - `tier_remark` adds provenance sentences for vault rows (naming the attached vault source and that no internet search ran for that product) and for online PCF/LCA rows (naming whether the source was the manufacturer's site or an open-web result, and that the document is not an EN 15804 declaration). - `SKILL.md` updated: the `Document Type` column, the boundary re-gate, the extended confidence rubric, and the four-tier incomplete-row remark. ## 0.8.0 - Build reads `searchHealth` / collect `health` and refuses to write the workbook when they report degradation (exit 4, vendored `environment_health.py`). The gate mirrors each upstream stage's verdict predicate exactly; missing health blocks on pre-contract artifacts are treated as unknown, never degraded. ## 0.7.1 - A failed row's remark now states when the industrial/sector tier was also attempted and failed (`industrialStatus` on the download entry) — without it a row read as though the mandated industrial fallback never ran. ## 0.7.0 - Library workbook gains three columns after `Final Unit`: `Manufacturer/Supplier` (supplier name; the literal `Industrial EPD` for industrial-proxy rows), `Confidence Factor` (deterministic High/Medium/Low rubric; the reason is in the remarks) and `Source URL` (registry page, else PDF URL). The `.md` summary mirrors the columns. - Extraction asks for `validFrom`/`validTo`; every data row's remarks carry a validity sentence. An expired EPD used because nothing newer exists is flagged `**EXPIRED EPD — validity ended <to>; no newer EPD found.**`; when no validity period is found the remark says expiry was not checked. - Industrial-proxy remarks now name the base material and the confidence rationale from the material rule. ## 0.6.0 - Library columns gain `kgCO2e/final unit (A1-A3 fossil)`, `tCO2e/final unit (A1-A3 fossil)`, `kgCO2e/final unit (A1-A3 LULUC)` and `tCO2e/final unit (A1-A3 LULUC)`. Extraction now asks for GWP-fossil and GWP-luluc when an EPD (e.g. EN 15804+A2) separately declares them; both are reported exactly as declared, never summed into or substituted for GWP-total. When neither is declared (typically EN 15804+A1), the columns stay empty and the remark notes the total may include biogenic carbon (IStructE HTCEC §2.2.2.5). The one re-extract retry remains keyed on the total A1–A3 GWP only. ## 0.5.0 - Process `downloaded_industrial_proxy` downloads; remarks state industrial/sector proxy applied. ## 0.4.0 - Emit per-product progress JSON on stdout during `build_library` (flushed) for long runs. ## 0.3.0 - Library columns now include `kgCO2e/final unit (A1-A3)` immediately before `tCO2e/final unit (A1-A3)` (declared-unit and slab rows; both null on miss). - When a downloaded PDF's first extract misses A1–A3, extract is retried once before accepting the miss, so a flaky wine2o2 pass cannot leave Product/EPD filled with blank GWP and blank Remarks on first run. ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 10/13/2026
- v5 # Changelog ## 0.8.0 - Build reads `searchHealth` / collect `health` and refuses to write the workbook when they report degradation (exit 4, vendored `environment_health.py`). The gate mirrors each upstream stage's verdict predicate exactly; missing health blocks on pre-contract artifacts are treated as unknown, never degraded. ## 0.7.1 - A failed row's remark now states when the industrial/sector tier was also attempted and failed (`industrialStatus` on the download entry) — without it a row read as though the mandated industrial fallback never ran. ## 0.7.0 - Library workbook gains three columns after `Final Unit`: `Manufacturer/Supplier` (supplier name; the literal `Industrial EPD` for industrial-proxy rows), `Confidence Factor` (deterministic High/Medium/Low rubric; the reason is in the remarks) and `Source URL` (registry page, else PDF URL). The `.md` summary mirrors the columns. - Extraction asks for `validFrom`/`validTo`; every data row's remarks carry a validity sentence. An expired EPD used because nothing newer exists is flagged `**EXPIRED EPD — validity ended <to>; no newer EPD found.**`; when no validity period is found the remark says expiry was not checked. - Industrial-proxy remarks now name the base material and the confidence rationale from the material rule. ## 0.6.0 - Library columns gain `kgCO2e/final unit (A1-A3 fossil)`, `tCO2e/final unit (A1-A3 fossil)`, `kgCO2e/final unit (A1-A3 LULUC)` and `tCO2e/final unit (A1-A3 LULUC)`. Extraction now asks for GWP-fossil and GWP-luluc when an EPD (e.g. EN 15804+A2) separately declares them; both are reported exactly as declared, never summed into or substituted for GWP-total. When neither is declared (typically EN 15804+A1), the columns stay empty and the remark notes the total may include biogenic carbon (IStructE HTCEC §2.2.2.5). The one re-extract retry remains keyed on the total A1–A3 GWP only. ## 0.5.0 - Process `downloaded_industrial_proxy` downloads; remarks state industrial/sector proxy applied. ## 0.4.0 - Emit per-product progress JSON on stdout during `build_library` (flushed) for long runs. ## 0.3.0 - Library columns now include `kgCO2e/final unit (A1-A3)` immediately before `tCO2e/final unit (A1-A3)` (declared-unit and slab rows; both null on miss). - When a downloaded PDF's first extract misses A1–A3, extract is retried once before accepting the miss, so a flaky wine2o2 pass cannot leave Product/EPD filled with blank GWP and blank Remarks on first run. ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 10/8/2026
- v4 # Changelog ## 0.6.0 - Library columns gain `kgCO2e/final unit (A1-A3 fossil)`, `tCO2e/final unit (A1-A3 fossil)`, `kgCO2e/final unit (A1-A3 LULUC)` and `tCO2e/final unit (A1-A3 LULUC)`. Extraction now asks for GWP-fossil and GWP-luluc when an EPD (e.g. EN 15804+A2) separately declares them; both are reported exactly as declared, never summed into or substituted for GWP-total. When neither is declared (typically EN 15804+A1), the columns stay empty and the remark notes the total may include biogenic carbon (IStructE HTCEC §2.2.2.5). The one re-extract retry remains keyed on the total A1–A3 GWP only. ## 0.5.0 - Process `downloaded_industrial_proxy` downloads; remarks state industrial/sector proxy applied. ## 0.4.0 - Emit per-product progress JSON on stdout during `build_library` (flushed) for long runs. ## 0.3.0 - Library columns now include `kgCO2e/final unit (A1-A3)` immediately before `tCO2e/final unit (A1-A3)` (declared-unit and slab rows; both null on miss). - When a downloaded PDF's first extract misses A1–A3, extract is retried once before accepting the miss, so a flaky wine2o2 pass cannot leave Product/EPD filled with blank GWP and blank Remarks on first run. ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 9/28/2026
- v3 # Changelog ## 0.5.0 - Process `downloaded_industrial_proxy` downloads; remarks state industrial/sector proxy applied. ## 0.4.0 - Emit per-product progress JSON on stdout during `build_library` (flushed) for long runs. ## 0.3.0 - Library columns now include `kgCO2e/final unit (A1-A3)` immediately before `tCO2e/final unit (A1-A3)` (declared-unit and slab rows; both null on miss). - When a downloaded PDF's first extract misses A1–A3, extract is retried once before accepting the miss, so a flaky wine2o2 pass cannot leave Product/EPD filled with blank GWP and blank Remarks on first run. ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 9/22/2026
- v2 # Changelog ## 0.2.0 - `isFamilyEpd` is now **tri-state**. An absent or null flag is reported as unknown in the remarks, not silently treated as "product-specific": absence of this field is itself a claim, and reading it favourably let a combined family EPD go out as a clean, unqualified row. - `product_mismatch` rows no longer claim "No PDF downloaded". A PDF was downloaded, extracted and rejected for declaring a different product; the remark now says so. - Family/combined EPDs are now flagged: when extraction reports `isFamilyEpd`, every row derived from that PDF gets a remark noting the source covers several products, not this one specifically, so a reviewer can see the value wasn't declared against this exact product. - `downloaded_unverified` downloads (a PDF was fetched but the product match couldn't be confirmed) are now processed like any other download, with an added remark flagging the match as unverified, instead of being silently skipped. - Reaffirmed: A1–A3 GWP is required per row; a missing value is left empty with an explanatory remark and is never reported as `0`. ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 9/1/2026
- v1 # Changelog ## 0.1.0 - Initial release: `lib/build_library.py` reads `/workspace/work/code/epd-products.json` and `/workspace/work/code/epd-download.json`, extracts A1–A3 GWP, declared unit, and density from downloaded EPD PDFs via wine2o2 (`wine2o2_extract` or `wine2o2_to_text` + `llm_chat_text`), scales declared-unit and dimension-based slab rows, and writes `/workspace/work/outputs/epd-library.xlsx` (EPD Summary sheet) plus `epd-library.md`. Never invents GWP values or density. 8/28/2026