From 7c0ed0dcc54fa2aa128eaa1607915da689a04b8b Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Wed, 7 Oct 2026 23:49:15 +0800 Subject: [PATCH 1/4] Add the SQA and QC framework and project instructions Mount the framework as a submodule at framework/ (v0.4.0), add AGENTS.md and the installed skill copies for Codex CLI (.agents/skills) and Claude Code (.claude/skills). --- .agents/skills/.framework-skills | 3 + .agents/skills/artifact/SKILL.md | 131 ++++++++ .agents/skills/artifact/references/ADR.md | 25 ++ .agents/skills/artifact/references/BC.md | 45 +++ .agents/skills/artifact/references/BMC.md | 21 ++ .agents/skills/artifact/references/BPMN.md | 22 ++ .agents/skills/artifact/references/DCD.md | 27 ++ .agents/skills/artifact/references/DICT.md | 31 ++ .agents/skills/artifact/references/DM.md | 26 ++ .agents/skills/artifact/references/ERD.md | 22 ++ .agents/skills/artifact/references/GOV.md | 16 + .agents/skills/artifact/references/KPI.md | 20 ++ .agents/skills/artifact/references/MIL.md | 48 +++ .agents/skills/artifact/references/OC.md | 27 ++ .agents/skills/artifact/references/PP.md | 64 ++++ .agents/skills/artifact/references/QC.md | 55 ++++ .agents/skills/artifact/references/RC.md | 45 +++ .agents/skills/artifact/references/SA.md | 27 ++ .agents/skills/artifact/references/SD.md | 26 ++ .agents/skills/artifact/references/SSD.md | 21 ++ .agents/skills/artifact/references/TM.md | 18 ++ .agents/skills/artifact/references/TRN.md | 18 ++ .agents/skills/artifact/references/TRR.md | 23 ++ .agents/skills/artifact/references/UC.md | 35 +++ .agents/skills/artifact/references/UCD.md | 24 ++ .agents/skills/artifact/references/US.md | 22 ++ .agents/skills/artifact/templates/ADR.md | 42 +++ .agents/skills/artifact/templates/BC.md | 72 +++++ .agents/skills/artifact/templates/BMC.md | 115 +++++++ .agents/skills/artifact/templates/BPMN.md | 43 +++ .agents/skills/artifact/templates/DCD.md | 50 ++++ .agents/skills/artifact/templates/DICT.md | 37 +++ .agents/skills/artifact/templates/DM.md | 56 ++++ .agents/skills/artifact/templates/ERD.md | 53 ++++ .agents/skills/artifact/templates/GOV.md | 70 +++++ .agents/skills/artifact/templates/KPI.md | 37 +++ .agents/skills/artifact/templates/MIL.md | 60 ++++ .agents/skills/artifact/templates/OC.md | 41 +++ .agents/skills/artifact/templates/PP.md | 63 ++++ .agents/skills/artifact/templates/QC.md | 42 +++ .agents/skills/artifact/templates/RC.md | 51 ++++ .agents/skills/artifact/templates/SA.md | 56 ++++ .agents/skills/artifact/templates/SD.md | 47 +++ .agents/skills/artifact/templates/SSD.md | 42 +++ .agents/skills/artifact/templates/TM.md | 30 ++ .agents/skills/artifact/templates/TRN.md | 60 ++++ .agents/skills/artifact/templates/TRR.md | 43 +++ .agents/skills/artifact/templates/UC.md | 57 ++++ .agents/skills/artifact/templates/UCD.md | 52 ++++ .agents/skills/artifact/templates/US.md | 38 +++ .agents/skills/coding-conventions/SKILL.md | 98 ++++++ .../skills/coding-conventions/references/c.md | 71 +++++ .../coding-conventions/references/cpp.md | 70 +++++ .../coding-conventions/references/csharp.md | 72 +++++ .../coding-conventions/references/python.md | 66 ++++ .../coding-conventions/references/shell.md | 62 ++++ .agents/skills/project-planning/SKILL.md | 283 ++++++++++++++++++ .claude/skills/.framework-skills | 3 + .claude/skills/artifact/SKILL.md | 131 ++++++++ .claude/skills/artifact/references/ADR.md | 25 ++ .claude/skills/artifact/references/BC.md | 45 +++ .claude/skills/artifact/references/BMC.md | 21 ++ .claude/skills/artifact/references/BPMN.md | 22 ++ .claude/skills/artifact/references/DCD.md | 27 ++ .claude/skills/artifact/references/DICT.md | 31 ++ .claude/skills/artifact/references/DM.md | 26 ++ .claude/skills/artifact/references/ERD.md | 22 ++ .claude/skills/artifact/references/GOV.md | 16 + .claude/skills/artifact/references/KPI.md | 20 ++ .claude/skills/artifact/references/MIL.md | 48 +++ .claude/skills/artifact/references/OC.md | 27 ++ .claude/skills/artifact/references/PP.md | 64 ++++ .claude/skills/artifact/references/QC.md | 55 ++++ .claude/skills/artifact/references/RC.md | 45 +++ .claude/skills/artifact/references/SA.md | 27 ++ .claude/skills/artifact/references/SD.md | 26 ++ .claude/skills/artifact/references/SSD.md | 21 ++ .claude/skills/artifact/references/TM.md | 18 ++ .claude/skills/artifact/references/TRN.md | 18 ++ .claude/skills/artifact/references/TRR.md | 23 ++ .claude/skills/artifact/references/UC.md | 35 +++ .claude/skills/artifact/references/UCD.md | 24 ++ .claude/skills/artifact/references/US.md | 22 ++ .claude/skills/artifact/templates/ADR.md | 42 +++ .claude/skills/artifact/templates/BC.md | 72 +++++ .claude/skills/artifact/templates/BMC.md | 115 +++++++ .claude/skills/artifact/templates/BPMN.md | 43 +++ .claude/skills/artifact/templates/DCD.md | 50 ++++ .claude/skills/artifact/templates/DICT.md | 37 +++ .claude/skills/artifact/templates/DM.md | 56 ++++ .claude/skills/artifact/templates/ERD.md | 53 ++++ .claude/skills/artifact/templates/GOV.md | 70 +++++ .claude/skills/artifact/templates/KPI.md | 37 +++ .claude/skills/artifact/templates/MIL.md | 60 ++++ .claude/skills/artifact/templates/OC.md | 41 +++ .claude/skills/artifact/templates/PP.md | 63 ++++ .claude/skills/artifact/templates/QC.md | 42 +++ .claude/skills/artifact/templates/RC.md | 51 ++++ .claude/skills/artifact/templates/SA.md | 56 ++++ .claude/skills/artifact/templates/SD.md | 47 +++ .claude/skills/artifact/templates/SSD.md | 42 +++ .claude/skills/artifact/templates/TM.md | 30 ++ .claude/skills/artifact/templates/TRN.md | 60 ++++ .claude/skills/artifact/templates/TRR.md | 43 +++ .claude/skills/artifact/templates/UC.md | 57 ++++ .claude/skills/artifact/templates/UCD.md | 52 ++++ .claude/skills/artifact/templates/US.md | 38 +++ .claude/skills/coding-conventions/SKILL.md | 98 ++++++ .../skills/coding-conventions/references/c.md | 71 +++++ .../coding-conventions/references/cpp.md | 70 +++++ .../coding-conventions/references/csharp.md | 72 +++++ .../coding-conventions/references/python.md | 66 ++++ .../coding-conventions/references/shell.md | 62 ++++ .claude/skills/project-planning/SKILL.md | 283 ++++++++++++++++++ .gitmodules | 3 + AGENTS.md | 60 ++++ framework | 1 + 117 files changed, 5706 insertions(+) create mode 100644 .agents/skills/.framework-skills create mode 100644 .agents/skills/artifact/SKILL.md create mode 100644 .agents/skills/artifact/references/ADR.md create mode 100644 .agents/skills/artifact/references/BC.md create mode 100644 .agents/skills/artifact/references/BMC.md create mode 100644 .agents/skills/artifact/references/BPMN.md create mode 100644 .agents/skills/artifact/references/DCD.md create mode 100644 .agents/skills/artifact/references/DICT.md create mode 100644 .agents/skills/artifact/references/DM.md create mode 100644 .agents/skills/artifact/references/ERD.md create mode 100644 .agents/skills/artifact/references/GOV.md create mode 100644 .agents/skills/artifact/references/KPI.md create mode 100644 .agents/skills/artifact/references/MIL.md create mode 100644 .agents/skills/artifact/references/OC.md create mode 100644 .agents/skills/artifact/references/PP.md create mode 100644 .agents/skills/artifact/references/QC.md create mode 100644 .agents/skills/artifact/references/RC.md create mode 100644 .agents/skills/artifact/references/SA.md create mode 100644 .agents/skills/artifact/references/SD.md create mode 100644 .agents/skills/artifact/references/SSD.md create mode 100644 .agents/skills/artifact/references/TM.md create mode 100644 .agents/skills/artifact/references/TRN.md create mode 100644 .agents/skills/artifact/references/TRR.md create mode 100644 .agents/skills/artifact/references/UC.md create mode 100644 .agents/skills/artifact/references/UCD.md create mode 100644 .agents/skills/artifact/references/US.md create mode 100644 .agents/skills/artifact/templates/ADR.md create mode 100644 .agents/skills/artifact/templates/BC.md create mode 100644 .agents/skills/artifact/templates/BMC.md create mode 100644 .agents/skills/artifact/templates/BPMN.md create mode 100644 .agents/skills/artifact/templates/DCD.md create mode 100644 .agents/skills/artifact/templates/DICT.md create mode 100644 .agents/skills/artifact/templates/DM.md create mode 100644 .agents/skills/artifact/templates/ERD.md create mode 100644 .agents/skills/artifact/templates/GOV.md create mode 100644 .agents/skills/artifact/templates/KPI.md create mode 100644 .agents/skills/artifact/templates/MIL.md create mode 100644 .agents/skills/artifact/templates/OC.md create mode 100644 .agents/skills/artifact/templates/PP.md create mode 100644 .agents/skills/artifact/templates/QC.md create mode 100644 .agents/skills/artifact/templates/RC.md create mode 100644 .agents/skills/artifact/templates/SA.md create mode 100644 .agents/skills/artifact/templates/SD.md create mode 100644 .agents/skills/artifact/templates/SSD.md create mode 100644 .agents/skills/artifact/templates/TM.md create mode 100644 .agents/skills/artifact/templates/TRN.md create mode 100644 .agents/skills/artifact/templates/TRR.md create mode 100644 .agents/skills/artifact/templates/UC.md create mode 100644 .agents/skills/artifact/templates/UCD.md create mode 100644 .agents/skills/artifact/templates/US.md create mode 100644 .agents/skills/coding-conventions/SKILL.md create mode 100644 .agents/skills/coding-conventions/references/c.md create mode 100644 .agents/skills/coding-conventions/references/cpp.md create mode 100644 .agents/skills/coding-conventions/references/csharp.md create mode 100644 .agents/skills/coding-conventions/references/python.md create mode 100644 .agents/skills/coding-conventions/references/shell.md create mode 100644 .agents/skills/project-planning/SKILL.md create mode 100644 .claude/skills/.framework-skills create mode 100644 .claude/skills/artifact/SKILL.md create mode 100644 .claude/skills/artifact/references/ADR.md create mode 100644 .claude/skills/artifact/references/BC.md create mode 100644 .claude/skills/artifact/references/BMC.md create mode 100644 .claude/skills/artifact/references/BPMN.md create mode 100644 .claude/skills/artifact/references/DCD.md create mode 100644 .claude/skills/artifact/references/DICT.md create mode 100644 .claude/skills/artifact/references/DM.md create mode 100644 .claude/skills/artifact/references/ERD.md create mode 100644 .claude/skills/artifact/references/GOV.md create mode 100644 .claude/skills/artifact/references/KPI.md create mode 100644 .claude/skills/artifact/references/MIL.md create mode 100644 .claude/skills/artifact/references/OC.md create mode 100644 .claude/skills/artifact/references/PP.md create mode 100644 .claude/skills/artifact/references/QC.md create mode 100644 .claude/skills/artifact/references/RC.md create mode 100644 .claude/skills/artifact/references/SA.md create mode 100644 .claude/skills/artifact/references/SD.md create mode 100644 .claude/skills/artifact/references/SSD.md create mode 100644 .claude/skills/artifact/references/TM.md create mode 100644 .claude/skills/artifact/references/TRN.md create mode 100644 .claude/skills/artifact/references/TRR.md create mode 100644 .claude/skills/artifact/references/UC.md create mode 100644 .claude/skills/artifact/references/UCD.md create mode 100644 .claude/skills/artifact/references/US.md create mode 100644 .claude/skills/artifact/templates/ADR.md create mode 100644 .claude/skills/artifact/templates/BC.md create mode 100644 .claude/skills/artifact/templates/BMC.md create mode 100644 .claude/skills/artifact/templates/BPMN.md create mode 100644 .claude/skills/artifact/templates/DCD.md create mode 100644 .claude/skills/artifact/templates/DICT.md create mode 100644 .claude/skills/artifact/templates/DM.md create mode 100644 .claude/skills/artifact/templates/ERD.md create mode 100644 .claude/skills/artifact/templates/GOV.md create mode 100644 .claude/skills/artifact/templates/KPI.md create mode 100644 .claude/skills/artifact/templates/MIL.md create mode 100644 .claude/skills/artifact/templates/OC.md create mode 100644 .claude/skills/artifact/templates/PP.md create mode 100644 .claude/skills/artifact/templates/QC.md create mode 100644 .claude/skills/artifact/templates/RC.md create mode 100644 .claude/skills/artifact/templates/SA.md create mode 100644 .claude/skills/artifact/templates/SD.md create mode 100644 .claude/skills/artifact/templates/SSD.md create mode 100644 .claude/skills/artifact/templates/TM.md create mode 100644 .claude/skills/artifact/templates/TRN.md create mode 100644 .claude/skills/artifact/templates/TRR.md create mode 100644 .claude/skills/artifact/templates/UC.md create mode 100644 .claude/skills/artifact/templates/UCD.md create mode 100644 .claude/skills/artifact/templates/US.md create mode 100644 .claude/skills/coding-conventions/SKILL.md create mode 100644 .claude/skills/coding-conventions/references/c.md create mode 100644 .claude/skills/coding-conventions/references/cpp.md create mode 100644 .claude/skills/coding-conventions/references/csharp.md create mode 100644 .claude/skills/coding-conventions/references/python.md create mode 100644 .claude/skills/coding-conventions/references/shell.md create mode 100644 .claude/skills/project-planning/SKILL.md create mode 100644 .gitmodules create mode 100644 AGENTS.md create mode 160000 framework diff --git a/.agents/skills/.framework-skills b/.agents/skills/.framework-skills new file mode 100644 index 0000000..5dad221 --- /dev/null +++ b/.agents/skills/.framework-skills @@ -0,0 +1,3 @@ +artifact +coding-conventions +project-planning diff --git a/.agents/skills/artifact/SKILL.md b/.agents/skills/artifact/SKILL.md new file mode 100644 index 0000000..283b77d --- /dev/null +++ b/.agents/skills/artifact/SKILL.md @@ -0,0 +1,131 @@ +--- +name: artifact +description: Create, edit or review any project document artifact — Business Case, Stakeholder Analysis, KPI, Business Model Canvas, BPMN, milestones/gateways, use case diagram, user stories, use cases, domain model, SSD, operation contracts, sequence diagrams, DCD, ERD, ADRs, SQA review records, traceability matrix, governance, QC checklists. Scaffolds the file with the correct ID, CrossReference and links, then gives the required sections for that type. +--- + +# Artifact + +Every artifact is a markdown file with `## Metadata` and `## Version History` +tables, a registered short-name ID, and links only at the bottom. Types and +their related types are in `framework/registry/artifact-catalog.md`; this +project's file locations and next versions are in `docs/artifact-registry.md`. + +## Creating an artifact + +1. Find the type's short name (`BC`, `SA`, `MIL`, `DM`, `ADR`, `RC`, …) in + the catalog. If the type is new, add it to the catalog and the registry. +2. Scaffold — this sets the ID, `CrossReference` (only artifacts that exist), + the bottom link block and the registry version: + + ```bash + bash framework/scripts/new-artifact.sh [--file ] [--title ""] [--cite =]... + ``` + + `--file` is required for multi-document types (`MIL`, `UC`, `ADR`, `RC`, + `QC`); single-document types default to the registry's `Primary File`. + The script refuses to overwrite an existing file. +3. Read `framework/.agents/skills/artifact/references/.md` (cite-for + hints and required sections), then fill in the file. Keep the sections in + order and replace every ``. + +## Editing an artifact + +Append a row to `## Version History` on every change (a status change such as +`Proposed` → `Accepted`, or a content change). To re-check `CrossReference`, +run `bash framework/scripts/find-crossreferences.sh `. When the first +instance of a type is created, add it to the `CrossReference` of every +existing artifact that lists that type as a candidate (with a new Version +History row). + +### Version History rule + +The table keeps only the **two latest** changes; git holds the rest. Columns: + +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-01 | Accepted | Jens Tirsvad Nielsen | S02 | Added Risks section
Fixed scope wording | [a1b2c3d] | + +- **Status** — `Proposed`, `Accepted`, `Rejected` or `Deprecated` for every + artifact type (`ADR` also has `Superseded by ADR-NNNN`); `Approved` is not + used. A new row starts `Proposed`. When the review gives Go, that row + becomes `Accepted` and the row before it becomes `Deprecated`, so at most + one row is `Accepted`: the latest reviewed one. If the reviewer refuses the + change for good (not a No-Go that returns it for rework), the row becomes + `Rejected`; the earlier `Accepted` row stays `Accepted` and nothing is + deprecated. +- **Change** — a short summary of what this row changed; several lines are + separated with `
`. +- **Commit** — the commit that made the change, as a reference-style link + defined at the bottom of the file + (`[a1b2c3d]: https://///commit/`; Gitea and + GitHub use the same `/commit/` form). A row cannot contain its own commit + hash, so a new row says `pending` until the commit exists. Leave it + `pending` and do not commit: only when the user asks for a commit, then: + 1. commit the edited documents; + 2. run `bash framework/scripts/resolve-pending-commits.sh ...`, which + replaces `pending` with the link to that commit and adds the definition; + 3. commit the result as a follow-up commit, before opening the PR. Do not + amend: an amend changes the hash, so the link would point at a commit + that is never pushed. +- When adding a third row, delete the oldest row and its now-unused commit + link definition. Never rewrite the content of the two retained rows other + than resolving `pending` and setting Status to `Accepted` / `Deprecated` + after a Go review. +- Every document uses this format, including QC checklists. A file still in the + old four-column format is converted when next edited (old row kept as + `Initial version`, plus a new row for the conversion). + +## Reviewing an artifact + +1. Take the QC checklist named in the catalog (`framework/qc/qc-*.md`). +2. Create the review record: `new-artifact.sh RC …`, following + `references/RC.md`. +3. Add or update the instance's row in the Traceability Matrix. + +## Rules + +- **Links:** all links are reference-style, defined once at the bottom of the + file after a final `---`, labelled by the target's ID (`[SA-001]`), never + inline. No links → omit the block. +- **CrossReference:** cite only artifacts that exist now. Empty if none. +- **People:** use exact stakeholder IDs from the project's Stakeholder + Analysis (`S01`) for owners, reviewers and RACI. Never invent role names. +- **Diagrams** are PlantUML blocks; check them with + `bash framework/scripts/render-diagrams.sh --server ` (or set + `PLANTUML_URL`) before review. +- **QC checklists** are framework files: every criterion is tagged with an + ISO/IEC 25010:2023 characteristic, and they never mention real instances. +- **IDs:** `-`, 3 digits (`BC-001`); `ADR` uses 4 digits; + `RC` is sequential across all types; QC is `QC--`. +- **Language:** the PO language and the register of each artifact type are in + the `Languages` section of `docs/artifact-registry.md` (registers: + `IT Executive English` for high-level, `IT Professional English` for + technical). +- **Language and Domain rows:** every artifact of a type written in the PO + language has `Language` and `Domain` rows in its Metadata table, so a reviewer + sees at once how to read it. `Language` is a BCP 47 code (`da`, `en`); + `Domain` is a value from the domain list in the registry's `Languages` + section (for example `it`, `medical`, `construction`), because the same + language can carry a different professional vocabulary. `new-artifact.sh` + fills both from the registry's `PO language` and `PO domain` settings and + leaves a placeholder (with a warning) when a setting is missing. Technical + types (OC, SD, DCD, ERD, ADR, TM, RC, QC, source code) have no such rows: + they are always professional IT English. To list every document's language + and domain, see `check-languages.sh --list`. +- **One file per artifact:** each type the registry marks "Written in the PO + language" exists once, in that language, under its normal name + (`business-case.md`). There is no translated twin (`business-case.da.md`) + and no authoritative English source; two files drift apart. Use the PO terms + from the dictionary (`DICT`). Types marked "No" (OC, SD, DCD, ERD, ADR, TM, + RC, QC, source code) stay in professional IT English. +- **Structural vocabulary stays English:** Metadata keys, section headings, + IDs and statuses are the same in every language, because the scripts read + them (`find-crossreferences.sh`, `new-artifact.sh`, + `resolve-pending-commits.sh`, `sync-project.sh`, `check-plan.sh`). Only the + content (prose and table cells) is in the PO language. +- **Changing an artifact's language** is a material change: add a Version + History row ("language en to da") and review it again. Git history keeps the + earlier language. +- **Dictionary:** the Domain Model uses the PO term; the Operation Contract, + Sequence Diagram, Design Class Diagram and ERD use the IT term. Every pair is + recorded in `docs/dictionary.md` (`DICT`). diff --git a/.agents/skills/artifact/references/ADR.md b/.agents/skills/artifact/references/ADR.md new file mode 100644 index 0000000..eb9e4c3 --- /dev/null +++ b/.agents/skills/artifact/references/ADR.md @@ -0,0 +1,25 @@ +# Architecture Decision Record (ADR) + +Files: `docs/adr/adr-NNNN-kebab-case-title.md`. `NNNN` is 4-digit and +sequential; the ID is `ADR-NNNN` and must match the filename number (an +intentional exception to the usual 3-digit IDs). Use +`new-artifact.sh ADR --file docs/adr/adr-NNNN-title.md`. + +Cite in `CrossReference` the artifacts this decision is about (pass each +with `--cite =`); ADR has no fixed candidate list. + +## Required sections (after Metadata / Version History) + +- **Context** — the problem, the options evaluated and the forces + (cost, risk, constraints). +- **Decision** — the outcome in one or two sentences, no hedging. +- **Consequences** — two bold labels, **Positive:** and **Negative:**, each + with a bullet list. +- **Affected Artifacts** — other artifacts impacted, as `[ID]` links, or a + single `-` if none. + +Allowed statuses: `Proposed`, `Accepted`, `Rejected`, `Deprecated`, +`Superseded by ADR-NNNN`; no others (an ADR is never `Approved`). Status +changes (`Proposed` → `Accepted` → `Deprecated` / `Superseded by ADR-NNNN`, or +`Proposed` → `Rejected`) are new rows in `## Version History` (only the two +latest are kept; earlier ones stay in git); never rewrite a retained row. diff --git a/.agents/skills/artifact/references/BC.md b/.agents/skills/artifact/references/BC.md new file mode 100644 index 0000000..f3f07da --- /dev/null +++ b/.agents/skills/artifact/references/BC.md @@ -0,0 +1,45 @@ +# Business Case (BC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SA** — Stakeholders section — cite S-IDs instead of re-describing roles +- **BMC** — Cost–Benefit Assessment must agree with its cost/revenue blocks +- **BPMN** — Forward: the process that realizes the objectives +- **KPI** — Success Criteria — each criterion is operationalized by a KPI +- **UCD** — Forward: scope expressed as actors and goals + +## Required sections (after Metadata / Version History) + +In this order: + +1. **Executive Summary** — one paragraph framing the problem and the + proposed solution. +2. **Methodological and Standards Foundation** — states the methodology + (e.g. Larman's *Applying UML and Patterns*) and quality standards (e.g. + ISO/IEC 25002/25010/25019) the rest of the document and downstream + artifacts are built on. +3. **Problem Statement** — the recurring problems that justify the project. +4. **Business Opportunity** — what becomes possible if the problem is + solved. +5. **Objectives** — concrete, verifiable statements of what the project + will achieve. +6. **Scope** — split into `## In Scope` and `## Out of Scope` subsections. +7. **Expected Benefits** — split into `### Tangible Benefits` and + `### Intangible Benefits`. +8. **Strategic Alignment** — how the project supports organizational goals. +9. **Success Criteria** — a table with explicit, measurable targets (not + aspirations). +10. **Risks** — a table with `Risk | Impact | Mitigation` columns; every + risk must have a mitigation. +11. **Assumptions** — bullet list, kept distinct from Constraints. +12. **Constraints** — bullet list, kept distinct from Assumptions. +13. **Cost–Benefit Assessment** — a table (`Costs | Benefits`); may be + qualitative if explicitly justified. +14. **Stakeholders** — a table referencing exact stakeholder IDs from the + project's Stakeholder Analysis (e.g. `S01`, `S07`) if `SA` exists per + the CrossReference check above. **Never re-describe stakeholder roles + inline instead of citing their IDs** — this is the single most common + defect found when reviewing Business Cases (see `QC-BC-001`'s Common + Defects). +15. **Recommendation** — a single, unambiguous "proceed" or "do not + proceed" statement. diff --git a/.agents/skills/artifact/references/BMC.md b/.agents/skills/artifact/references/BMC.md new file mode 100644 index 0000000..6fe740d --- /dev/null +++ b/.agents/skills/artifact/references/BMC.md @@ -0,0 +1,21 @@ +# Business Model Canvas (BMC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — Objectives the canvas operationalizes; cost/revenue must agree with its Cost–Benefit Assessment +- **SA** — Value Propositions, Customer Segments, Channels — cite S-IDs +- **BPMN** — Key Activities — cite the process that realizes them +- **KPI** — Measures of value proposition / revenue — cite KPI IDs + +## Required sections (after Metadata / Version History) + +1. **Purpose / Scope** — one paragraph. +2. **Canvas** — all 9 building blocks populated, none empty: Key Partners, + Key Activities, Key Resources, Value Propositions, Customer + Relationships, Channels, Customer Segments, Cost Structure, Revenue + Streams. Must stay reviewable as a single, concise overview. +3. **Assumptions** — explicit and testable (how would we know it is wrong?). +4. **Consistency Check** — Revenue Streams vs Cost Structure agree; + Segments/Channels match stakeholder groups from `SA`. + +Cite Business Case objectives (`[BC-001]`) instead of restating them. diff --git a/.agents/skills/artifact/references/BPMN.md b/.agents/skills/artifact/references/BPMN.md new file mode 100644 index 0000000..6c8cda9 --- /dev/null +++ b/.agents/skills/artifact/references/BPMN.md @@ -0,0 +1,22 @@ +# BPMN Process Model (BPMN) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The stated business goal/objective the process serves +- **SA** — Participants / lanes — cite S-IDs +- **UCD** — Forward link: actors and goals derived from this process + +## Required sections (after Metadata / Version History) + +1. **Purpose and Business Goal** — the Business Case objective realized + (cite `[BC-001]`). +2. **Participants (Pools / Lanes)** — every participant, mapped to an `SA` + stakeholder ID where one exists. +3. **Process Diagram** — valid BPMN 2.0. Message flows cross pool + boundaries; sequence flows do not. Keep the diagram source next to the + document (BPMN has no native markdown form) so it stays diffable. +4. **Element Table** — `Element | Type | Lane | Description` for every + event, activity and gateway. Every gateway states its type (XOR/AND/OR) + and its matching join. +5. **Path Coverage** — every path runs from a start event to a defined end + event; no dead ends. diff --git a/.agents/skills/artifact/references/DCD.md b/.agents/skills/artifact/references/DCD.md new file mode 100644 index 0000000..c1df83b --- /dev/null +++ b/.agents/skills/artifact/references/DCD.md @@ -0,0 +1,27 @@ +# Design Class Diagram (DCD) (DCD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **DM** — Concepts each design class refines — names must stay consistent +- **SD** — Messages that become method signatures +- **ERD** — Forward: persistence of the classes' attributes + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope**. +2. **Diagram** — PlantUML class diagram: visibility markers (`+` `-` `#`) + on every member; association vs aggregation vs composition vs dependency + used per true ownership/lifecycle; multiplicity and navigability on every + association. +3. **Class Table** — `Class | Refines (Domain Model concept) | + Responsibility (one sentence) | Attributes | Operations`. SOLID applied; + no god classes; names consistent with the Domain Model. +4. **Method Traceability** — `Method signature | Operation Contract / SD + message`; every method traces to one. +5. **Pattern Annotations** — `Pattern | Classes | Rationale`, explicit. +6. **Dependency Check** — note confirming no circular class/package + dependencies (or an explicit justification). + +## Terminology + +Class and attribute names are the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.agents/skills/artifact/references/DICT.md b/.agents/skills/artifact/references/DICT.md new file mode 100644 index 0000000..d9d6872 --- /dev/null +++ b/.agents/skills/artifact/references/DICT.md @@ -0,0 +1,31 @@ +# Domain Dictionary (DICT) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The business goals the vocabulary serves +- **SA** — The Product Owner (and other business stakeholders) whose terms are recorded +- **DM** — Concepts whose PO terms are recorded + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the PO language and domain (from the registry's + `Languages` section, also in the `Language` and `Domain` Metadata rows) and + what the dictionary covers. +2. **Dictionary** — one row per term: + `PO term | Language | IT term | Definition | Used as PO term in | Used as IT term in`. + The definition is written in the PO language. "Used as PO term in" and + "Used as IT term in" list artifact types (for example `DM` and `OC, SD, + DCD, ERD`). +3. **Rules** — the register split (PO term in the Domain Model, use cases and + user stories; IT term in the Operation Contract, Sequence Diagram, Design + Class Diagram and ERD) and one IT term per PO term. + +One dictionary has one domain: the PO terms are the domain's own words (for +example `medical`), the IT terms are professional IT. A project whose PO terms +come from two domains keeps one dictionary per domain, each with its own +`Domain` row; a term never appears in two. + +Keep the dictionary in step with the Domain Model: a new concept gets a row +in the same change. When the PO language is English, the PO and IT columns can +still differ (a business word against a technical one); keep the file anyway +when the two registers use different words. diff --git a/.agents/skills/artifact/references/DM.md b/.agents/skills/artifact/references/DM.md new file mode 100644 index 0000000..de35ff6 --- /dev/null +++ b/.agents/skills/artifact/references/DM.md @@ -0,0 +1,26 @@ +# Domain Model (DM) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UC** — Source of every concept (noun phrases) — cite each `UC-*` used +- **UCD** — Scope check: actors/goals the model must cover +- **SSD** — Forward: system operations that act on these concepts + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — which use cases the model covers. +2. **Diagram** — PlantUML class diagram (or linked source) showing + **concepts, attributes and associations only — no operations**. + Business language throughout ("Sale", not "SaleTable"/"SaleClass"). +3. **Concept Table** — `Concept | Definition | Attributes | Source (use + case noun phrase / glossary)`. Every concept traces to a noun in a use + case or glossary. Attributes are simple domain data, not foreign-key-like + references (model those as associations). +4. **Association Table** — `From | Association name (with reading + direction) | To | Multiplicity (both ends)`. All multiplicities present. +5. **Generalizations** — only true "is-a" relationships, never inheritance + for code reuse. + +## Terminology + +Concept names are the PO terms recorded in the dictionary (`DICT`); add a row there for each new concept. diff --git a/.agents/skills/artifact/references/ERD.md b/.agents/skills/artifact/references/ERD.md new file mode 100644 index 0000000..27a98ac --- /dev/null +++ b/.agents/skills/artifact/references/ERD.md @@ -0,0 +1,22 @@ +# Entity Relationship Diagram (ERD) (ERD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **DCD** — **Required source**: classes/attributes each entity persists; data types must match + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope**. +2. **Diagram** — PlantUML entity diagram with PK/FK marked on every entity and + cardinality (1:1, 1:N) on every relationship; N:M relationships resolved + through explicit junction entities. +3. **Entity Table** — per entity: `Attribute | Type | PK/FK | Nullable | + Source (DCD class.attribute)`. Types consistent with the DCD; consistent + naming, no implementation-specific abbreviations. +4. **Relationship Table** — `Entity | Cardinality | Entity | FK | Rule`. +5. **Normalization Notes** — 3NF confirmed; any denormalization documented + with its performance justification. + +## Terminology + +Entity and column names follow the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.agents/skills/artifact/references/GOV.md b/.agents/skills/artifact/references/GOV.md new file mode 100644 index 0000000..d441bc6 --- /dev/null +++ b/.agents/skills/artifact/references/GOV.md @@ -0,0 +1,16 @@ +# Governance / ARB Workflow (GOV) + +One per project: `docs/sqa/governance.md`. Sections: Purpose, ARB Review +Workflow (submission → QC review → `RC-*` record → Go/No-Go → sign-off → +Traceability update), RACI by Artifact Category, Escalation Rules, Cadence. +The template has the standard text; fill the RACI. + +**RACI cells use exact stakeholder IDs from the project's Stakeholder +Analysis (`S`), never role names.** + +Related singletons (edit the existing file, no scaffold needed): +- `PRC` — `framework/process/review-checklist-process.md`, the narrative + review procedure. Framework-level; do not add project data. +- `CSG` — `docs/sqa/coding-standards-governance.md`. Scope is *defining and + governing* (not enforcing) language standards and style guides, as set by + the project Business Case's scope. diff --git a/.agents/skills/artifact/references/KPI.md b/.agents/skills/artifact/references/KPI.md new file mode 100644 index 0000000..7e5b01c --- /dev/null +++ b/.agents/skills/artifact/references/KPI.md @@ -0,0 +1,20 @@ +# KPI Definitions (KPI) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The Success Criteria row each KPI is aligned to (`[BC-001]`) + +## Required sections (after Metadata / Version History) + +1. **Purpose** — which Business Case success criteria this document + operationalizes. +2. **KPI Definitions** — one row per KPI: `KPI ID | Name | SMART statement | + Baseline | Target | Business Case Success Criterion | Owner | Frequency & + Method | Data Source`. +3. **Thresholds** — per KPI: acceptable / at-risk / failing bounds. +4. **Reporting** — where results are reported and to whom. + +Rules: every KPI is SMART (reject goals/activities like "improve quality"); +baseline *and* target are both present; `Owner` is a stakeholder ID from +the project's Stakeholder Analysis (e.g. `S07`), never free text; give KPIs stable IDs (`KPI-01`, …) so +Milestones can cite them. diff --git a/.agents/skills/artifact/references/MIL.md b/.agents/skills/artifact/references/MIL.md new file mode 100644 index 0000000..9a8e621 --- /dev/null +++ b/.agents/skills/artifact/references/MIL.md @@ -0,0 +1,48 @@ +# Milestone / Gateway (MIL) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The objective / constraint (e.g. project duration) the milestone traces to +- **KPI** — The KPI IDs evaluated at this gate +- **US** — Forward: the user stories (`US-.`) that deliver this gateway + +## Required sections (after Metadata / Version History) + +1. **Purpose** — what decision this gate supports. +2. **Deliverable** — the concrete, tangible output evaluated (never just a date). +3. **Go / No-Go Criteria** — objectively checkable, one per row. +4. **Dependencies** — other milestones that must precede this one. +5. **Traceability** — the Business Case objective and/or KPI ID(s) it maps to. +6. **Ownership** — owner and approving reviewer as `SA` stakeholder IDs. +7. **Target Date** — consistent with Business Case constraints. +8. **Tasks** — the implementation-level breakdown for this phase, one row + per task: `# | Task | Summary | Needs its own Use Case/User Story? | + Reference`. `Task` is a short title (becomes the Issue title on sync); + `Summary` is one to three sentences of real context — what the task + actually involves and why, grounded in this project's own documents, not + a restatement of the title — so someone reading the Issue on Gitea/GitHub + understands it without opening this file (becomes the Issue body). + +## Breaking a phase into tasks + +Not every task needs a use case — only model one when the task is something +a user, or another system, actually does: + +- **Needs a use case/user story** — "User resets password", "Admin exports + customer report", "Payment service processes refund". Set the column to + `Yes` and put the `US-…`/`UC-…` ID in Reference. +- **Plain task, no use case** — "Refactor authentication middleware", "Add + database index", "Upgrade React version", "Fix null-pointer bug", "Add + unit tests", "Configure CI pipeline", "Optimize SQL query". Set the column + to `No` and leave Reference blank, or point at the design artifact it + implements (`DCD-…`, `OC-…`). + +The hierarchy is: Business goal → Feature/requirement → Use case/user story +→ Tasks. The use case explains *why* a feature exists; tasks explain *how* +the team implements it — most tasks stay at that level. + +## Syncing to Gitea/GitHub + +Each `MIL-*` gateway becomes one Milestone on the git host; its `## Tasks` +row become Issues assigned to that milestone. Use the `project-planning` +skill and `framework/scripts/sync-project.sh` — do not create these by hand. diff --git a/.agents/skills/artifact/references/OC.md b/.agents/skills/artifact/references/OC.md new file mode 100644 index 0000000..3b74009 --- /dev/null +++ b/.agents/skills/artifact/references/OC.md @@ -0,0 +1,27 @@ +# Operation Contract (OC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SSD** — **Required source**: each contract traces to exactly one SSD message +- **DM** — Classes/associations named in pre/postconditions +- **SD** — Forward: the design realizing each contract's postconditions + +## Required sections (after Metadata / Version History) + +One block per system operation, each containing: + +1. **Operation** — complete signature: name, parameter types, return type + (must match the SSD message). +2. **Cross References** — the SSD message it traces to (one contract per + message) and the Domain Model concepts touched. +3. **Preconditions** — required state before execution, expressed in Domain + Model terms. +4. **Postconditions** — state changes only, in Larman's style: *instance + created / instance associated / attribute modified*. Declarative ("what"), + never algorithmic ("how"); avoid vague text like "system processes the + request". +5. **Exceptions** — error conditions, each with the failing precondition. + +## Terminology + +Use the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.agents/skills/artifact/references/PP.md b/.agents/skills/artifact/references/PP.md new file mode 100644 index 0000000..80784fe --- /dev/null +++ b/.agents/skills/artifact/references/PP.md @@ -0,0 +1,64 @@ +# Project Plan (PP) + +One per project: `docs/project-plan.md`. Schedules the phases (`MIL-*` +gateways) over the Business Case's timeline/duration constraint. + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` +checks this for you): + +- **BC** — the duration/constraint and objectives the plan schedules against +- **SA** — the communication cadence (e.g. sync frequency) the phase length follows +- **MIL** — every phase gateway the plan schedules (add each as it is created) +- **US** — the gateway user stories, once they exist + +## Required sections (after Metadata / Version History) + +1. **Purpose** — what the plan schedules and over what constraint. +2. **Planning Assumptions** — start date, phase length, any resolved + conflicts the phasing follows (cite `SA`/`BC` where relevant). +3. **Gateway Schedule** — one row per phase: `Gateway | Document | Window | + Decision date | Owner | Stories | Main deliverable | Milestone`. One row + per `MIL-*`. `Milestone` links to that phase's Gitea/GitHub Milestone once + `sync-project.sh` has created it (see "Phases, tasks and the git host" + below); leave it blank until then. +4. **Timeline diagram** — a PlantUML Gantt chart, one bar per phase plus a + milestone marker per Go/No-Go decision. +5. **Scope Coverage** — maps each Business Case scope item to the gateway + that delivers it. +6. **Dependencies** — the gateway order (usually a simple chain) and what a + No-Go does to later dates. +7. **Plan Risks** — risks specific to the plan (schedule slip, dependency + risk), separate from the Business Case's own Risks table. +8. **Open Issues** — anything unresolved (start date to confirm, ambiguous + targets, missing checklists needed by a later phase). + +## Phases, tasks and the git host + +Each phase is a `MIL-*` gateway document (see `references/MIL.md`), which +also holds that phase's task breakdown in its own `## Tasks` section. The +Project Plan does not repeat the tasks — it only lists the phases and their +schedule, plus a link to each phase's Milestone once synced (see above). Use +the `project-planning` skill to break a phase into tasks and sync phases (as +Milestones) and tasks (as Issues) to Gitea/GitHub: + +```bash +bash framework/scripts/sync-project.sh # dry run — prints the plan, no network calls +bash framework/scripts/sync-project.sh --apply # creates/updates Milestones and Issues +``` + +`--apply` needs a token: `GITEA_TOKEN` (Gitea, a personal access token — not +a deploy key) or `gh auth login` (GitHub). `GITEA_TOKEN` can come from a +`.env` file at the project root (copy `.env.example`, never commit it). If +`.env` is missing or has no token when a sync is needed, ask the user for +one and create `.env` from `.env.example` with it rather than skipping the +sync or inventing a value. + +After a real `--apply` run, copy each Milestone's URL into the Gateway +Schedule's `Milestone` column (a content update, not a status change — it +does not need a new `## Version History` row). + +## Validating + +`PP` has no QC checklist yet (open item) — validate a plan against the +Business Case constraint it schedules and against `## Go / No-Go Criteria` +in each `MIL-*` it lists, rather than a dedicated checklist. diff --git a/.agents/skills/artifact/references/QC.md b/.agents/skills/artifact/references/QC.md new file mode 100644 index 0000000..8395a60 --- /dev/null +++ b/.agents/skills/artifact/references/QC.md @@ -0,0 +1,55 @@ +# Quality Criteria checklist (QC) + +A QC checklist is the reusable review checklist for one artifact **type**. +It lives in the framework (`framework/qc/qc-.md`), is +project-independent, and **must never name or link a real artifact +instance** (no `[BC-001]`, no `RC-*`, no stakeholder IDs). Refer to types +generically ("the Stakeholder Analysis"). + +- **ID:** `QC--` (e.g. `QC-BC-001`); the version starts + at `001` and only changes when the checklist itself is revised. +- **Create:** `new-artifact.sh QC --id QC-XX-001 --file framework/qc/qc-.md + --cite QC-=framework/qc/qc-.md ...` +- **CrossReference:** the QC checklists immediately backward and forward in + Larman's chain, in both directions: + +``` +QC-SA → QC-BC → { QC-BMC, QC-BPMN, QC-KPI } → QC-MIL +QC-BC, QC-SA → QC-UCD → { QC-US, QC-UC } +QC-UC → QC-DM → QC-SSD → QC-OC → QC-SD → QC-DCD → QC-ERD +QC-ADR ↔ QC-DCD, QC-ERD +QC-DCD, QC-ADR → { QC-PY, QC-CL, QC-CPP, QC-CS } (language code checklists) +``` + +## Cross-cutting checklists + +`QC-LANG-001` (`framework/qc/qc-language-domain.md`, language and domain) is +not tied to one artifact type. Apply it together with the checklist of the +artifact's own type to every type the registry marks "Written in the PO +language"; the review record (`RC`) lists both checklists and keeps the rows of +each. It has an ID in the `QC-` form like the others, but the short +name `LANG` is not an artifact type: there is no `LANG` instance, template or +catalog row. Add another cross-cutting checklist only when a rule applies to +several types and does not belong in any one of their checklists. + +## Version History statuses + +The statuses are `Proposed`, `Accepted`, `Rejected` and `Deprecated`, as for every +artifact type (rule in the `artifact` skill, "Version History rule"). + +## Required sections (after Metadata / Version History) + +- **Purpose** — why this type matters, what decision it supports. +- **Quality Criteria Checklist** — `# | Criterion | Level | ISO/IEC 25010 + Characteristic(s) | Notes`. `Level` is `Mandatory` (baseline every instance + must meet) or `Optional` (advanced, may be deferred); a checklist with an + extra column (e.g. `Format`) keeps `Level` right after `Criterion`. Every criterion is tagged with at least one of + the eight ISO/IEC 25010:2023 characteristics (Functional Suitability, + Performance Efficiency, Compatibility, Usability, Reliability, Security, + Maintainability, Portability). Never add an untagged criterion. +- **Common Defects** — anti-patterns a reviewer rejects on sight. +- **Traceability Rule** — Backward / Forward bullets with the same `[QC-*]` + labels as `CrossReference`. + +Also add an entry to `framework/CHANGELOG.md`. Reviews of real instances are +recorded in the project as `RC-*` records, not in the checklist. diff --git a/.agents/skills/artifact/references/RC.md b/.agents/skills/artifact/references/RC.md new file mode 100644 index 0000000..e89eed5 --- /dev/null +++ b/.agents/skills/artifact/references/RC.md @@ -0,0 +1,45 @@ +# SQA Review Record (RC) + +One record per review of a specific artifact instance against its QC +checklist. Create one for **every** artifact instance in the project. + +- **File:** `docs/sqa/reviews/rc--.md` +- **ID:** `RC-NNN`, sequential across all artifact types (the registry's + Next Available Version for `RC`). +- **Create:** `new-artifact.sh RC --file docs/sqa/reviews/rc-NNN-.md + --cite = --cite QC--001=framework/qc/.md` + +## Required sections (after Metadata / Version History) + +- **Artifact Under Review** — links to the instance and to the QC checklist + used, the scope (`full review`, or `delta re-review` with the criteria + covered, the reason and the earlier record), the artifact's language and + domain (its `Language` and `Domain` rows, `n/a` for a technical type) and + the language reviewer (a stakeholder ID, or `none`). +- **Checklist Results** — `# | Criterion | Status (Pass/Fail/N-A) | + Evidence/Notes`, one row per criterion copied from the QC checklist, in + the same order. +- **Language and Domain Results** — only for an artifact of a type written in + the PO language: the rows of `QC-LANG-001`, same columns and order. `N-A` is + not allowed for criteria 3, 5 and 9. +- **Overall Verdict** — Go / Go-with-conditions / No-Go, with rationale. +- **Action Items** — `Action | Owner | Due`. `Owner` is a stakeholder ID from + the project's Stakeholder Analysis, never a role name. Every `Fail` and + every condition gets an item. + +## After the review + +- The reviewer must not be the artifact's author (see governance). For a + PO-language artifact the reviewer must read its language and know its + domain, or a second reviewer, the language reviewer, does and is named on + the record; see `process/review-checklist-process.md`. +- The record is in English. Quote evidence that is the artifact's own wording + in its language, with a short gloss. +- Add or update the instance's row in the Traceability Matrix, including + this `RC-*` in "Last Reviewed". +- On **Go**, set the reviewed artifact's latest `## Version History` row to + `Accepted` and the row before it to `Deprecated` (`Approved` is not a + status). On Go-with-conditions the status stays `Proposed` until the action + items are closed. If the change is refused for good, set the row to + `Rejected` (the earlier `Accepted` row stays). +- Step-by-step narrative: `framework/process/review-checklist-process.md`. diff --git a/.agents/skills/artifact/references/SA.md b/.agents/skills/artifact/references/SA.md new file mode 100644 index 0000000..a7307d4 --- /dev/null +++ b/.agents/skills/artifact/references/SA.md @@ -0,0 +1,27 @@ +# Stakeholder Analysis (SA) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — Business Case objectives each stakeholder concern traces to (Business Goal Alignment section) + +## Required sections (after Metadata / Version History) + +1. **Purpose** — why the analysis exists and the methodology it follows. +2. **Stakeholder Summary Table** — `ID | Name | Role/Title | Organization | + Power Level | Interest Level | Quadrant | Primary Concern (Business + Language)`. Every row fully filled; no unclassified stakeholder. +3. **Power/Interest Classification Rationale** — narrative per quadrant, + consistent with the table. +4. **Primary Concerns and FURPS+ Mapping** — each concern in business + language *and* mapped to a FURPS+ attribute. +5. **Communication Requirements** — channel, frequency, deliverable type, + tied to a project phase or milestone. +6. **Conflicting Interests and Mitigations** — every conflict has a + mitigation. +7. **Traceability Analysis** — stakeholder → actor/use case mapping, and + business-goal alignment citing Business Case (`[BC-001]`) objectives. +8. **Sign-Off**. + +Stakeholder IDs (`S01`, `S02`, …) are **stable: never renumbered or reused**. +Every other artifact cites them for RACI, ownership and review assignment +(`AGENTS.md` rule 3). Add new stakeholders with the next free `S`. diff --git a/.agents/skills/artifact/references/SD.md b/.agents/skills/artifact/references/SD.md new file mode 100644 index 0000000..a8b2476 --- /dev/null +++ b/.agents/skills/artifact/references/SD.md @@ -0,0 +1,26 @@ +# Sequence Diagram (Design) (SD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **OC** — **Required source**: the contract whose postconditions each diagram realizes +- **DCD** — Forward: classes/methods these messages become + +## Required sections (after Metadata / Version History) + +One block per realized Operation Contract: + +1. **Realizes** — the contract (`[OC-…]`) and its operation name. +2. **Diagram** — PlantUML sequence diagram: sync (solid filled arrow), + async (open arrow), returns (dashed); activations matching the call + nesting; `create` / `destroy` shown for transient objects; + `loop` / `alt` / `opt` fragments for conditional/repeated behavior. +3. **Pattern Annotations** — table `Pattern (GRASP/GoF) | Applied to | + Rationale`. Patterns are labelled, never implicit. +4. **Postcondition Coverage** — `Postcondition | Satisfied by message`; + every postcondition of the contract must be covered. +5. **Responsibility Check** — a short note showing no god-object receives + all messages (low coupling, high cohesion). + +## Terminology + +Use the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.agents/skills/artifact/references/SSD.md b/.agents/skills/artifact/references/SSD.md new file mode 100644 index 0000000..16e8f94 --- /dev/null +++ b/.agents/skills/artifact/references/SSD.md @@ -0,0 +1,21 @@ +# System Sequence Diagram (SSD) (SSD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UC** — **Required source** of every SSD: cite the use case (name and ID) it depicts +- **DM** — Concepts behind message parameters / returned values +- **OC** — Forward: contract per system operation shown + +## Required sections (after Metadata / Version History) + +1. **Source Use Case** — name and ID (`[UC-…]`) and the specific scenario. +2. **Diagram** — PlantUML sequence diagram with just the actor and + `:System`; **no internal objects**. Dashed return arrows for operations + that produce a result. One scenario per diagram — separate diagrams for + alternate/exception flows (or state them out of scope). +3. **System Operations Table** — `Step | Message (verb phrase) | Parameters + | Return | Use case step`. Messages match the use case's main success + scenario step-for-step; justify any deviation. Message names become the + Operation Contract names. +4. **Lifecycle Notes** — creation/destruction of the System instance where + relevant (session/transaction scope). diff --git a/.agents/skills/artifact/references/TM.md b/.agents/skills/artifact/references/TM.md new file mode 100644 index 0000000..e09ceef --- /dev/null +++ b/.agents/skills/artifact/references/TM.md @@ -0,0 +1,18 @@ +# Traceability Matrix (TM) + +One matrix per project: `docs/sqa/traceability-matrix.md`. It makes the +Business Case's cross-artifact traceability target measurable. + +## Required sections (after Metadata / Version History) + +- **Purpose**. +- **Traceability Table** — one row per artifact instance: + `Artifact Instance | Type | Language | Domain | Upstream (Backward Link) | + Downstream (Forward Link) | Last Reviewed (RC-ID)`. `Language` and `Domain` + copy the artifact's Metadata rows so a reviewer can pick the right reviewer + from the matrix; they are `-` for a technical type (OC, SD, DCD, ERD, ADR, + TM, RC, QC, source code). `check-languages.sh --list` prints the same values. + Add or update a row whenever an instance is created or reviewed. +- **Coverage Notes** — which types have no instance yet, and how to read + `-`: in Upstream it means foundational; in Downstream it means nothing + is built on it yet; in Last Reviewed it means no `RC-*` exists yet. diff --git a/.agents/skills/artifact/references/TRN.md b/.agents/skills/artifact/references/TRN.md new file mode 100644 index 0000000..64b2b7d --- /dev/null +++ b/.agents/skills/artifact/references/TRN.md @@ -0,0 +1,18 @@ +# Reviewer Training (TRN) + +The reusable training material for reviewers. It mitigates the "resistance to +standardized reviews" risk of the Business Case. Framework-level, like `PRC`: +no project data, no stakeholder IDs, no links to project artifacts. The +*record* that a session happened (date, attendees, result) is a project +document, written after the session. + +- **File:** `framework/process/reviewer-training.md` (singleton, version `001`). +- **Create:** `new-artifact.sh TRN`. +- **CrossReference:** `PRC` (the procedure the training teaches). + +## Required sections (after Metadata / Version History) + +Purpose, Audience and Prerequisites, Learning Objectives, Agenda (modules with +minutes), Module Notes, Exercises (each with input and expected result), +Assessment (how and pass criteria), Session Record (what to capture). +Exercises use a seeded, generic example, not a real project artifact. diff --git a/.agents/skills/artifact/references/TRR.md b/.agents/skills/artifact/references/TRR.md new file mode 100644 index 0000000..70f083e --- /dev/null +++ b/.agents/skills/artifact/references/TRR.md @@ -0,0 +1,23 @@ +# Reviewer Training Record (TRR) + +The project's record that a reviewer training session happened: the evidence +that mitigates the "resistance to standardized reviews" risk and satisfies a +"training delivered and recorded" gateway criterion. It records one session +of the framework's training material (`TRN`); it holds project data +(stakeholder IDs, dates), so it lives in the project, not in the framework. + +- **File:** `docs/sqa/reviewer-training-record.md` (one per project; add a new + row under `## Attendees and Assessment` and a Version History row for each + further session). +- **Create:** `new-artifact.sh TRR --file docs/sqa/reviewer-training-record.md + --cite TRN-001=framework/process/reviewer-training.md`. +- **CrossReference:** `TRN` (the material that was taught). + +## Required sections (after Metadata / Version History) + +Session (material, date, facilitator, format), Attendees and Assessment +(stakeholder IDs, attended, Pass or Not yet against the assessment in the +material, and the languages the reviewer reads and the domains they know, for +example `da, en; it, medical`: the review process uses it to choose a reviewer +for a PO-language artifact), Feedback, Follow-ups (action, owner as a stakeholder ID, due). +Fill the record after the session; never before. diff --git a/.agents/skills/artifact/references/UC.md b/.agents/skills/artifact/references/UC.md new file mode 100644 index 0000000..fcb5da8 --- /dev/null +++ b/.agents/skills/artifact/references/UC.md @@ -0,0 +1,35 @@ +# Use Case (UC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UCD** — Actor and use-case names must match it exactly +- **US** — Stories this use case decomposes into +- **SA** — Stakeholders & Interests — cite S-IDs +- **DM** — Forward: concepts derived from this use case's nouns +- **SSD** — Forward: the SSD depicting this use case's main scenario + +## Location + +`docs/uc-NNN/uc.md`, one folder per use case (create with +`new-artifact.sh UC --file docs/uc-NNN/uc.md`). The artifacts this use case +affects are saved in the same folder; see the `project-planning` skill, "Use +cases get their own folder". + +## Required sections (after Metadata / Version History) + +Pick the format explicitly (`Format: Brief | Casual | Fully Dressed`) and +state the **scope/level** (summary, user-goal, subfunction). Always: primary +actor, pre/postconditions, goal-perspective wording with no UI or +implementation detail. + +- **Brief** — a single paragraph summarizing only the main success scenario. +- **Casual** — informal multi-paragraph narrative; may mention some + alternate flows. +- **Fully Dressed** — all sections, in order: Scope, Level, Primary Actor, + Stakeholders and Interests (cite `SA` S-IDs), Preconditions, + Postconditions (success guarantee), Main Success Scenario (numbered + steps), Extensions / Alternative Flows (reference `<>` / + `<>` use cases), Special Requirements / Business Rules (per step), + Open Issues. + +Title and actor names must match `UCD` and `US` exactly. diff --git a/.agents/skills/artifact/references/UCD.md b/.agents/skills/artifact/references/UCD.md new file mode 100644 index 0000000..c7ce5bd --- /dev/null +++ b/.agents/skills/artifact/references/UCD.md @@ -0,0 +1,24 @@ +# Use Case Diagram (UCD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SA** — Each actor traces to a stakeholder need — cite S-IDs +- **BC** — Scope / objectives the boundary reflects +- **US** — Forward: stories whose role must match an actor here +- **UC** — Forward: the use cases detailing each goal shown + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the system boundary in words. +2. **Diagram** — actors with correct stereotypes (`<>`, + `<>`), a labelled system boundary, `<>` / `<>` + used per UML 2.5.1 (not as generic "uses"). Embed PlantUML or link the + diagram source; no UI or implementation detail. +3. **Actor Table** — `Actor | Stereotype | Stakeholder ID (SA) | Goals + (use cases)`. No orphan actors: every actor appears in at least one use + case. +4. **Use Case Table** — `Use Case | Actor(s) | Goal`, names as **verb + phrases describing actor goals** ("Place Order"), not system operations + ("Validate Input"). +5. **Relationships** — every `<>` / `<>` with a one-line + justification. diff --git a/.agents/skills/artifact/references/US.md b/.agents/skills/artifact/references/US.md new file mode 100644 index 0000000..554f93a --- /dev/null +++ b/.agents/skills/artifact/references/US.md @@ -0,0 +1,22 @@ +# User Story (US) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UCD** — The story's role must match an actor defined here +- **UC** — The use case each story traces to +- **BC** — Objective / epic the story ultimately supports +- **MIL** — The gateway (epic) each story delivers — one or more stories per gateway + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the epic(s) covered. +2. **Story List** — each story with a stable ID `US-.` (e.g. + `US-001.01`, so it cannot be confused with the document ID `US-001`): + - Statement: **As a** ``, **I want** ``, **so that** + `` — one goal per story, no implementation detail. + - **Acceptance Criteria** — clear and testable (Given/When/Then works). + - **Traces to** — the use case (`UC-…`) or epic it comes from; a gateway + (`MIL-…`) is an epic. + - **Size** — fits a single iteration. +3. **INVEST Check** — one line confirming Independent, Negotiable, + Valuable, Estimable, Small, Testable (flag any exception with reason). diff --git a/.agents/skills/artifact/templates/ADR.md b/.agents/skills/artifact/templates/ADR.md new file mode 100644 index 0000000..05a4acf --- /dev/null +++ b/.agents/skills/artifact/templates/ADR.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +Allowed Status values: `Proposed`, `Accepted`, `Rejected`, `Deprecated`, `Superseded by ADR-NNNN`. + +--- + +## Context + + + +## Decision + + + +## Consequences + +**Positive:** + +- + +**Negative:** + +- + +## Affected Artifacts + +- [] — , or a single "-" if none + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/BC.md b/.agents/skills/artifact/templates/BC.md new file mode 100644 index 0000000..653014e --- /dev/null +++ b/.agents/skills/artifact/templates/BC.md @@ -0,0 +1,72 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Executive Summary + +## Methodological and Standards Foundation + +## Problem Statement + +## Business Opportunity + +## Objectives + +## Scope + +### In Scope + +### Out of Scope + +## Expected Benefits + +### Tangible Benefits + +### Intangible Benefits + +## Strategic Alignment + +## Success Criteria + +| # | Criterion | Target | Measure | +| --- | --- | --- | --- | + +## Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | + +## Assumptions + +## Constraints + +## Cost–Benefit Assessment + +| Costs | Benefits | +| --- | --- | + +## Stakeholders + +| Stakeholder ID (SA) | Interest in this project | +| --- | --- | + +## Recommendation + + — + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/BMC.md b/.agents/skills/artifact/templates/BMC.md new file mode 100644 index 0000000..a4f14fb --- /dev/null +++ b/.agents/skills/artifact/templates/BMC.md @@ -0,0 +1,115 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose / Scope + +Operationalizes: ([BC-]) + +## Canvas + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Key PartnersKey ActivitiesValue PropositionsCustomer RelationshipsCustomer Segments
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
Key ResourcesChannels
+ +
    +
  • +
+
+ +
    +
  • +
+
Cost StructureRevenue Streams
+ +
    +
  • +
+
+ +
    +
  • +
+
+ + +## Assumptions + +| # | Assumption | How it can be tested | +| --- | --- | --- | + +## Consistency Check + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/BPMN.md b/.agents/skills/artifact/templates/BPMN.md new file mode 100644 index 0000000..0cbe4ce --- /dev/null +++ b/.agents/skills/artifact/templates/BPMN.md @@ -0,0 +1,43 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Business Goal + +Realizes: ([BC-]) + +## Participants + +| Pool / Lane | Participant | Stakeholder ID (SA) | +| --- | --- | --- | + +## Process Diagram + + + +## Element Table + +| Element | Type (event / activity / gateway) | Lane | Description | +| --- | --- | --- | --- | + +## Path Coverage + +| Path | Start event | End event | +| --- | --- | --- | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/DCD.md b/.agents/skills/artifact/templates/DCD.md new file mode 100644 index 0000000..7ee1c17 --- /dev/null +++ b/.agents/skills/artifact/templates/DCD.md @@ -0,0 +1,50 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Diagram + +```plantuml +@startuml +class Controller { + -repo : Repository + +operationName(param : Type) : ReturnType +} +class Entity +Controller --> Entity : uses +@enduml +``` + +## Class Table + +| Class | Refines (Domain Model concept) | Responsibility | Attributes | Operations | +| --- | --- | --- | --- | --- | + +## Method Traceability + +| Method signature | Operation Contract / SD message | +| --- | --- | + +## Pattern Annotations + +| Pattern | Classes | Rationale | +| --- | --- | --- | + +## Dependency Check + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/DICT.md b/.agents/skills/artifact/templates/DICT.md new file mode 100644 index 0000000..da5f2b8 --- /dev/null +++ b/.agents/skills/artifact/templates/DICT.md @@ -0,0 +1,37 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +Maps each Product Owner (PO) term to its professional IT term. PO language: +. + +## Dictionary + +| PO term | Language | IT term | Definition | Used as PO term in | Used as IT term in | +| --- | --- | --- | --- | --- | --- | +| | | | | DM | OC, SD, DCD, ERD | + +## Rules + +- The Domain Model, use cases and user stories use the PO term; the Operation + Contract, Sequence Diagram, Design Class Diagram and ERD use the IT term. +- One IT term per PO term and one PO term per IT term; no synonyms. + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/DM.md b/.agents/skills/artifact/templates/DM.md new file mode 100644 index 0000000..8e46404 --- /dev/null +++ b/.agents/skills/artifact/templates/DM.md @@ -0,0 +1,56 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +Covers: + +## Diagram + +Concepts, attributes and associations only — no operations. + +```plantuml +@startuml +class Order { + date + status +} +class Customer { + name +} +Customer "1" --> "0..*" Order : places +@enduml +``` + +## Concept Table + +| Concept | Definition | Attributes | Source (use case / glossary) | +| --- | --- | --- | --- | + +## Association Table + +| From | Association (reading direction) | To | Multiplicity | +| --- | --- | --- | --- | + +## Generalizations + +| General | Specializations | Is-a justification | +| --- | --- | --- | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/ERD.md b/.agents/skills/artifact/templates/ERD.md new file mode 100644 index 0000000..4a3c5bc --- /dev/null +++ b/.agents/skills/artifact/templates/ERD.md @@ -0,0 +1,53 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Diagram + +```plantuml +@startuml +hide circle +entity CUSTOMER { + * id : int <> + -- + name : string +} +entity ORDER { + * id : int <> + -- + * customer_id : int <> +} +CUSTOMER ||--o{ ORDER : places +@enduml +``` + +## Entity Table + +### + +| Attribute | Type | PK/FK | Nullable | Source (DCD class.attribute) | +| --- | --- | --- | --- | --- | + +## Relationship Table + +| Entity | Cardinality | Entity | FK | Rule | +| --- | --- | --- | --- | --- | + +## Normalization Notes + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/GOV.md b/.agents/skills/artifact/templates/GOV.md new file mode 100644 index 0000000..8f58c1d --- /dev/null +++ b/.agents/skills/artifact/templates/GOV.md @@ -0,0 +1,70 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + +Defines how this project applies Quality Criteria (QC) checklists to real +artifact instances, producing SQA Review Records and Go/No-Go decisions. + +## ARB Review Workflow + +1. **Submission** — the artifact owner submits an instance for review, + identifying its type's QC checklist (`framework/qc/qc-*.md`). +2. **QC Checklist Review** — the assigned reviewer (see RACI) applies the + checklist criterion by criterion. +3. **Review Record** — the reviewer documents the outcome as an SQA Review + Record (`RC-*` under `docs/sqa/reviews/`). +4. **Go/No-Go Decision** — the Accountable role for the artifact category + decides; contested or cross-cutting cases escalate to the ARB Chair. +5. **Sign-off** — on **Go**, the artifact's `## Version History` gets an + `Accepted` row (the previous row becomes `Deprecated`). On **Go-with-conditions**, status stays `Proposed` until + the Action Items are closed. On **No-Go**, the artifact returns to its + owner. +6. **Traceability Update** — the Traceability Matrix is updated with the + instance and its `RC-*` reference. + +## RACI by Artifact Category + +Fill every cell with stakeholder IDs from the Stakeholder Analysis (`S`), +never role names. + +| Artifact Category | Responsible (runs the review) | Accountable (Go/No-Go owner) | Consulted | Informed | +| --- | --- | --- | --- | --- | +| Strategic (Stakeholder Analysis, Business Case, BMC) | S | S | S | S | +| Process/Business (BPMN, KPI, Milestones/Gateways) | S | S | S | S | +| Requirements (Use Case Diagram, User Story, Use Case) | S | S | S | S | +| Modeling/Design (Domain Model, SSD, Operation Contract, Sequence Diagram, DCD, ERD) | S | S | S | S | + +Cross-cutting escalations and disputed verdicts are Accountable to the ARB +Chair (`S`), overriding the category-level Accountable role. + +## Escalation Rules + +- A **No-Go** verdict, or any disagreement between the Responsible reviewer + and the category's Accountable owner, escalates to the ARB Chair. +- A reviewer may not review an instance they authored. +- Repeated No-Go verdicts (2 or more) on the same artifact type trigger a + review of the corresponding `QC-*` checklist itself. + +## Cadence + +- Reviews are triggered per artifact instance as it is produced or revised. +- QC checklists are reviewed annually. + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/KPI.md b/.agents/skills/artifact/templates/KPI.md new file mode 100644 index 0000000..2cfad59 --- /dev/null +++ b/.agents/skills/artifact/templates/KPI.md @@ -0,0 +1,37 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## KPI Definitions + +| KPI ID | Name | SMART statement | Baseline | Target | Business Case success criterion | Owner (S-ID) | Frequency & method | Data source | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| KPI-01 | | | | | | | | | + +## Thresholds + +| KPI ID | Acceptable | At risk | Failing | +| --- | --- | --- | --- | + +## Reporting + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/MIL.md b/.agents/skills/artifact/templates/MIL.md new file mode 100644 index 0000000..794a2d7 --- /dev/null +++ b/.agents/skills/artifact/templates/MIL.md @@ -0,0 +1,60 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Deliverable + + + +## Go / No-Go Criteria + +| # | Criterion (objectively checkable) | Go | No-Go | +| --- | --- | --- | --- | + +## Dependencies + +| Depends on | Reason | +| --- | --- | + +## Traceability + +| Business Case objective / KPI / user story | Reference | +| --- | --- | + +## Ownership + +| Role | Stakeholder ID (SA) | +| --- | --- | +| Owner | | +| Approving reviewer | | + +## Target Date + +YYYY-MM-DD — + +## Tasks + +| # | Task | Summary | Needs its own Use Case/User Story? | Reference | +| --- | --- | --- | --- | --- | +| 1 | | | No | | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/OC.md b/.agents/skills/artifact/templates/OC.md new file mode 100644 index 0000000..87a4220 --- /dev/null +++ b/.agents/skills/artifact/templates/OC.md @@ -0,0 +1,41 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Contract: + +| Item | Value | +| --- | --- | +| Operation | `operationName(param: Type): ReturnType` | +| Traces to | in [SSD-] | +| Domain Model concepts | ([DM-]) | + +**Preconditions** + +- + +**Postconditions** + +- A instance was created. +- was associated with . +- . was set to . + +**Exceptions** + +| Condition (failing precondition) | Outcome | +| --- | --- | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/PP.md b/.agents/skills/artifact/templates/PP.md new file mode 100644 index 0000000..1e3487b --- /dev/null +++ b/.agents/skills/artifact/templates/PP.md @@ -0,0 +1,63 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Planning Assumptions + +- Week 1 starts ; the plan ends by , per the Business Case constraint. +- Phase length: . + +## Gateway Schedule + +| Gateway | Document | Window | Decision date | Owner | Stories | Main deliverable | Milestone | +| --- | --- | --- | --- | --- | --- | --- | --- | +| | [MIL-] | | | | | | | + +```plantuml +@startgantt +Project starts +[Phase 1] starts and ends +[Phase 1 Go/No-Go] happens +@endgantt +``` + +## Scope Coverage + +| Business Case scope item | Gateway | +| --- | --- | + +## Dependencies + +``` + → → ... +``` + +## Plan Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | + +## Open Issues + +- + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/QC.md b/.agents/skills/artifact/templates/QC.md new file mode 100644 index 0000000..4e37af3 --- /dev/null +++ b/.agents/skills/artifact/templates/QC.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +Allowed Status values: `Proposed`, `Accepted`, `Rejected`, `Deprecated`. The latest reviewed row is `Accepted`; the row before it is `Deprecated`. + +--- + +## Purpose + + + +## Quality Criteria Checklist + +Level: **Mandatory** criteria are the baseline every instance must meet; **Optional** criteria are advanced and may be deferred. + +| # | Criterion | Level | ISO/IEC 25010 Characteristic(s) | Notes | +| --- | --- | --- | --- | --- | +| 1 | | Mandatory | | | + +## Common Defects + +- +- + +## Traceability Rule + +- Backward: ([QC--]) +- Forward: ([QC--]) + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/RC.md b/.agents/skills/artifact/templates/RC.md new file mode 100644 index 0000000..a4dcdb7 --- /dev/null +++ b/.agents/skills/artifact/templates/RC.md @@ -0,0 +1,51 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [] +- Checklist used: [] (`QC--`, e.g. `QC-BC-001`) +- Scope: , reason, earlier record []> +- Language and domain: / (the artifact's Metadata rows), or `n/a` for a technical type +- Language reviewer: , or `none` when the reviewer reads the language and knows the domain + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | | Pass/Fail/N-A | | + +## Language and Domain Results + +Only for an artifact of a type written in the PO language: the rows of +`QC-LANG-001`, in order. Delete this section for a technical type. + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | | Pass/Fail/N-A | | + +## Overall Verdict + + — + +## Action Items + +| Action | Owner | Due | +| --- | --- | --- | +| | | | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/SA.md b/.agents/skills/artifact/templates/SA.md new file mode 100644 index 0000000..2b18ecc --- /dev/null +++ b/.agents/skills/artifact/templates/SA.md @@ -0,0 +1,56 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Stakeholder Summary Table + +| ID | Name | Role/Title | Organization | Power Level | Interest Level | Quadrant | Primary Concern (Business Language) | +| --- | --- | --- | --- | --- | --- | --- | --- | +| S01 | | | | HIGH / MEDIUM / LOW | HIGH / MEDIUM / LOW | Manage Closely / Keep Satisfied / Keep Informed / Monitor | | + +## Power/Interest Classification Rationale + +## Primary Concerns and FURPS+ Mapping + +| ID | Concern | FURPS+ attribute | +| --- | --- | --- | + +## Communication Requirements + +| ID | Channel | Frequency | Deliverable | Phase / Milestone | +| --- | --- | --- | --- | --- | + +## Conflicting Interests and Mitigations + +| Conflict | Stakeholders | Mitigation | +| --- | --- | --- | + +## Traceability Analysis + +### Business Goal Alignment + +| Stakeholder | Concern | Business Case objective | +| --- | --- | --- | + +## Sign-Off + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/SD.md b/.agents/skills/artifact/templates/SD.md new file mode 100644 index 0000000..70a02af --- /dev/null +++ b/.agents/skills/artifact/templates/SD.md @@ -0,0 +1,47 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Sequence: + +**Realizes:** `operationName` in [OC-] + +### Diagram + +```plantuml +@startuml +participant ":Controller" as C +participant ":Collaborator" as X +C -> X : message(args) +activate X +X --> C : result +deactivate X +@enduml +``` + +### Pattern Annotations + +| Pattern (GRASP / GoF) | Applied to | Rationale | +| --- | --- | --- | + +### Postcondition Coverage + +| Postcondition (from contract) | Satisfied by message | +| --- | --- | + +### Responsibility Check + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/SSD.md b/.agents/skills/artifact/templates/SSD.md new file mode 100644 index 0000000..7856d4c --- /dev/null +++ b/.agents/skills/artifact/templates/SSD.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Source Use Case + + ([UC-]) — scenario:
+ +## Diagram + +```plantuml +@startuml +actor Actor as A +participant ":System" as S +A -> S : verbPhrase(param) +S --> A : result +@enduml +``` + +## System Operations + +| Step | Message | Parameters | Return | Use case step | +| --- | --- | --- | --- | --- | + +## Lifecycle Notes + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/TM.md b/.agents/skills/artifact/templates/TM.md new file mode 100644 index 0000000..6e08eba --- /dev/null +++ b/.agents/skills/artifact/templates/TM.md @@ -0,0 +1,30 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + +Tracks backward/forward links between artifact instances so that the Business Case's +cross-artifact traceability success criterion is measurable. A row is added or +updated whenever an artifact instance is created or reviewed. + +## Traceability Table + +| Artifact Instance | Type | Language | Domain | Upstream (Backward Link) | Downstream (Forward Link) | Last Reviewed (RC-ID) | +| --- | --- | --- | --- | --- | --- | --- | +| [] | | | | [] | [] | [] | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/TRN.md b/.agents/skills/artifact/templates/TRN.md new file mode 100644 index 0000000..8dd6170 --- /dev/null +++ b/.agents/skills/artifact/templates/TRN.md @@ -0,0 +1,60 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Audience and Prerequisites + + + +## Learning Objectives + +By the end, a participant can: + +- + +## Agenda + +| Module | Topic | Minutes | +| --- | --- | --- | +| 1 | | | + +## Module Notes + +### Module 1: + + + +## Exercises + +### Exercise 1: + + + +## Assessment + + + +## Session Record + +What is recorded after each session, in the project (never in the framework): +date, facilitator, attendees, result against the assessment, feedback and +follow-ups. + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/TRR.md b/.agents/skills/artifact/templates/TRR.md new file mode 100644 index 0000000..f73f357 --- /dev/null +++ b/.agents/skills/artifact/templates/TRR.md @@ -0,0 +1,43 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Session + +| Item | Value | +| --- | --- | +| Training material | [] | +| Date | | +| Facilitator | | +| Format and place | | + +## Attendees and Assessment + +| Stakeholder ID (SA) | Attended | Assessment (Pass / Not yet) | Languages and domains | Notes | +| --- | --- | --- | --- | --- | +| | | | | | + +## Feedback + + + +## Follow-ups + +| Action | Owner | Due | +| --- | --- | --- | +| | | | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/UC.md b/.agents/skills/artifact/templates/UC.md new file mode 100644 index 0000000..b8b3388 --- /dev/null +++ b/.agents/skills/artifact/templates/UC.md @@ -0,0 +1,57 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +**Format:** Brief | Casual | Fully Dressed (delete the unused sections below) + +## Brief + + + +## Casual + + + +## Fully Dressed + +- **Scope:** +- **Level:** summary | user-goal | subfunction +- **Primary Actor:** ]> +- **Stakeholders and Interests:** + - — +- **Preconditions:** +- **Postconditions (success guarantee):** + +### Main Success Scenario + +1. +2. + +### Extensions (Alternative / Exception Flows) + +- 2a. : + 1. (`<>` / `<>` ) + +### Special Requirements / Business Rules + +| Step | Rule | +| --- | --- | + +### Open Issues + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/UCD.md b/.agents/skills/artifact/templates/UCD.md new file mode 100644 index 0000000..2fd7867 --- /dev/null +++ b/.agents/skills/artifact/templates/UCD.md @@ -0,0 +1,52 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + + + +## Diagram + +```plantuml +@startuml +left to right direction +actor "Name" as A1 +rectangle "System Name" { + usecase "Verb-phrase goal" as UC1 +} +A1 --> UC1 +@enduml +``` + +## Actor Table + +| Actor | Stereotype | Stakeholder ID (SA) | Goals (use cases) | +| --- | --- | --- | --- | + +## Use Case Table + +| Use Case | Actor(s) | Goal | +| --- | --- | --- | + +## Relationships + +| From | Relationship (`<>` / `<>`) | To | Justification | +| --- | --- | --- | --- | + +--- + +@LINKS@ diff --git a/.agents/skills/artifact/templates/US.md b/.agents/skills/artifact/templates/US.md new file mode 100644 index 0000000..f0fa723 --- /dev/null +++ b/.agents/skills/artifact/templates/US.md @@ -0,0 +1,38 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Story List + +### @ID@.01 — + +**As a** ]>, **I want** , **so that** . + +**Acceptance Criteria** + +- Given , when , then . + +| Traces to | Size | INVEST exceptions | +| --- | --- | --- | +| [UC-] or [MIL-] | fits one iteration | none | + +## INVEST Check + +--- + +@LINKS@ diff --git a/.agents/skills/coding-conventions/SKILL.md b/.agents/skills/coding-conventions/SKILL.md new file mode 100644 index 0000000..d207710 --- /dev/null +++ b/.agents/skills/coding-conventions/SKILL.md @@ -0,0 +1,98 @@ +--- +name: coding-conventions +description: Programming conventions for writing or reviewing source code — naming, layout, formatting and language idioms — for Python, C, C++, C# and Shell (bash). Use when writing, editing, reviewing or refactoring code in one of those languages, choosing names, setting up a formatter/linter config, or adding conventions for another language. Holds the rules shared by every language and points to a per-language sub-skill. +--- + +# Coding Conventions + +One skill for all languages. This file holds what is true in every language; +everything specific to one language is in a sub-skill that is read only when +that language is in hand: + +| Language | Sub-skill | QC checklist | +| --- | --- | --- | +| Python | `references/python.md` | `framework/qc/qc-programming-python.md` (`QC-PY-001`) | +| C | `references/c.md` | `framework/qc/qc-programming-c.md` (`QC-CL-001`) | +| C++ | `references/cpp.md` | `framework/qc/qc-programming-cpp.md` (`QC-CPP-001`) | +| C# | `references/csharp.md` | `framework/qc/qc-programming-csharp.md` (`QC-CS-001`) | +| Shell (bash) | `references/shell.md` | `framework/qc/qc-programming-shell.md` (`QC-SH-001`) | + +Read the sub-skill for the language you are working in, then write or review +the code. To review, use the language's QC checklist and record the result as +an `RC-*` (see the `artifact` skill). + +## Precondition: a planned task + +Before writing or editing code under `src/` or `tests/`, name the task row +(`MIL-NNN`, task N) or the synced issue, and the use case or design artifact +it implements, or say it is a plain technical task. If you cannot, refuse and +use the `project-planning` skill instead (rule: `framework/process/plan-first-gate.md`). +The task's milestone must be `Accepted` with a `Go` review record; if it is +still `Proposed`, or its latest review is conditional or No-Go, stop and say so. +Reviewing code needs no task. Before the pull request, code is reviewed against +the checklist of its language (`qc-programming-*`) and the result is recorded +as an `RC-*`. + +## Rules for every language + +1. **The existing code wins.** In a file or project that already has a + convention, follow it, even if the sub-skill says otherwise. Do not mix + styles within a file; do not reformat code you are not changing. +2. **Naming follows the language, not your habits.** Casing differs per + language (table below). Never carry one language's casing into another. +3. **A name says what, not how.** Name by purpose in the domain's language + (IT Professional English, as the registry's `Languages` section says), not by + type or implementation (`customer_list`, not `arr2`). +4. **Length follows scope.** Short names (`i`, `n`) only for tiny scopes; + wider scope, longer name. No abbreviations except ones the whole domain + uses (`id`, `url`, `http`). +5. **Booleans read as a question:** `is_valid`, `has_items`, `can_retry` + (cased per language). No negated names (`is_not_ready`). +6. **Functions are verbs, types are nouns.** A function that returns a value + without side effects may be a noun (`total`, `Total`) where the language + community does so. +7. **Formatting is done by the formatter,** not by hand and not in review + comments. Each sub-skill names the formatter and linter; commit its + configuration file with the code. +8. **Comments say why,** never what the code already says. Public APIs get + the language's documentation-comment form. +9. **No dead or commented-out code, no unexplained magic numbers.** Name the + constant. +10. **Errors are handled or propagated, never swallowed.** Each sub-skill says + how its language does this. + +## Casing at a glance + +| Element | Python | C | C++ | C# | +| --- | --- | --- | --- | --- | +| Type / class | `PascalCase` | `snake_case_t` | `PascalCase` | `PascalCase` | +| Function / method | `snake_case` | `snake_case` | `snake_case` | `PascalCase` | +| Variable / parameter | `snake_case` | `snake_case` | `snake_case` | `camelCase` | +| Constant | `UPPER_SNAKE` | `UPPER_SNAKE` | `kPascalCase` | `PascalCase` | +| Private member | `_leading` | file-scope `static` | `trailing_` | `_camelCase` | +| Namespace / module | `snake_case` module | `mod_` prefix | `snake_case` | `PascalCase` | +| File | `snake_case.py` | `snake_case.c/.h` | `snake_case.cpp/.h` | `PascalCase.cs` | + +The table is a summary; the sub-skill is authoritative. Shell (bash) is not in +the table: functions and variables are `snake_case`, constants and environment +variables `UPPER_SNAKE`, files `kebab-case.sh`. + +## Governance boundary + +Conventions are **defined and governed** here but **not enforced** by this +framework: writing a linter or CI job that enforces them is outside the +framework's scope. The formatter and linter names in each sub-skill are the +recommended tools, not a pipeline. A new or changed convention follows the +process in the project's Coding Standards Governance document and is recorded +in `framework/CHANGELOG.md`. No project data belongs in this skill. + +## Adding a language + +1. Add `references/.md` with these sections: Standard base, Naming, + Formatting, Language rules, Errors, Tests, Tooling. +2. Add `framework/qc/qc-.md` (`QC--001`) using the `QC` type + of the `artifact` skill, tagging every criterion with an ISO/IEC 25010:2023 + characteristic and a Level. +3. Add the row to the table above, the casing table, and a row for the + language in `framework/registry/artifact-catalog.md`; note it in + `framework/CHANGELOG.md`; run `bash framework/scripts/install-skills.sh`. diff --git a/.agents/skills/coding-conventions/references/c.md b/.agents/skills/coding-conventions/references/c.md new file mode 100644 index 0000000..14375ca --- /dev/null +++ b/.agents/skills/coding-conventions/references/c.md @@ -0,0 +1,71 @@ +# C conventions + +C has no single official style guide. This sub-skill fixes one consistent +style built on common practice; MISRA C and SEI CERT C are the references for +safety-critical or security-sensitive code. + +## Standard base + +ISO C11 or C17 as set by the project's compiler flag (`-std=c11`). Compile +with warnings on (`-Wall -Wextra -Wpedantic`); treat warnings as errors in +release builds. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Function, variable, parameter | `snake_case` | `parse_header`, `byte_count` | +| Public symbol | module prefix plus `snake_case` (C has no namespaces) | `stay_reader_open()` | +| File-local function / variable | `static`, no prefix needed | `static int next_token(...)` | +| Type (`struct`, `enum`, `typedef`) | `snake_case_t`, module prefix for public types | `stay_reader_t` | +| Enum constant, macro, constant | `UPPER_SNAKE` with the module prefix | `STAY_READER_OK` | +| Header / source file | `snake_case.h` / `snake_case.c`, same base name | `stay_reader.h` | +| Header guard | `MODULE_FILE_H` (or `#pragma once` if the project allows) | `STAY_READER_H` | + +- Do not use reserved identifiers: a leading underscore followed by an + uppercase letter, any double underscore, or a leading underscore at file + scope. +- POSIX reserves the `_t` suffix; keep the module prefix on public types so + they cannot collide. +- Macros are a last resort; prefer `static inline` functions and `enum` or + `const` values. + +## Formatting + +- One formatter configuration for the project (`clang-format`); 4 spaces (or + the project's setting), no tabs mixed in. +- Braces on every `if`, `else`, `for`, `while`, even for one statement. +- One declaration per line; declare variables at first use, initialised. +- Headers: include what you use, only what you use; public headers are + self-contained; add `extern "C"` guards when C++ code consumes them. + +## Language rules + +- Check every return value that can fail; check every allocation. +- Every `malloc`/`open`/`lock` has one clear owner and one matching release; + release on every exit path (single exit or `goto cleanup`). +- Use `size_t` for sizes and indices, fixed-width types (``) for + data layout, `const` wherever data is not modified, `restrict` only with + care. +- Bounds are explicit: pass a length with every buffer; use `snprintf`, + never `sprintf`, `strcpy` or `gets`. +- No undefined behaviour: no signed overflow, no out-of-range shifts, no use + after free, no uninitialised reads. +- Avoid global mutable state; if unavoidable, `static` and documented. + +## Errors + +Return a status code (an `enum`) or `-1`/`NULL` plus an error out-parameter; +document which in the header. Never ignore a failing call. Read `errno` +immediately after the failing call. + +## Tests + +A unit-test framework (for example Unity or CMocka); run under sanitizers +(`-fsanitize=address,undefined`) in at least one build. + +## Tooling + +`clang-format`, `clang-tidy` or `cppcheck`, compiler warnings, sanitizers. + +Review with `QC-CL-001`. diff --git a/.agents/skills/coding-conventions/references/cpp.md b/.agents/skills/coding-conventions/references/cpp.md new file mode 100644 index 0000000..8e164ab --- /dev/null +++ b/.agents/skills/coding-conventions/references/cpp.md @@ -0,0 +1,70 @@ +# C++ conventions + +## Standard base + +ISO C++17 or later as set by the project (`-std=c++20`); the C++ Core +Guidelines are the rule source. C++ has no official naming style, so the +style below is used unless the project already has another (rule 1 of the +overall skill). + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Class, struct, enum, concept, type alias | `PascalCase` | `StayReader`, `Stay` | +| Function, method, variable, parameter | `snake_case` | `read_stays()`, `byte_count` | +| Data member (private) | `snake_case` with trailing underscore | `buffer_` | +| Struct public data member | `snake_case`, no underscore | `check_in` | +| Constant (`constexpr`, namespace-scope `const`) | `kPascalCase` | `kMaxRetries` | +| Enum class value | `PascalCase` | `Status::NotFound` | +| Namespace | short `snake_case`; no `using namespace` in headers | `billing` | +| Template parameter | `PascalCase` | `typename ItemT` | +| Macro | `UPPER_SNAKE` with project prefix; avoid macros | `BILLING_ASSERT` | +| Header / source file | `snake_case.h` / `snake_case.cpp`, same base name | `stay_reader.h` | +| Header guard | `#pragma once` (or `PROJECT_PATH_FILE_H`) | | + +## Formatting + +- `clang-format` with one checked-in config; 4 spaces (or the project's + setting); braces on every control-flow body. +- Include order: matching header, project headers, third party, standard + library; each group sorted. +- One declaration per line; declare at first use, initialise with `{}`. + +## Language rules + +- **Ownership:** RAII everywhere. No owning raw pointers, no naked + `new`/`delete`; use `std::unique_ptr` by default, `std::shared_ptr` only for + real shared ownership, created with `std::make_unique`/`make_shared`. +- Follow the rule of zero; if you define one of destructor, copy or move, + define or delete all five. +- Pass by `const&` (large, read-only) or by value (small or sink); use + `std::span` and `std::string_view` for non-owning views, `std::optional` for + "maybe", `std::variant` for alternatives. +- `const` and `constexpr` by default; mark single-argument constructors + `explicit`; mark `override`/`final`; `[[nodiscard]]` on results that must + be used. +- Prefer algorithms and range-for over hand-written loops; `enum class` over + plain `enum`; `nullptr` over `NULL` or `0`; `using` over `typedef`. +- No C-style casts; use `static_cast` and friends. No mutable global state. +- Headers are self-contained and contain declarations, templates and + `inline` definitions only. + +## Errors + +Use exceptions for exceptional failures, or `std::expected` and error codes +where the project forbids exceptions; one choice per project. Destructors +never throw. Catch by `const&`; never `catch (...)` without rethrowing or +logging. + +## Tests + +GoogleTest, Catch2 or doctest; run under sanitizers (`address`, `undefined`) +in at least one build. + +## Tooling + +`clang-format`, `clang-tidy` with the Core Guidelines checks, compiler +warnings (`-Wall -Wextra -Wpedantic`), sanitizers. + +Review with `QC-CPP-001`. diff --git a/.agents/skills/coding-conventions/references/csharp.md b/.agents/skills/coding-conventions/references/csharp.md new file mode 100644 index 0000000..88abb3b --- /dev/null +++ b/.agents/skills/coding-conventions/references/csharp.md @@ -0,0 +1,72 @@ +# C# conventions + +## Standard base + +Microsoft's C# coding conventions and .NET Framework Design Guidelines, with +the analyzers that ship in the SDK. Target the language version of the +project's `LangVersion` / target framework. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Namespace | `PascalCase`, matches folder path | `Billing.Stays` | +| Class, struct, record, enum, delegate | `PascalCase` (nouns) | `StayReader` | +| Interface | `I` + `PascalCase` | `IStayReader` | +| Method, property, event, public field | `PascalCase` | `ReadStays()`, `CheckIn` | +| Constant, `static readonly` | `PascalCase` | `MaxRetries` | +| Enum value | `PascalCase`; `[Flags]` enums are plural | `Status.NotFound` | +| Parameter, local variable | `camelCase` | `byteCount` | +| Private / internal field | `_camelCase` | `_buffer` | +| Generic type parameter | `T` or `T` + `PascalCase` | `T`, `TKey` | +| Async method | ends in `Async` | `ReadStaysAsync()` | +| Exception, attribute | end in `Exception` / `Attribute` | `InvalidDateException` | +| Boolean | `Is`, `Has`, `Can` prefix | `IsActive` | +| File | the type's name, one top-level type per file | `StayReader.cs` | + +- Two-letter acronyms are upper case (`IO`); longer ones are `PascalCase` + (`Xml`, `Http`). + +## Formatting + +- `.editorconfig` checked in; `dotnet format` applies it. 4 spaces, Allman + braces, braces on every control-flow body. +- File-scoped namespaces (`namespace X;`); `using` directives outside the + namespace, `System` first. +- `var` when the type is obvious from the right-hand side, explicit type + otherwise. + +## Language rules + +- Enable nullable reference types (`enable`) and treat + nullable warnings as errors; do not suppress with `!` without a comment. +- `IDisposable` owners use `using`; implement the dispose pattern only when + needed. +- `async`/`await` all the way; no `.Result` or `.Wait()`; no `async void` + except event handlers; pass `CancellationToken` through public async APIs. +- Prefer properties over public fields, `readonly` and `init` for + immutability, `record` for value-like data, pattern matching and switch + expressions over long `if` chains. +- LINQ for queries, loops for side effects; do not enumerate a sequence twice. +- String interpolation over concatenation; `StringBuilder` in loops; + `DateTimeOffset` over `DateTime` for points in time; `decimal` for money. +- XML documentation comments (`///`) on public types and members. + +## Errors + +Throw specific exceptions (`ArgumentNullException`, custom types); validate +arguments at the public boundary (`ArgumentNullException.ThrowIfNull`). Catch +the narrowest type; `throw;` (not `throw ex;`) to rethrow. Never an empty +`catch`. + +## Tests + +xUnit, NUnit or MSTest; names like `Method_Condition_Expected`; one behaviour +per test; no dependence on order, time or the network. + +## Tooling + +`dotnet format`, the .NET analyzers (`AnalysisLevel`, `EnforceCodeStyleInBuild`) +and optionally StyleCop.Analyzers, configured in `.editorconfig`. + +Review with `QC-CS-001`. diff --git a/.agents/skills/coding-conventions/references/python.md b/.agents/skills/coding-conventions/references/python.md new file mode 100644 index 0000000..e262961 --- /dev/null +++ b/.agents/skills/coding-conventions/references/python.md @@ -0,0 +1,66 @@ +# Python conventions + +## Standard base + +PEP 8 (style), PEP 257 (docstrings), PEP 484 and later (type hints). Target +the Python version declared in `pyproject.toml`. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Package / module | short `snake_case` | `billing`, `stay_reader.py` | +| Class, exception | `PascalCase`; exceptions end in `Error` | `StayReader`, `InvalidDateError` | +| Function, method, variable, parameter | `snake_case` | `total_price`, `read_stays()` | +| Constant (module level) | `UPPER_SNAKE` | `MAX_RETRIES` | +| Internal (not public API) | one leading underscore | `_parse_row` | +| Type variable | short `PascalCase`; `_co` / `_contra` for variance | `T`, `KeyT`, `ItemT_co` | +| Boolean | `is_`, `has_`, `can_` prefix | `is_active` | +| Test file / function | `test_.py` / `test__` | `test_total_when_empty` | + +- Avoid name mangling (`__name`) unless you need it to prevent a subclass clash. +- Never use `l`, `O` or `I` as single-letter names. +- Do not shadow builtins (`list`, `id`, `type`); add a trailing underscore + (`type_`) only as a last resort. + +## Formatting + +- 4 spaces, no tabs. Maximum line length set once in the formatter config + (88 with `ruff format`; 79 if the project follows PEP 8 strictly). +- Imports at the top, grouped standard library / third party / local, one + blank line between groups, no wildcard imports. +- Double quotes for strings unless the formatter says otherwise. +- Trailing commas in multi-line literals and calls. + +## Language rules + +- Annotate every function signature (parameters and return, `-> None` too). + Modern syntax: `X | None`, `list[str]`. Avoid `Any` without a comment. +- `pathlib` over `os.path`; f-strings over `%` or `.format`; `enum` over + magic strings; `dataclass` (frozen where possible) over ad-hoc dicts. +- No mutable default arguments; no bare `except:`; no `print` for logging + (use `logging`). +- Use context managers (`with`) for files, locks and connections. +- Prefer comprehensions over `map`/`filter` with lambdas; keep them simple. +- Docstrings (PEP 257) on public modules, classes and functions; say what, + not how. + +## Errors + +Raise specific exceptions; catch the narrowest type; re-raise with +`raise ... from err` to keep the cause. Do not use exceptions for normal +control flow. + +## Tests + +`pytest`; one behaviour per test; `tmp_path` for files; fakes over mocks where +a simple fake is possible; tests do not depend on order or on the network. + +## Tooling + +`ruff` (lint and format) and `mypy --strict` (types), configured in +`pyproject.toml`. Architecture rules (layers, ports, dataframes) are in +`.agents/rules/python.md` and the `python-developer` agent; this sub-skill +covers naming and style only. + +Review with `QC-PY-001`. diff --git a/.agents/skills/coding-conventions/references/shell.md b/.agents/skills/coding-conventions/references/shell.md new file mode 100644 index 0000000..aa8a728 --- /dev/null +++ b/.agents/skills/coding-conventions/references/shell.md @@ -0,0 +1,62 @@ +# Shell conventions (bash) + +## Standard base + +Bash 4 or later, POSIX `test` semantics through `[[ ]]`. Start every script +with `#!/usr/bin/env bash` and `set -euo pipefail`. State the bash version and +the external tools it needs in the header comment. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Script file | `kebab-case.sh`, executable | `check-plan.sh`, `new-artifact.sh` | +| Function | `snake_case`, a verb | `read_registry`, `die` | +| Local variable, parameter | `snake_case`, declared with `local` | `msgfile`, `changed` | +| Constant, environment variable | `UPPER_SNAKE` | `PLAN_GATE`, `PROJECT_ROOT` | +| Boolean | `is_` / `has_` prefix, value `0` or `1` | `is_enabled=1` | + +- Name a script and its functions by what they do, not how. +- Do not shadow a command with a function of the same name. + +## Formatting + +- 2 spaces, no tabs. One command per line; `then` and `do` on the same line as + `if` and `for`. Keep lines near 80 characters; break long pipelines at `|`. +- Format with `shfmt -i 2 -ci`; lint with `shellcheck`. Commit the project's + `.editorconfig` or `.shellcheckrc` with the code. + +## Language rules + +- **Quote every expansion** (`"$var"`, `"${arr[@]}"`); use arrays, not + space-separated strings, for lists. Use `[[ ]]`, not `[ ]`, and `$(...)`, + not backticks. +- Declare function variables `local`; no globals except constants. +- Parse options with `case` and `shift`, check required arguments with + `"${1:?usage: ...}"`, and print a usage line on bad input. +- Read input with `read -r`; iterate files with globs or `find -print0`, never + by parsing `ls`. +- Use `mktemp` for temporary files and remove them with `trap ... EXIT`; never + a fixed `/tmp` name. + +## Errors and exit codes + +- Errors go to standard error, start with `error:`, say what is wrong and what + to do, and end the script with a non-zero exit code (`die` helper). +- `0` is success, `1` a failed check or bad input, `2` a usage error; document + any other code in the header. +- Never swallow a failure with `|| true` without a comment that says why. + +## Safety + +- A script that changes state outside its own directory defaults to a dry run + or asks for an explicit flag (`--apply`, `--force`); say so in the header. +- Never echo a token or password, put one on a command line, or commit one; + read secrets from the environment or a gitignored file. +- Do not `eval` input; do not build a command from unvalidated text. + +## Tools + +Formatter `shfmt`, linter `shellcheck`, syntax check `bash -n`. These are the +recommended tools, not a pipeline: enforcing them in CI is outside this +framework's scope. diff --git a/.agents/skills/project-planning/SKILL.md b/.agents/skills/project-planning/SKILL.md new file mode 100644 index 0000000..561235c --- /dev/null +++ b/.agents/skills/project-planning/SKILL.md @@ -0,0 +1,283 @@ +--- +name: project-planning +description: Plan a project (or a new phase of one) in phases with tasks, and sync those phases and tasks to Gitea or GitHub as Milestones and Issues. Use when starting a new project, adding or updating a phase/gateway, breaking a phase down into tasks, deciding whether a task needs its own use case or user story, or asked to create/update project milestones or issues on the git host. +--- + +# Project Planning + +Plans a project as phases (gateways), each phase as a set of tasks, and +keeps those phases and tasks in sync with the git host's own project +management (Milestones and Issues). It builds on the `artifact` skill's +`PP` (Project Plan) and `MIL` (Milestone/Gateway) types. + +## Domain language first + +Before planning, find the Product Owner's (PO's) domain language. Take it from +the first of these that states it: + +1. the user's prompt; +2. a file the user included or that the project already has (the `Languages` + section of `docs/artifact-registry.md`). + +If none states it, **ask the user for it** and stop planning until it is +answered; never assume one. Record the answer in the registry's `Languages` +section. The artifact types the registry marks "Written in the PO language" +are written in it, once, with no translated copy. + +## Start here + +Before planning or building anything, check that the baseline exists (the +`docs/artifact-registry.md` rows and files): the Business Case (`BC`), the +Stakeholder Analysis (`SA`), the Project Plan (`PP`) and at least one +milestone (`MIL`). If one is missing, create it first, in that order, with +`new-artifact.sh` (it refuses when a type in the catalog's `Requires` column +is missing). + +Each of these steps ends with a review: the document counts only once its +review record (`RC-*`) says `Go` and its Version History row is `Accepted` +(`process/review-checklist-process.md`). Do not start a step on a document that +is still `Proposed`; ask for the review first. + +A "build X" request is planning-first. Produce the phases, tasks and issues, +show the dry run of `sync-project.sh`, and ask for a go-ahead before any code +is written; running `--apply` is the user's decision. Only the user can waive +the plan, in chat, for that request; the waiver does not carry over. The rule +is defined in `framework/process/plan-first-gate.md`. + +## The hierarchy + +``` +Business goal → Feature/requirement → Use case/user story → Tasks +``` + +The use case/user story explains **why** a feature exists; tasks explain +**how** the team implements it. Not every task needs a use case: + +- **Needs a use case/user story** — the task is something a user, or + another system, actually does: "User resets password", "Admin exports + customer report", "Payment service processes refund". +- **Plain task, no use case** — purely technical/implementation work: + "Refactor authentication middleware", "Add database index", "Upgrade + React version", "Fix null-pointer bug", "Add unit tests", "Configure CI + pipeline", "Optimize SQL query". + +When in doubt, ask: does this row describe a goal an actor is pursuing, or +a step the team takes to build something? Goals get a use case; steps stay +a plain task. + +## Use cases get their own folder + +Each use case lives in `docs/uc-NNN/` (`UC-001` → `docs/uc-001/`), together +with the artifacts that belong to it. Nothing about a use case goes in a +shared `docs/use-cases/` folder. + +1. **Create the folder and the use case:** + `bash framework/scripts/new-artifact.sh UC --file docs/uc-001/uc.md`. +2. **Create only the artifacts this use case affects,** in this order, each in + the same folder with a fixed file name. Skip any it does not touch (no + `erd.md` if nothing is stored; no `dm.md` if it adds no concept). + + | Order | Type | File | Create when the use case… | + | --- | --- | --- | --- | + | 1 | `SSD` | `ssd.md` | has system interaction to show (almost always) | + | 2 | `DM` | `dm.md` | introduces or changes domain concepts | + | 3 | `OC` | `oc.md` | has system operations that change state | + | 4 | `SD` | `sd.md` | needs a collaboration design for an operation | + | 5 | `DCD` | `dcd.md` | adds or changes design classes | + | 6 | `ERD` | `erd.md` | adds or changes persisted data | + + ```bash + bash framework/scripts/new-artifact.sh SSD --file docs/uc-001/ssd.md + bash framework/scripts/new-artifact.sh DM --file docs/uc-001/dm.md --cite UC-001=docs/uc-001/uc.md + ``` + + `new-artifact.sh` cites only the artifacts in the same use-case folder + (and project-level ones); for `DM`, `DCD` and `ERD`, add the use case and + sibling artifacts by hand with `--cite`. Each gets its own ID (the next + version in the registry, e.g. `DM-002`) and its own `RC-*` review. +3. **Reconcile with the project models.** The use-case artifacts are a scoped + view; the project-level `docs/domain-model.md`, `docs/dcd.md` and + `docs/erd.md` are the consolidated truth. When the use case's `DM`, `DCD` + or `ERD` are done, compare each with its project-level document: + - add the new concepts, classes, attributes and entities; change the ones + the use case modified; + - keep names identical to the existing ones, and resolve any conflict + with another use case's model instead of duplicating the element; + - give each project-level document a new `## Version History` row (Change: + which use case caused it), and create it from the first use case's + document if it does not exist yet; + - if nothing needs to change, say so in the use case's task and PR + description ("project DM/DCD/ERD unchanged: "). + +## Planning a project (or a new phase) + +1. **Phases are gateways.** Each phase is a `MIL-*` document (the `artifact` + skill's `MIL` type): purpose, deliverable, Go/No-Go criteria, dependencies, + ownership, target date. Create one with + `bash framework/scripts/new-artifact.sh MIL --file docs/milestones/mil--.md`. +2. **The plan schedules the phases.** `docs/project-plan.md` (the `artifact` + skill's `PP` type) lists every phase with its window and owner, and holds + the overall timeline diagram. Create it once with + `bash framework/scripts/new-artifact.sh PP`. +3. **Break each phase into tasks.** In the phase's `## Tasks` section, add + one row per task using the hierarchy rule above. Tasks that need a use + case or user story get one created first (`UC`/`US` types; a use case + follows "Use cases get their own folder" below), then are referenced from + the Tasks row; plain tasks just describe the work. +4. **Sync to the git host.** Run the sync tool (below) to create or update + the corresponding Milestones and Issues. Do this whenever a phase or its + tasks change — the tool is idempotent (matches by title, never creates a + duplicate). + +## Syncing to Gitea/GitHub + +```bash +bash framework/scripts/sync-project.sh # prints the plan only — no network calls +bash framework/scripts/sync-project.sh --milestone MIL-002 # limit to one phase +bash framework/scripts/sync-project.sh --apply # actually create/update on the git host +``` + +- **Default is a dry run**: it parses `docs/milestones/mil-*.md` and prints + what would be created or updated. Nothing is sent anywhere. +- **`--apply`** performs the real requests. It needs: + - **Gitea** (default — detected from `origin`'s hostname): `GITEA_TOKEN` + env var, a personal access token. This is an HTTP API call (milestones, + issues) — a deploy key cannot be used, since deploy keys only + authenticate SSH git transport (clone/fetch/push), not the REST API. + Scope the token to issues only if your Gitea version supports scoped + tokens, rather than a full-account one. + - **GitHub** (detected when `origin` is on github.com, or pass + `--host github`): the `gh` CLI, already logged in (`gh auth login`). +- **`--with-project`** additionally tries to create/attach a Kanban Project + board. This is best-effort: some Gitea versions (confirmed on 1.27.3) show + Projects/Kanban only in the web UI and expose **no REST API for it at + all**, so this always fails there — check your server's own + `https:///swagger.v1.json` for any `project` path if unsure. GitHub + Projects (v2) needs `gh` with the `project` scope. Either way the script + warns and continues; Milestones and Issues are unaffected. Where there's + no API, create the board by hand at `https://///projects`. +- Read the script's header comment for the full flag list, including + `--owner`, `--repo`, `--api-base` to override auto-detection. + +`--apply` is an outward-facing, hard-to-reverse action (it creates real +Milestones/Issues on the git host) — run it yourself once you're ready, or +ask explicitly for it to be run. + +## Branching before commits + +**Never commit, push or open a PR unless the user asks.** The user reviews +the working-tree changes first; finish the edits, summarise them and stop. +Everything below (branching, closing keywords, resolving commit links, the +PR) describes how to do those steps once the user has asked for them; it is +not permission to start them. + +Never commit directly on `main`. This is enforced locally by a pre-commit +hook (`framework/githooks/pre-commit`) once +`bash framework/scripts/install-git-hooks.sh` has been run in this clone — +run it once per clone; it points `core.hooksPath` at the versioned +`framework/githooks/` instead of the per-clone, untracked `.git/hooks/`. +The hook refuses the commit with a pointer back to this section; a rare, +deliberate exception can bypass it with `ALLOW_MAIN_COMMIT=1 git commit`. + +Before the first commit of a piece of work, create and switch to a new +branch, then commit there: + +```bash +git checkout -b +# ... commits ... +git push -u origin +``` + +- Name the branch for the gateway/task it covers, kebab-case, e.g. + `g2-kpi-bmc-bpmn` or `mil-002-kpi-baseline` — short enough to read in a + PR list, specific enough to say what it's for. +- Open a PR (`gh pr create` on GitHub, or the Gitea equivalent) instead of + merging straight to `main`; this project's own history is a merged PR + per phase (e.g. "Merge pull request 'Close G1 inception baseline...'"), + so keep following that pattern. +- This applies to every commit, not just gateway/task work — if in doubt + whether the current branch is `main`, check (`git branch --show-current`) + before committing. + +## Closing tasks from commits + +When a commit finishes the work for a task that `sync-project.sh` has +already synced as an Issue, reference that Issue number in the commit +message so Gitea/GitHub auto-closes it once that commit lands on the +default branch (`main`) — via the PR merge required by "Branching before +commits" above, not by pushing the closing keyword to a feature branch. +Use `Refs #N` instead when the commit only touches the task without +finishing it — that links the commit without closing. + +- **One issue per commit:** use `Closes #5`. +- **Several issues closed by the same commit:** put one `Closes #N` per + line, not a comma-separated list (`Closes #5, #6, #7` only closed `#5` on + a confirmed live Gitea instance — the comma form is not reliably parsed): + + ``` + Closes #5 + Closes #6 + Closes #7 + ``` +- Get each issue number either from the most recent `sync-project.sh` / + `sync-project.sh --apply` output (it prints `updated issue #N` / + `created issue #N` per task) or, if that's not at hand, from the git + host's issue list for the milestone. +- Match commits to issues by the task row they implement — one task row in + a `MIL-*` document's `## Tasks` section is one Issue, so a commit that + completes that row's work closes that Issue. +- Don't guess an issue number; if it isn't known from a recent sync or a + lookup, ask rather than omit or fabricate one. +- **After pushing a multi-issue closing commit, verify** each issue's state + came back `closed` (e.g. `GET /repos///issues/` with + `GITEA_TOKEN`) rather than assuming the whole list closed; close any that + didn't with a direct `PATCH .../issues/` `{"state":"closed"}` call. + +## Pull requests close the issues they complete + +Every PR must close the Issues its work finishes, so the board matches +reality once it merges. Before opening (or updating) a PR: + +1. **List the issues the branch completes.** Go through the task rows the + branch implements (`git log main..HEAD`, plus the `## Tasks` of the + `MIL-*` it belongs to) and get each Issue number as described in "Closing + tasks from commits". +2. **Put one closing line per issue in the PR description**, one per line, + never comma-separated: + + ``` + Closes #5 + Closes #6 + ``` + + Use `Refs #N` for an issue the PR only touches. A PR that finishes no + issue says so explicitly ("No issue closed: ") instead of saying + nothing. +3. **Ask, don't guess.** If an issue number is unknown or a task is only + partly done, ask rather than omit or invent one. Leave a partly done task + open and note what remains. +4. **After the merge, verify** that each issue is `closed` (the check in + "Closing tasks from commits") and close any stragglers by hand. Also make + sure the finished task rows are reflected in the `MIL-*` document and, when + every task of a phase is closed, that the Milestone is closed too. + +5. **Resolve pending commit links before the PR** (see "Version History rule" + in the `artifact` skill): commit, run + `bash framework/scripts/resolve-pending-commits.sh `, then + commit the result as a follow-up commit. +6. **Never ask for, offer or perform the merge.** A PR needs a reviewer: ask + the user to open the PR / request a review, and stop there. Merging, + enabling auto-merge and "shall I merge?" are not yours to do. + +Closing keywords in the PR description and in commit messages both count on +Gitea and GitHub; they only take effect when the PR merges into the default +branch. + +## Files this skill touches + +- `docs/project-plan.md` — `PP`, via the `artifact` skill. +- `docs/milestones/mil-*.md` — `MIL`, via the `artifact` skill, each with a + `## Tasks` section. +- `docs/uc-NNN/*.md` (the use case and its `SSD`, `DM`, `OC`, `SD`, `DCD`, `ERD`), `docs/user-stories.md` — only for tasks that need one; plus `docs/domain-model.md`, `docs/dcd.md`, `docs/erd.md` when reconciling. +- `framework/scripts/sync-project.sh` — never edited per project; propose + changes upstream in the framework. diff --git a/.claude/skills/.framework-skills b/.claude/skills/.framework-skills new file mode 100644 index 0000000..5dad221 --- /dev/null +++ b/.claude/skills/.framework-skills @@ -0,0 +1,3 @@ +artifact +coding-conventions +project-planning diff --git a/.claude/skills/artifact/SKILL.md b/.claude/skills/artifact/SKILL.md new file mode 100644 index 0000000..283b77d --- /dev/null +++ b/.claude/skills/artifact/SKILL.md @@ -0,0 +1,131 @@ +--- +name: artifact +description: Create, edit or review any project document artifact — Business Case, Stakeholder Analysis, KPI, Business Model Canvas, BPMN, milestones/gateways, use case diagram, user stories, use cases, domain model, SSD, operation contracts, sequence diagrams, DCD, ERD, ADRs, SQA review records, traceability matrix, governance, QC checklists. Scaffolds the file with the correct ID, CrossReference and links, then gives the required sections for that type. +--- + +# Artifact + +Every artifact is a markdown file with `## Metadata` and `## Version History` +tables, a registered short-name ID, and links only at the bottom. Types and +their related types are in `framework/registry/artifact-catalog.md`; this +project's file locations and next versions are in `docs/artifact-registry.md`. + +## Creating an artifact + +1. Find the type's short name (`BC`, `SA`, `MIL`, `DM`, `ADR`, `RC`, …) in + the catalog. If the type is new, add it to the catalog and the registry. +2. Scaffold — this sets the ID, `CrossReference` (only artifacts that exist), + the bottom link block and the registry version: + + ```bash + bash framework/scripts/new-artifact.sh [--file ] [--title ""] [--cite =]... + ``` + + `--file` is required for multi-document types (`MIL`, `UC`, `ADR`, `RC`, + `QC`); single-document types default to the registry's `Primary File`. + The script refuses to overwrite an existing file. +3. Read `framework/.agents/skills/artifact/references/.md` (cite-for + hints and required sections), then fill in the file. Keep the sections in + order and replace every ``. + +## Editing an artifact + +Append a row to `## Version History` on every change (a status change such as +`Proposed` → `Accepted`, or a content change). To re-check `CrossReference`, +run `bash framework/scripts/find-crossreferences.sh `. When the first +instance of a type is created, add it to the `CrossReference` of every +existing artifact that lists that type as a candidate (with a new Version +History row). + +### Version History rule + +The table keeps only the **two latest** changes; git holds the rest. Columns: + +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-01 | Accepted | Jens Tirsvad Nielsen | S02 | Added Risks section
Fixed scope wording | [a1b2c3d] | + +- **Status** — `Proposed`, `Accepted`, `Rejected` or `Deprecated` for every + artifact type (`ADR` also has `Superseded by ADR-NNNN`); `Approved` is not + used. A new row starts `Proposed`. When the review gives Go, that row + becomes `Accepted` and the row before it becomes `Deprecated`, so at most + one row is `Accepted`: the latest reviewed one. If the reviewer refuses the + change for good (not a No-Go that returns it for rework), the row becomes + `Rejected`; the earlier `Accepted` row stays `Accepted` and nothing is + deprecated. +- **Change** — a short summary of what this row changed; several lines are + separated with `
`. +- **Commit** — the commit that made the change, as a reference-style link + defined at the bottom of the file + (`[a1b2c3d]: https://///commit/`; Gitea and + GitHub use the same `/commit/` form). A row cannot contain its own commit + hash, so a new row says `pending` until the commit exists. Leave it + `pending` and do not commit: only when the user asks for a commit, then: + 1. commit the edited documents; + 2. run `bash framework/scripts/resolve-pending-commits.sh ...`, which + replaces `pending` with the link to that commit and adds the definition; + 3. commit the result as a follow-up commit, before opening the PR. Do not + amend: an amend changes the hash, so the link would point at a commit + that is never pushed. +- When adding a third row, delete the oldest row and its now-unused commit + link definition. Never rewrite the content of the two retained rows other + than resolving `pending` and setting Status to `Accepted` / `Deprecated` + after a Go review. +- Every document uses this format, including QC checklists. A file still in the + old four-column format is converted when next edited (old row kept as + `Initial version`, plus a new row for the conversion). + +## Reviewing an artifact + +1. Take the QC checklist named in the catalog (`framework/qc/qc-*.md`). +2. Create the review record: `new-artifact.sh RC …`, following + `references/RC.md`. +3. Add or update the instance's row in the Traceability Matrix. + +## Rules + +- **Links:** all links are reference-style, defined once at the bottom of the + file after a final `---`, labelled by the target's ID (`[SA-001]`), never + inline. No links → omit the block. +- **CrossReference:** cite only artifacts that exist now. Empty if none. +- **People:** use exact stakeholder IDs from the project's Stakeholder + Analysis (`S01`) for owners, reviewers and RACI. Never invent role names. +- **Diagrams** are PlantUML blocks; check them with + `bash framework/scripts/render-diagrams.sh --server ` (or set + `PLANTUML_URL`) before review. +- **QC checklists** are framework files: every criterion is tagged with an + ISO/IEC 25010:2023 characteristic, and they never mention real instances. +- **IDs:** `-`, 3 digits (`BC-001`); `ADR` uses 4 digits; + `RC` is sequential across all types; QC is `QC--`. +- **Language:** the PO language and the register of each artifact type are in + the `Languages` section of `docs/artifact-registry.md` (registers: + `IT Executive English` for high-level, `IT Professional English` for + technical). +- **Language and Domain rows:** every artifact of a type written in the PO + language has `Language` and `Domain` rows in its Metadata table, so a reviewer + sees at once how to read it. `Language` is a BCP 47 code (`da`, `en`); + `Domain` is a value from the domain list in the registry's `Languages` + section (for example `it`, `medical`, `construction`), because the same + language can carry a different professional vocabulary. `new-artifact.sh` + fills both from the registry's `PO language` and `PO domain` settings and + leaves a placeholder (with a warning) when a setting is missing. Technical + types (OC, SD, DCD, ERD, ADR, TM, RC, QC, source code) have no such rows: + they are always professional IT English. To list every document's language + and domain, see `check-languages.sh --list`. +- **One file per artifact:** each type the registry marks "Written in the PO + language" exists once, in that language, under its normal name + (`business-case.md`). There is no translated twin (`business-case.da.md`) + and no authoritative English source; two files drift apart. Use the PO terms + from the dictionary (`DICT`). Types marked "No" (OC, SD, DCD, ERD, ADR, TM, + RC, QC, source code) stay in professional IT English. +- **Structural vocabulary stays English:** Metadata keys, section headings, + IDs and statuses are the same in every language, because the scripts read + them (`find-crossreferences.sh`, `new-artifact.sh`, + `resolve-pending-commits.sh`, `sync-project.sh`, `check-plan.sh`). Only the + content (prose and table cells) is in the PO language. +- **Changing an artifact's language** is a material change: add a Version + History row ("language en to da") and review it again. Git history keeps the + earlier language. +- **Dictionary:** the Domain Model uses the PO term; the Operation Contract, + Sequence Diagram, Design Class Diagram and ERD use the IT term. Every pair is + recorded in `docs/dictionary.md` (`DICT`). diff --git a/.claude/skills/artifact/references/ADR.md b/.claude/skills/artifact/references/ADR.md new file mode 100644 index 0000000..eb9e4c3 --- /dev/null +++ b/.claude/skills/artifact/references/ADR.md @@ -0,0 +1,25 @@ +# Architecture Decision Record (ADR) + +Files: `docs/adr/adr-NNNN-kebab-case-title.md`. `NNNN` is 4-digit and +sequential; the ID is `ADR-NNNN` and must match the filename number (an +intentional exception to the usual 3-digit IDs). Use +`new-artifact.sh ADR --file docs/adr/adr-NNNN-title.md`. + +Cite in `CrossReference` the artifacts this decision is about (pass each +with `--cite =`); ADR has no fixed candidate list. + +## Required sections (after Metadata / Version History) + +- **Context** — the problem, the options evaluated and the forces + (cost, risk, constraints). +- **Decision** — the outcome in one or two sentences, no hedging. +- **Consequences** — two bold labels, **Positive:** and **Negative:**, each + with a bullet list. +- **Affected Artifacts** — other artifacts impacted, as `[ID]` links, or a + single `-` if none. + +Allowed statuses: `Proposed`, `Accepted`, `Rejected`, `Deprecated`, +`Superseded by ADR-NNNN`; no others (an ADR is never `Approved`). Status +changes (`Proposed` → `Accepted` → `Deprecated` / `Superseded by ADR-NNNN`, or +`Proposed` → `Rejected`) are new rows in `## Version History` (only the two +latest are kept; earlier ones stay in git); never rewrite a retained row. diff --git a/.claude/skills/artifact/references/BC.md b/.claude/skills/artifact/references/BC.md new file mode 100644 index 0000000..f3f07da --- /dev/null +++ b/.claude/skills/artifact/references/BC.md @@ -0,0 +1,45 @@ +# Business Case (BC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SA** — Stakeholders section — cite S-IDs instead of re-describing roles +- **BMC** — Cost–Benefit Assessment must agree with its cost/revenue blocks +- **BPMN** — Forward: the process that realizes the objectives +- **KPI** — Success Criteria — each criterion is operationalized by a KPI +- **UCD** — Forward: scope expressed as actors and goals + +## Required sections (after Metadata / Version History) + +In this order: + +1. **Executive Summary** — one paragraph framing the problem and the + proposed solution. +2. **Methodological and Standards Foundation** — states the methodology + (e.g. Larman's *Applying UML and Patterns*) and quality standards (e.g. + ISO/IEC 25002/25010/25019) the rest of the document and downstream + artifacts are built on. +3. **Problem Statement** — the recurring problems that justify the project. +4. **Business Opportunity** — what becomes possible if the problem is + solved. +5. **Objectives** — concrete, verifiable statements of what the project + will achieve. +6. **Scope** — split into `## In Scope` and `## Out of Scope` subsections. +7. **Expected Benefits** — split into `### Tangible Benefits` and + `### Intangible Benefits`. +8. **Strategic Alignment** — how the project supports organizational goals. +9. **Success Criteria** — a table with explicit, measurable targets (not + aspirations). +10. **Risks** — a table with `Risk | Impact | Mitigation` columns; every + risk must have a mitigation. +11. **Assumptions** — bullet list, kept distinct from Constraints. +12. **Constraints** — bullet list, kept distinct from Assumptions. +13. **Cost–Benefit Assessment** — a table (`Costs | Benefits`); may be + qualitative if explicitly justified. +14. **Stakeholders** — a table referencing exact stakeholder IDs from the + project's Stakeholder Analysis (e.g. `S01`, `S07`) if `SA` exists per + the CrossReference check above. **Never re-describe stakeholder roles + inline instead of citing their IDs** — this is the single most common + defect found when reviewing Business Cases (see `QC-BC-001`'s Common + Defects). +15. **Recommendation** — a single, unambiguous "proceed" or "do not + proceed" statement. diff --git a/.claude/skills/artifact/references/BMC.md b/.claude/skills/artifact/references/BMC.md new file mode 100644 index 0000000..6fe740d --- /dev/null +++ b/.claude/skills/artifact/references/BMC.md @@ -0,0 +1,21 @@ +# Business Model Canvas (BMC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — Objectives the canvas operationalizes; cost/revenue must agree with its Cost–Benefit Assessment +- **SA** — Value Propositions, Customer Segments, Channels — cite S-IDs +- **BPMN** — Key Activities — cite the process that realizes them +- **KPI** — Measures of value proposition / revenue — cite KPI IDs + +## Required sections (after Metadata / Version History) + +1. **Purpose / Scope** — one paragraph. +2. **Canvas** — all 9 building blocks populated, none empty: Key Partners, + Key Activities, Key Resources, Value Propositions, Customer + Relationships, Channels, Customer Segments, Cost Structure, Revenue + Streams. Must stay reviewable as a single, concise overview. +3. **Assumptions** — explicit and testable (how would we know it is wrong?). +4. **Consistency Check** — Revenue Streams vs Cost Structure agree; + Segments/Channels match stakeholder groups from `SA`. + +Cite Business Case objectives (`[BC-001]`) instead of restating them. diff --git a/.claude/skills/artifact/references/BPMN.md b/.claude/skills/artifact/references/BPMN.md new file mode 100644 index 0000000..6c8cda9 --- /dev/null +++ b/.claude/skills/artifact/references/BPMN.md @@ -0,0 +1,22 @@ +# BPMN Process Model (BPMN) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The stated business goal/objective the process serves +- **SA** — Participants / lanes — cite S-IDs +- **UCD** — Forward link: actors and goals derived from this process + +## Required sections (after Metadata / Version History) + +1. **Purpose and Business Goal** — the Business Case objective realized + (cite `[BC-001]`). +2. **Participants (Pools / Lanes)** — every participant, mapped to an `SA` + stakeholder ID where one exists. +3. **Process Diagram** — valid BPMN 2.0. Message flows cross pool + boundaries; sequence flows do not. Keep the diagram source next to the + document (BPMN has no native markdown form) so it stays diffable. +4. **Element Table** — `Element | Type | Lane | Description` for every + event, activity and gateway. Every gateway states its type (XOR/AND/OR) + and its matching join. +5. **Path Coverage** — every path runs from a start event to a defined end + event; no dead ends. diff --git a/.claude/skills/artifact/references/DCD.md b/.claude/skills/artifact/references/DCD.md new file mode 100644 index 0000000..c1df83b --- /dev/null +++ b/.claude/skills/artifact/references/DCD.md @@ -0,0 +1,27 @@ +# Design Class Diagram (DCD) (DCD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **DM** — Concepts each design class refines — names must stay consistent +- **SD** — Messages that become method signatures +- **ERD** — Forward: persistence of the classes' attributes + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope**. +2. **Diagram** — PlantUML class diagram: visibility markers (`+` `-` `#`) + on every member; association vs aggregation vs composition vs dependency + used per true ownership/lifecycle; multiplicity and navigability on every + association. +3. **Class Table** — `Class | Refines (Domain Model concept) | + Responsibility (one sentence) | Attributes | Operations`. SOLID applied; + no god classes; names consistent with the Domain Model. +4. **Method Traceability** — `Method signature | Operation Contract / SD + message`; every method traces to one. +5. **Pattern Annotations** — `Pattern | Classes | Rationale`, explicit. +6. **Dependency Check** — note confirming no circular class/package + dependencies (or an explicit justification). + +## Terminology + +Class and attribute names are the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.claude/skills/artifact/references/DICT.md b/.claude/skills/artifact/references/DICT.md new file mode 100644 index 0000000..d9d6872 --- /dev/null +++ b/.claude/skills/artifact/references/DICT.md @@ -0,0 +1,31 @@ +# Domain Dictionary (DICT) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The business goals the vocabulary serves +- **SA** — The Product Owner (and other business stakeholders) whose terms are recorded +- **DM** — Concepts whose PO terms are recorded + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the PO language and domain (from the registry's + `Languages` section, also in the `Language` and `Domain` Metadata rows) and + what the dictionary covers. +2. **Dictionary** — one row per term: + `PO term | Language | IT term | Definition | Used as PO term in | Used as IT term in`. + The definition is written in the PO language. "Used as PO term in" and + "Used as IT term in" list artifact types (for example `DM` and `OC, SD, + DCD, ERD`). +3. **Rules** — the register split (PO term in the Domain Model, use cases and + user stories; IT term in the Operation Contract, Sequence Diagram, Design + Class Diagram and ERD) and one IT term per PO term. + +One dictionary has one domain: the PO terms are the domain's own words (for +example `medical`), the IT terms are professional IT. A project whose PO terms +come from two domains keeps one dictionary per domain, each with its own +`Domain` row; a term never appears in two. + +Keep the dictionary in step with the Domain Model: a new concept gets a row +in the same change. When the PO language is English, the PO and IT columns can +still differ (a business word against a technical one); keep the file anyway +when the two registers use different words. diff --git a/.claude/skills/artifact/references/DM.md b/.claude/skills/artifact/references/DM.md new file mode 100644 index 0000000..de35ff6 --- /dev/null +++ b/.claude/skills/artifact/references/DM.md @@ -0,0 +1,26 @@ +# Domain Model (DM) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UC** — Source of every concept (noun phrases) — cite each `UC-*` used +- **UCD** — Scope check: actors/goals the model must cover +- **SSD** — Forward: system operations that act on these concepts + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — which use cases the model covers. +2. **Diagram** — PlantUML class diagram (or linked source) showing + **concepts, attributes and associations only — no operations**. + Business language throughout ("Sale", not "SaleTable"/"SaleClass"). +3. **Concept Table** — `Concept | Definition | Attributes | Source (use + case noun phrase / glossary)`. Every concept traces to a noun in a use + case or glossary. Attributes are simple domain data, not foreign-key-like + references (model those as associations). +4. **Association Table** — `From | Association name (with reading + direction) | To | Multiplicity (both ends)`. All multiplicities present. +5. **Generalizations** — only true "is-a" relationships, never inheritance + for code reuse. + +## Terminology + +Concept names are the PO terms recorded in the dictionary (`DICT`); add a row there for each new concept. diff --git a/.claude/skills/artifact/references/ERD.md b/.claude/skills/artifact/references/ERD.md new file mode 100644 index 0000000..27a98ac --- /dev/null +++ b/.claude/skills/artifact/references/ERD.md @@ -0,0 +1,22 @@ +# Entity Relationship Diagram (ERD) (ERD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **DCD** — **Required source**: classes/attributes each entity persists; data types must match + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope**. +2. **Diagram** — PlantUML entity diagram with PK/FK marked on every entity and + cardinality (1:1, 1:N) on every relationship; N:M relationships resolved + through explicit junction entities. +3. **Entity Table** — per entity: `Attribute | Type | PK/FK | Nullable | + Source (DCD class.attribute)`. Types consistent with the DCD; consistent + naming, no implementation-specific abbreviations. +4. **Relationship Table** — `Entity | Cardinality | Entity | FK | Rule`. +5. **Normalization Notes** — 3NF confirmed; any denormalization documented + with its performance justification. + +## Terminology + +Entity and column names follow the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.claude/skills/artifact/references/GOV.md b/.claude/skills/artifact/references/GOV.md new file mode 100644 index 0000000..d441bc6 --- /dev/null +++ b/.claude/skills/artifact/references/GOV.md @@ -0,0 +1,16 @@ +# Governance / ARB Workflow (GOV) + +One per project: `docs/sqa/governance.md`. Sections: Purpose, ARB Review +Workflow (submission → QC review → `RC-*` record → Go/No-Go → sign-off → +Traceability update), RACI by Artifact Category, Escalation Rules, Cadence. +The template has the standard text; fill the RACI. + +**RACI cells use exact stakeholder IDs from the project's Stakeholder +Analysis (`S`), never role names.** + +Related singletons (edit the existing file, no scaffold needed): +- `PRC` — `framework/process/review-checklist-process.md`, the narrative + review procedure. Framework-level; do not add project data. +- `CSG` — `docs/sqa/coding-standards-governance.md`. Scope is *defining and + governing* (not enforcing) language standards and style guides, as set by + the project Business Case's scope. diff --git a/.claude/skills/artifact/references/KPI.md b/.claude/skills/artifact/references/KPI.md new file mode 100644 index 0000000..7e5b01c --- /dev/null +++ b/.claude/skills/artifact/references/KPI.md @@ -0,0 +1,20 @@ +# KPI Definitions (KPI) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The Success Criteria row each KPI is aligned to (`[BC-001]`) + +## Required sections (after Metadata / Version History) + +1. **Purpose** — which Business Case success criteria this document + operationalizes. +2. **KPI Definitions** — one row per KPI: `KPI ID | Name | SMART statement | + Baseline | Target | Business Case Success Criterion | Owner | Frequency & + Method | Data Source`. +3. **Thresholds** — per KPI: acceptable / at-risk / failing bounds. +4. **Reporting** — where results are reported and to whom. + +Rules: every KPI is SMART (reject goals/activities like "improve quality"); +baseline *and* target are both present; `Owner` is a stakeholder ID from +the project's Stakeholder Analysis (e.g. `S07`), never free text; give KPIs stable IDs (`KPI-01`, …) so +Milestones can cite them. diff --git a/.claude/skills/artifact/references/MIL.md b/.claude/skills/artifact/references/MIL.md new file mode 100644 index 0000000..9a8e621 --- /dev/null +++ b/.claude/skills/artifact/references/MIL.md @@ -0,0 +1,48 @@ +# Milestone / Gateway (MIL) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — The objective / constraint (e.g. project duration) the milestone traces to +- **KPI** — The KPI IDs evaluated at this gate +- **US** — Forward: the user stories (`US-.`) that deliver this gateway + +## Required sections (after Metadata / Version History) + +1. **Purpose** — what decision this gate supports. +2. **Deliverable** — the concrete, tangible output evaluated (never just a date). +3. **Go / No-Go Criteria** — objectively checkable, one per row. +4. **Dependencies** — other milestones that must precede this one. +5. **Traceability** — the Business Case objective and/or KPI ID(s) it maps to. +6. **Ownership** — owner and approving reviewer as `SA` stakeholder IDs. +7. **Target Date** — consistent with Business Case constraints. +8. **Tasks** — the implementation-level breakdown for this phase, one row + per task: `# | Task | Summary | Needs its own Use Case/User Story? | + Reference`. `Task` is a short title (becomes the Issue title on sync); + `Summary` is one to three sentences of real context — what the task + actually involves and why, grounded in this project's own documents, not + a restatement of the title — so someone reading the Issue on Gitea/GitHub + understands it without opening this file (becomes the Issue body). + +## Breaking a phase into tasks + +Not every task needs a use case — only model one when the task is something +a user, or another system, actually does: + +- **Needs a use case/user story** — "User resets password", "Admin exports + customer report", "Payment service processes refund". Set the column to + `Yes` and put the `US-…`/`UC-…` ID in Reference. +- **Plain task, no use case** — "Refactor authentication middleware", "Add + database index", "Upgrade React version", "Fix null-pointer bug", "Add + unit tests", "Configure CI pipeline", "Optimize SQL query". Set the column + to `No` and leave Reference blank, or point at the design artifact it + implements (`DCD-…`, `OC-…`). + +The hierarchy is: Business goal → Feature/requirement → Use case/user story +→ Tasks. The use case explains *why* a feature exists; tasks explain *how* +the team implements it — most tasks stay at that level. + +## Syncing to Gitea/GitHub + +Each `MIL-*` gateway becomes one Milestone on the git host; its `## Tasks` +row become Issues assigned to that milestone. Use the `project-planning` +skill and `framework/scripts/sync-project.sh` — do not create these by hand. diff --git a/.claude/skills/artifact/references/OC.md b/.claude/skills/artifact/references/OC.md new file mode 100644 index 0000000..3b74009 --- /dev/null +++ b/.claude/skills/artifact/references/OC.md @@ -0,0 +1,27 @@ +# Operation Contract (OC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SSD** — **Required source**: each contract traces to exactly one SSD message +- **DM** — Classes/associations named in pre/postconditions +- **SD** — Forward: the design realizing each contract's postconditions + +## Required sections (after Metadata / Version History) + +One block per system operation, each containing: + +1. **Operation** — complete signature: name, parameter types, return type + (must match the SSD message). +2. **Cross References** — the SSD message it traces to (one contract per + message) and the Domain Model concepts touched. +3. **Preconditions** — required state before execution, expressed in Domain + Model terms. +4. **Postconditions** — state changes only, in Larman's style: *instance + created / instance associated / attribute modified*. Declarative ("what"), + never algorithmic ("how"); avoid vague text like "system processes the + request". +5. **Exceptions** — error conditions, each with the failing precondition. + +## Terminology + +Use the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.claude/skills/artifact/references/PP.md b/.claude/skills/artifact/references/PP.md new file mode 100644 index 0000000..80784fe --- /dev/null +++ b/.claude/skills/artifact/references/PP.md @@ -0,0 +1,64 @@ +# Project Plan (PP) + +One per project: `docs/project-plan.md`. Schedules the phases (`MIL-*` +gateways) over the Business Case's timeline/duration constraint. + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` +checks this for you): + +- **BC** — the duration/constraint and objectives the plan schedules against +- **SA** — the communication cadence (e.g. sync frequency) the phase length follows +- **MIL** — every phase gateway the plan schedules (add each as it is created) +- **US** — the gateway user stories, once they exist + +## Required sections (after Metadata / Version History) + +1. **Purpose** — what the plan schedules and over what constraint. +2. **Planning Assumptions** — start date, phase length, any resolved + conflicts the phasing follows (cite `SA`/`BC` where relevant). +3. **Gateway Schedule** — one row per phase: `Gateway | Document | Window | + Decision date | Owner | Stories | Main deliverable | Milestone`. One row + per `MIL-*`. `Milestone` links to that phase's Gitea/GitHub Milestone once + `sync-project.sh` has created it (see "Phases, tasks and the git host" + below); leave it blank until then. +4. **Timeline diagram** — a PlantUML Gantt chart, one bar per phase plus a + milestone marker per Go/No-Go decision. +5. **Scope Coverage** — maps each Business Case scope item to the gateway + that delivers it. +6. **Dependencies** — the gateway order (usually a simple chain) and what a + No-Go does to later dates. +7. **Plan Risks** — risks specific to the plan (schedule slip, dependency + risk), separate from the Business Case's own Risks table. +8. **Open Issues** — anything unresolved (start date to confirm, ambiguous + targets, missing checklists needed by a later phase). + +## Phases, tasks and the git host + +Each phase is a `MIL-*` gateway document (see `references/MIL.md`), which +also holds that phase's task breakdown in its own `## Tasks` section. The +Project Plan does not repeat the tasks — it only lists the phases and their +schedule, plus a link to each phase's Milestone once synced (see above). Use +the `project-planning` skill to break a phase into tasks and sync phases (as +Milestones) and tasks (as Issues) to Gitea/GitHub: + +```bash +bash framework/scripts/sync-project.sh # dry run — prints the plan, no network calls +bash framework/scripts/sync-project.sh --apply # creates/updates Milestones and Issues +``` + +`--apply` needs a token: `GITEA_TOKEN` (Gitea, a personal access token — not +a deploy key) or `gh auth login` (GitHub). `GITEA_TOKEN` can come from a +`.env` file at the project root (copy `.env.example`, never commit it). If +`.env` is missing or has no token when a sync is needed, ask the user for +one and create `.env` from `.env.example` with it rather than skipping the +sync or inventing a value. + +After a real `--apply` run, copy each Milestone's URL into the Gateway +Schedule's `Milestone` column (a content update, not a status change — it +does not need a new `## Version History` row). + +## Validating + +`PP` has no QC checklist yet (open item) — validate a plan against the +Business Case constraint it schedules and against `## Go / No-Go Criteria` +in each `MIL-*` it lists, rather than a dedicated checklist. diff --git a/.claude/skills/artifact/references/QC.md b/.claude/skills/artifact/references/QC.md new file mode 100644 index 0000000..8395a60 --- /dev/null +++ b/.claude/skills/artifact/references/QC.md @@ -0,0 +1,55 @@ +# Quality Criteria checklist (QC) + +A QC checklist is the reusable review checklist for one artifact **type**. +It lives in the framework (`framework/qc/qc-.md`), is +project-independent, and **must never name or link a real artifact +instance** (no `[BC-001]`, no `RC-*`, no stakeholder IDs). Refer to types +generically ("the Stakeholder Analysis"). + +- **ID:** `QC--` (e.g. `QC-BC-001`); the version starts + at `001` and only changes when the checklist itself is revised. +- **Create:** `new-artifact.sh QC --id QC-XX-001 --file framework/qc/qc-.md + --cite QC-=framework/qc/qc-.md ...` +- **CrossReference:** the QC checklists immediately backward and forward in + Larman's chain, in both directions: + +``` +QC-SA → QC-BC → { QC-BMC, QC-BPMN, QC-KPI } → QC-MIL +QC-BC, QC-SA → QC-UCD → { QC-US, QC-UC } +QC-UC → QC-DM → QC-SSD → QC-OC → QC-SD → QC-DCD → QC-ERD +QC-ADR ↔ QC-DCD, QC-ERD +QC-DCD, QC-ADR → { QC-PY, QC-CL, QC-CPP, QC-CS } (language code checklists) +``` + +## Cross-cutting checklists + +`QC-LANG-001` (`framework/qc/qc-language-domain.md`, language and domain) is +not tied to one artifact type. Apply it together with the checklist of the +artifact's own type to every type the registry marks "Written in the PO +language"; the review record (`RC`) lists both checklists and keeps the rows of +each. It has an ID in the `QC-` form like the others, but the short +name `LANG` is not an artifact type: there is no `LANG` instance, template or +catalog row. Add another cross-cutting checklist only when a rule applies to +several types and does not belong in any one of their checklists. + +## Version History statuses + +The statuses are `Proposed`, `Accepted`, `Rejected` and `Deprecated`, as for every +artifact type (rule in the `artifact` skill, "Version History rule"). + +## Required sections (after Metadata / Version History) + +- **Purpose** — why this type matters, what decision it supports. +- **Quality Criteria Checklist** — `# | Criterion | Level | ISO/IEC 25010 + Characteristic(s) | Notes`. `Level` is `Mandatory` (baseline every instance + must meet) or `Optional` (advanced, may be deferred); a checklist with an + extra column (e.g. `Format`) keeps `Level` right after `Criterion`. Every criterion is tagged with at least one of + the eight ISO/IEC 25010:2023 characteristics (Functional Suitability, + Performance Efficiency, Compatibility, Usability, Reliability, Security, + Maintainability, Portability). Never add an untagged criterion. +- **Common Defects** — anti-patterns a reviewer rejects on sight. +- **Traceability Rule** — Backward / Forward bullets with the same `[QC-*]` + labels as `CrossReference`. + +Also add an entry to `framework/CHANGELOG.md`. Reviews of real instances are +recorded in the project as `RC-*` records, not in the checklist. diff --git a/.claude/skills/artifact/references/RC.md b/.claude/skills/artifact/references/RC.md new file mode 100644 index 0000000..e89eed5 --- /dev/null +++ b/.claude/skills/artifact/references/RC.md @@ -0,0 +1,45 @@ +# SQA Review Record (RC) + +One record per review of a specific artifact instance against its QC +checklist. Create one for **every** artifact instance in the project. + +- **File:** `docs/sqa/reviews/rc--.md` +- **ID:** `RC-NNN`, sequential across all artifact types (the registry's + Next Available Version for `RC`). +- **Create:** `new-artifact.sh RC --file docs/sqa/reviews/rc-NNN-.md + --cite = --cite QC--001=framework/qc/.md` + +## Required sections (after Metadata / Version History) + +- **Artifact Under Review** — links to the instance and to the QC checklist + used, the scope (`full review`, or `delta re-review` with the criteria + covered, the reason and the earlier record), the artifact's language and + domain (its `Language` and `Domain` rows, `n/a` for a technical type) and + the language reviewer (a stakeholder ID, or `none`). +- **Checklist Results** — `# | Criterion | Status (Pass/Fail/N-A) | + Evidence/Notes`, one row per criterion copied from the QC checklist, in + the same order. +- **Language and Domain Results** — only for an artifact of a type written in + the PO language: the rows of `QC-LANG-001`, same columns and order. `N-A` is + not allowed for criteria 3, 5 and 9. +- **Overall Verdict** — Go / Go-with-conditions / No-Go, with rationale. +- **Action Items** — `Action | Owner | Due`. `Owner` is a stakeholder ID from + the project's Stakeholder Analysis, never a role name. Every `Fail` and + every condition gets an item. + +## After the review + +- The reviewer must not be the artifact's author (see governance). For a + PO-language artifact the reviewer must read its language and know its + domain, or a second reviewer, the language reviewer, does and is named on + the record; see `process/review-checklist-process.md`. +- The record is in English. Quote evidence that is the artifact's own wording + in its language, with a short gloss. +- Add or update the instance's row in the Traceability Matrix, including + this `RC-*` in "Last Reviewed". +- On **Go**, set the reviewed artifact's latest `## Version History` row to + `Accepted` and the row before it to `Deprecated` (`Approved` is not a + status). On Go-with-conditions the status stays `Proposed` until the action + items are closed. If the change is refused for good, set the row to + `Rejected` (the earlier `Accepted` row stays). +- Step-by-step narrative: `framework/process/review-checklist-process.md`. diff --git a/.claude/skills/artifact/references/SA.md b/.claude/skills/artifact/references/SA.md new file mode 100644 index 0000000..a7307d4 --- /dev/null +++ b/.claude/skills/artifact/references/SA.md @@ -0,0 +1,27 @@ +# Stakeholder Analysis (SA) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **BC** — Business Case objectives each stakeholder concern traces to (Business Goal Alignment section) + +## Required sections (after Metadata / Version History) + +1. **Purpose** — why the analysis exists and the methodology it follows. +2. **Stakeholder Summary Table** — `ID | Name | Role/Title | Organization | + Power Level | Interest Level | Quadrant | Primary Concern (Business + Language)`. Every row fully filled; no unclassified stakeholder. +3. **Power/Interest Classification Rationale** — narrative per quadrant, + consistent with the table. +4. **Primary Concerns and FURPS+ Mapping** — each concern in business + language *and* mapped to a FURPS+ attribute. +5. **Communication Requirements** — channel, frequency, deliverable type, + tied to a project phase or milestone. +6. **Conflicting Interests and Mitigations** — every conflict has a + mitigation. +7. **Traceability Analysis** — stakeholder → actor/use case mapping, and + business-goal alignment citing Business Case (`[BC-001]`) objectives. +8. **Sign-Off**. + +Stakeholder IDs (`S01`, `S02`, …) are **stable: never renumbered or reused**. +Every other artifact cites them for RACI, ownership and review assignment +(`AGENTS.md` rule 3). Add new stakeholders with the next free `S`. diff --git a/.claude/skills/artifact/references/SD.md b/.claude/skills/artifact/references/SD.md new file mode 100644 index 0000000..a8b2476 --- /dev/null +++ b/.claude/skills/artifact/references/SD.md @@ -0,0 +1,26 @@ +# Sequence Diagram (Design) (SD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **OC** — **Required source**: the contract whose postconditions each diagram realizes +- **DCD** — Forward: classes/methods these messages become + +## Required sections (after Metadata / Version History) + +One block per realized Operation Contract: + +1. **Realizes** — the contract (`[OC-…]`) and its operation name. +2. **Diagram** — PlantUML sequence diagram: sync (solid filled arrow), + async (open arrow), returns (dashed); activations matching the call + nesting; `create` / `destroy` shown for transient objects; + `loop` / `alt` / `opt` fragments for conditional/repeated behavior. +3. **Pattern Annotations** — table `Pattern (GRASP/GoF) | Applied to | + Rationale`. Patterns are labelled, never implicit. +4. **Postcondition Coverage** — `Postcondition | Satisfied by message`; + every postcondition of the contract must be covered. +5. **Responsibility Check** — a short note showing no god-object receives + all messages (low coupling, high cohesion). + +## Terminology + +Use the IT terms from the dictionary (`DICT`), not the PO terms the Domain Model uses. diff --git a/.claude/skills/artifact/references/SSD.md b/.claude/skills/artifact/references/SSD.md new file mode 100644 index 0000000..16e8f94 --- /dev/null +++ b/.claude/skills/artifact/references/SSD.md @@ -0,0 +1,21 @@ +# System Sequence Diagram (SSD) (SSD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UC** — **Required source** of every SSD: cite the use case (name and ID) it depicts +- **DM** — Concepts behind message parameters / returned values +- **OC** — Forward: contract per system operation shown + +## Required sections (after Metadata / Version History) + +1. **Source Use Case** — name and ID (`[UC-…]`) and the specific scenario. +2. **Diagram** — PlantUML sequence diagram with just the actor and + `:System`; **no internal objects**. Dashed return arrows for operations + that produce a result. One scenario per diagram — separate diagrams for + alternate/exception flows (or state them out of scope). +3. **System Operations Table** — `Step | Message (verb phrase) | Parameters + | Return | Use case step`. Messages match the use case's main success + scenario step-for-step; justify any deviation. Message names become the + Operation Contract names. +4. **Lifecycle Notes** — creation/destruction of the System instance where + relevant (session/transaction scope). diff --git a/.claude/skills/artifact/references/TM.md b/.claude/skills/artifact/references/TM.md new file mode 100644 index 0000000..e09ceef --- /dev/null +++ b/.claude/skills/artifact/references/TM.md @@ -0,0 +1,18 @@ +# Traceability Matrix (TM) + +One matrix per project: `docs/sqa/traceability-matrix.md`. It makes the +Business Case's cross-artifact traceability target measurable. + +## Required sections (after Metadata / Version History) + +- **Purpose**. +- **Traceability Table** — one row per artifact instance: + `Artifact Instance | Type | Language | Domain | Upstream (Backward Link) | + Downstream (Forward Link) | Last Reviewed (RC-ID)`. `Language` and `Domain` + copy the artifact's Metadata rows so a reviewer can pick the right reviewer + from the matrix; they are `-` for a technical type (OC, SD, DCD, ERD, ADR, + TM, RC, QC, source code). `check-languages.sh --list` prints the same values. + Add or update a row whenever an instance is created or reviewed. +- **Coverage Notes** — which types have no instance yet, and how to read + `-`: in Upstream it means foundational; in Downstream it means nothing + is built on it yet; in Last Reviewed it means no `RC-*` exists yet. diff --git a/.claude/skills/artifact/references/TRN.md b/.claude/skills/artifact/references/TRN.md new file mode 100644 index 0000000..64b2b7d --- /dev/null +++ b/.claude/skills/artifact/references/TRN.md @@ -0,0 +1,18 @@ +# Reviewer Training (TRN) + +The reusable training material for reviewers. It mitigates the "resistance to +standardized reviews" risk of the Business Case. Framework-level, like `PRC`: +no project data, no stakeholder IDs, no links to project artifacts. The +*record* that a session happened (date, attendees, result) is a project +document, written after the session. + +- **File:** `framework/process/reviewer-training.md` (singleton, version `001`). +- **Create:** `new-artifact.sh TRN`. +- **CrossReference:** `PRC` (the procedure the training teaches). + +## Required sections (after Metadata / Version History) + +Purpose, Audience and Prerequisites, Learning Objectives, Agenda (modules with +minutes), Module Notes, Exercises (each with input and expected result), +Assessment (how and pass criteria), Session Record (what to capture). +Exercises use a seeded, generic example, not a real project artifact. diff --git a/.claude/skills/artifact/references/TRR.md b/.claude/skills/artifact/references/TRR.md new file mode 100644 index 0000000..70f083e --- /dev/null +++ b/.claude/skills/artifact/references/TRR.md @@ -0,0 +1,23 @@ +# Reviewer Training Record (TRR) + +The project's record that a reviewer training session happened: the evidence +that mitigates the "resistance to standardized reviews" risk and satisfies a +"training delivered and recorded" gateway criterion. It records one session +of the framework's training material (`TRN`); it holds project data +(stakeholder IDs, dates), so it lives in the project, not in the framework. + +- **File:** `docs/sqa/reviewer-training-record.md` (one per project; add a new + row under `## Attendees and Assessment` and a Version History row for each + further session). +- **Create:** `new-artifact.sh TRR --file docs/sqa/reviewer-training-record.md + --cite TRN-001=framework/process/reviewer-training.md`. +- **CrossReference:** `TRN` (the material that was taught). + +## Required sections (after Metadata / Version History) + +Session (material, date, facilitator, format), Attendees and Assessment +(stakeholder IDs, attended, Pass or Not yet against the assessment in the +material, and the languages the reviewer reads and the domains they know, for +example `da, en; it, medical`: the review process uses it to choose a reviewer +for a PO-language artifact), Feedback, Follow-ups (action, owner as a stakeholder ID, due). +Fill the record after the session; never before. diff --git a/.claude/skills/artifact/references/UC.md b/.claude/skills/artifact/references/UC.md new file mode 100644 index 0000000..fcb5da8 --- /dev/null +++ b/.claude/skills/artifact/references/UC.md @@ -0,0 +1,35 @@ +# Use Case (UC) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UCD** — Actor and use-case names must match it exactly +- **US** — Stories this use case decomposes into +- **SA** — Stakeholders & Interests — cite S-IDs +- **DM** — Forward: concepts derived from this use case's nouns +- **SSD** — Forward: the SSD depicting this use case's main scenario + +## Location + +`docs/uc-NNN/uc.md`, one folder per use case (create with +`new-artifact.sh UC --file docs/uc-NNN/uc.md`). The artifacts this use case +affects are saved in the same folder; see the `project-planning` skill, "Use +cases get their own folder". + +## Required sections (after Metadata / Version History) + +Pick the format explicitly (`Format: Brief | Casual | Fully Dressed`) and +state the **scope/level** (summary, user-goal, subfunction). Always: primary +actor, pre/postconditions, goal-perspective wording with no UI or +implementation detail. + +- **Brief** — a single paragraph summarizing only the main success scenario. +- **Casual** — informal multi-paragraph narrative; may mention some + alternate flows. +- **Fully Dressed** — all sections, in order: Scope, Level, Primary Actor, + Stakeholders and Interests (cite `SA` S-IDs), Preconditions, + Postconditions (success guarantee), Main Success Scenario (numbered + steps), Extensions / Alternative Flows (reference `<>` / + `<>` use cases), Special Requirements / Business Rules (per step), + Open Issues. + +Title and actor names must match `UCD` and `US` exactly. diff --git a/.claude/skills/artifact/references/UCD.md b/.claude/skills/artifact/references/UCD.md new file mode 100644 index 0000000..c7ce5bd --- /dev/null +++ b/.claude/skills/artifact/references/UCD.md @@ -0,0 +1,24 @@ +# Use Case Diagram (UCD) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **SA** — Each actor traces to a stakeholder need — cite S-IDs +- **BC** — Scope / objectives the boundary reflects +- **US** — Forward: stories whose role must match an actor here +- **UC** — Forward: the use cases detailing each goal shown + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the system boundary in words. +2. **Diagram** — actors with correct stereotypes (`<>`, + `<>`), a labelled system boundary, `<>` / `<>` + used per UML 2.5.1 (not as generic "uses"). Embed PlantUML or link the + diagram source; no UI or implementation detail. +3. **Actor Table** — `Actor | Stereotype | Stakeholder ID (SA) | Goals + (use cases)`. No orphan actors: every actor appears in at least one use + case. +4. **Use Case Table** — `Use Case | Actor(s) | Goal`, names as **verb + phrases describing actor goals** ("Place Order"), not system operations + ("Validate Input"). +5. **Relationships** — every `<>` / `<>` with a one-line + justification. diff --git a/.claude/skills/artifact/references/US.md b/.claude/skills/artifact/references/US.md new file mode 100644 index 0000000..554f93a --- /dev/null +++ b/.claude/skills/artifact/references/US.md @@ -0,0 +1,22 @@ +# User Story (US) + +Cite in `CrossReference` only if the instance exists (`new-artifact.sh` checks this for you): + +- **UCD** — The story's role must match an actor defined here +- **UC** — The use case each story traces to +- **BC** — Objective / epic the story ultimately supports +- **MIL** — The gateway (epic) each story delivers — one or more stories per gateway + +## Required sections (after Metadata / Version History) + +1. **Purpose and Scope** — the epic(s) covered. +2. **Story List** — each story with a stable ID `US-.` (e.g. + `US-001.01`, so it cannot be confused with the document ID `US-001`): + - Statement: **As a** ``, **I want** ``, **so that** + `` — one goal per story, no implementation detail. + - **Acceptance Criteria** — clear and testable (Given/When/Then works). + - **Traces to** — the use case (`UC-…`) or epic it comes from; a gateway + (`MIL-…`) is an epic. + - **Size** — fits a single iteration. +3. **INVEST Check** — one line confirming Independent, Negotiable, + Valuable, Estimable, Small, Testable (flag any exception with reason). diff --git a/.claude/skills/artifact/templates/ADR.md b/.claude/skills/artifact/templates/ADR.md new file mode 100644 index 0000000..05a4acf --- /dev/null +++ b/.claude/skills/artifact/templates/ADR.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +Allowed Status values: `Proposed`, `Accepted`, `Rejected`, `Deprecated`, `Superseded by ADR-NNNN`. + +--- + +## Context + + + +## Decision + + + +## Consequences + +**Positive:** + +- + +**Negative:** + +- + +## Affected Artifacts + +- [] — , or a single "-" if none + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/BC.md b/.claude/skills/artifact/templates/BC.md new file mode 100644 index 0000000..653014e --- /dev/null +++ b/.claude/skills/artifact/templates/BC.md @@ -0,0 +1,72 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Executive Summary + +## Methodological and Standards Foundation + +## Problem Statement + +## Business Opportunity + +## Objectives + +## Scope + +### In Scope + +### Out of Scope + +## Expected Benefits + +### Tangible Benefits + +### Intangible Benefits + +## Strategic Alignment + +## Success Criteria + +| # | Criterion | Target | Measure | +| --- | --- | --- | --- | + +## Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | + +## Assumptions + +## Constraints + +## Cost–Benefit Assessment + +| Costs | Benefits | +| --- | --- | + +## Stakeholders + +| Stakeholder ID (SA) | Interest in this project | +| --- | --- | + +## Recommendation + + — + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/BMC.md b/.claude/skills/artifact/templates/BMC.md new file mode 100644 index 0000000..a4f14fb --- /dev/null +++ b/.claude/skills/artifact/templates/BMC.md @@ -0,0 +1,115 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose / Scope + +Operationalizes: ([BC-]) + +## Canvas + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
Key PartnersKey ActivitiesValue PropositionsCustomer RelationshipsCustomer Segments
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
+ +
    +
  • +
+
Key ResourcesChannels
+ +
    +
  • +
+
+ +
    +
  • +
+
Cost StructureRevenue Streams
+ +
    +
  • +
+
+ +
    +
  • +
+
+ + +## Assumptions + +| # | Assumption | How it can be tested | +| --- | --- | --- | + +## Consistency Check + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/BPMN.md b/.claude/skills/artifact/templates/BPMN.md new file mode 100644 index 0000000..0cbe4ce --- /dev/null +++ b/.claude/skills/artifact/templates/BPMN.md @@ -0,0 +1,43 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Business Goal + +Realizes: ([BC-]) + +## Participants + +| Pool / Lane | Participant | Stakeholder ID (SA) | +| --- | --- | --- | + +## Process Diagram + + + +## Element Table + +| Element | Type (event / activity / gateway) | Lane | Description | +| --- | --- | --- | --- | + +## Path Coverage + +| Path | Start event | End event | +| --- | --- | --- | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/DCD.md b/.claude/skills/artifact/templates/DCD.md new file mode 100644 index 0000000..7ee1c17 --- /dev/null +++ b/.claude/skills/artifact/templates/DCD.md @@ -0,0 +1,50 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Diagram + +```plantuml +@startuml +class Controller { + -repo : Repository + +operationName(param : Type) : ReturnType +} +class Entity +Controller --> Entity : uses +@enduml +``` + +## Class Table + +| Class | Refines (Domain Model concept) | Responsibility | Attributes | Operations | +| --- | --- | --- | --- | --- | + +## Method Traceability + +| Method signature | Operation Contract / SD message | +| --- | --- | + +## Pattern Annotations + +| Pattern | Classes | Rationale | +| --- | --- | --- | + +## Dependency Check + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/DICT.md b/.claude/skills/artifact/templates/DICT.md new file mode 100644 index 0000000..da5f2b8 --- /dev/null +++ b/.claude/skills/artifact/templates/DICT.md @@ -0,0 +1,37 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +Maps each Product Owner (PO) term to its professional IT term. PO language: +. + +## Dictionary + +| PO term | Language | IT term | Definition | Used as PO term in | Used as IT term in | +| --- | --- | --- | --- | --- | --- | +| | | | | DM | OC, SD, DCD, ERD | + +## Rules + +- The Domain Model, use cases and user stories use the PO term; the Operation + Contract, Sequence Diagram, Design Class Diagram and ERD use the IT term. +- One IT term per PO term and one PO term per IT term; no synonyms. + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/DM.md b/.claude/skills/artifact/templates/DM.md new file mode 100644 index 0000000..8e46404 --- /dev/null +++ b/.claude/skills/artifact/templates/DM.md @@ -0,0 +1,56 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +Covers: + +## Diagram + +Concepts, attributes and associations only — no operations. + +```plantuml +@startuml +class Order { + date + status +} +class Customer { + name +} +Customer "1" --> "0..*" Order : places +@enduml +``` + +## Concept Table + +| Concept | Definition | Attributes | Source (use case / glossary) | +| --- | --- | --- | --- | + +## Association Table + +| From | Association (reading direction) | To | Multiplicity | +| --- | --- | --- | --- | + +## Generalizations + +| General | Specializations | Is-a justification | +| --- | --- | --- | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/ERD.md b/.claude/skills/artifact/templates/ERD.md new file mode 100644 index 0000000..4a3c5bc --- /dev/null +++ b/.claude/skills/artifact/templates/ERD.md @@ -0,0 +1,53 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Diagram + +```plantuml +@startuml +hide circle +entity CUSTOMER { + * id : int <> + -- + name : string +} +entity ORDER { + * id : int <> + -- + * customer_id : int <> +} +CUSTOMER ||--o{ ORDER : places +@enduml +``` + +## Entity Table + +### + +| Attribute | Type | PK/FK | Nullable | Source (DCD class.attribute) | +| --- | --- | --- | --- | --- | + +## Relationship Table + +| Entity | Cardinality | Entity | FK | Rule | +| --- | --- | --- | --- | --- | + +## Normalization Notes + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/GOV.md b/.claude/skills/artifact/templates/GOV.md new file mode 100644 index 0000000..8f58c1d --- /dev/null +++ b/.claude/skills/artifact/templates/GOV.md @@ -0,0 +1,70 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + +Defines how this project applies Quality Criteria (QC) checklists to real +artifact instances, producing SQA Review Records and Go/No-Go decisions. + +## ARB Review Workflow + +1. **Submission** — the artifact owner submits an instance for review, + identifying its type's QC checklist (`framework/qc/qc-*.md`). +2. **QC Checklist Review** — the assigned reviewer (see RACI) applies the + checklist criterion by criterion. +3. **Review Record** — the reviewer documents the outcome as an SQA Review + Record (`RC-*` under `docs/sqa/reviews/`). +4. **Go/No-Go Decision** — the Accountable role for the artifact category + decides; contested or cross-cutting cases escalate to the ARB Chair. +5. **Sign-off** — on **Go**, the artifact's `## Version History` gets an + `Accepted` row (the previous row becomes `Deprecated`). On **Go-with-conditions**, status stays `Proposed` until + the Action Items are closed. On **No-Go**, the artifact returns to its + owner. +6. **Traceability Update** — the Traceability Matrix is updated with the + instance and its `RC-*` reference. + +## RACI by Artifact Category + +Fill every cell with stakeholder IDs from the Stakeholder Analysis (`S`), +never role names. + +| Artifact Category | Responsible (runs the review) | Accountable (Go/No-Go owner) | Consulted | Informed | +| --- | --- | --- | --- | --- | +| Strategic (Stakeholder Analysis, Business Case, BMC) | S | S | S | S | +| Process/Business (BPMN, KPI, Milestones/Gateways) | S | S | S | S | +| Requirements (Use Case Diagram, User Story, Use Case) | S | S | S | S | +| Modeling/Design (Domain Model, SSD, Operation Contract, Sequence Diagram, DCD, ERD) | S | S | S | S | + +Cross-cutting escalations and disputed verdicts are Accountable to the ARB +Chair (`S`), overriding the category-level Accountable role. + +## Escalation Rules + +- A **No-Go** verdict, or any disagreement between the Responsible reviewer + and the category's Accountable owner, escalates to the ARB Chair. +- A reviewer may not review an instance they authored. +- Repeated No-Go verdicts (2 or more) on the same artifact type trigger a + review of the corresponding `QC-*` checklist itself. + +## Cadence + +- Reviews are triggered per artifact instance as it is produced or revised. +- QC checklists are reviewed annually. + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/KPI.md b/.claude/skills/artifact/templates/KPI.md new file mode 100644 index 0000000..2cfad59 --- /dev/null +++ b/.claude/skills/artifact/templates/KPI.md @@ -0,0 +1,37 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## KPI Definitions + +| KPI ID | Name | SMART statement | Baseline | Target | Business Case success criterion | Owner (S-ID) | Frequency & method | Data source | +| --- | --- | --- | --- | --- | --- | --- | --- | --- | +| KPI-01 | | | | | | | | | + +## Thresholds + +| KPI ID | Acceptable | At risk | Failing | +| --- | --- | --- | --- | + +## Reporting + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/MIL.md b/.claude/skills/artifact/templates/MIL.md new file mode 100644 index 0000000..794a2d7 --- /dev/null +++ b/.claude/skills/artifact/templates/MIL.md @@ -0,0 +1,60 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Deliverable + + + +## Go / No-Go Criteria + +| # | Criterion (objectively checkable) | Go | No-Go | +| --- | --- | --- | --- | + +## Dependencies + +| Depends on | Reason | +| --- | --- | + +## Traceability + +| Business Case objective / KPI / user story | Reference | +| --- | --- | + +## Ownership + +| Role | Stakeholder ID (SA) | +| --- | --- | +| Owner | | +| Approving reviewer | | + +## Target Date + +YYYY-MM-DD — + +## Tasks + +| # | Task | Summary | Needs its own Use Case/User Story? | Reference | +| --- | --- | --- | --- | --- | +| 1 | | | No | | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/OC.md b/.claude/skills/artifact/templates/OC.md new file mode 100644 index 0000000..87a4220 --- /dev/null +++ b/.claude/skills/artifact/templates/OC.md @@ -0,0 +1,41 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Contract: + +| Item | Value | +| --- | --- | +| Operation | `operationName(param: Type): ReturnType` | +| Traces to | in [SSD-] | +| Domain Model concepts | ([DM-]) | + +**Preconditions** + +- + +**Postconditions** + +- A instance was created. +- was associated with . +- . was set to . + +**Exceptions** + +| Condition (failing precondition) | Outcome | +| --- | --- | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/PP.md b/.claude/skills/artifact/templates/PP.md new file mode 100644 index 0000000..1e3487b --- /dev/null +++ b/.claude/skills/artifact/templates/PP.md @@ -0,0 +1,63 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Planning Assumptions + +- Week 1 starts ; the plan ends by , per the Business Case constraint. +- Phase length: . + +## Gateway Schedule + +| Gateway | Document | Window | Decision date | Owner | Stories | Main deliverable | Milestone | +| --- | --- | --- | --- | --- | --- | --- | --- | +| | [MIL-] | | | | | | | + +```plantuml +@startgantt +Project starts +[Phase 1] starts and ends +[Phase 1 Go/No-Go] happens +@endgantt +``` + +## Scope Coverage + +| Business Case scope item | Gateway | +| --- | --- | + +## Dependencies + +``` + → → ... +``` + +## Plan Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | + +## Open Issues + +- + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/QC.md b/.claude/skills/artifact/templates/QC.md new file mode 100644 index 0000000..4e37af3 --- /dev/null +++ b/.claude/skills/artifact/templates/QC.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +Allowed Status values: `Proposed`, `Accepted`, `Rejected`, `Deprecated`. The latest reviewed row is `Accepted`; the row before it is `Deprecated`. + +--- + +## Purpose + + + +## Quality Criteria Checklist + +Level: **Mandatory** criteria are the baseline every instance must meet; **Optional** criteria are advanced and may be deferred. + +| # | Criterion | Level | ISO/IEC 25010 Characteristic(s) | Notes | +| --- | --- | --- | --- | --- | +| 1 | | Mandatory | | | + +## Common Defects + +- +- + +## Traceability Rule + +- Backward: ([QC--]) +- Forward: ([QC--]) + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/RC.md b/.claude/skills/artifact/templates/RC.md new file mode 100644 index 0000000..a4dcdb7 --- /dev/null +++ b/.claude/skills/artifact/templates/RC.md @@ -0,0 +1,51 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [] +- Checklist used: [] (`QC--`, e.g. `QC-BC-001`) +- Scope: , reason, earlier record []> +- Language and domain: / (the artifact's Metadata rows), or `n/a` for a technical type +- Language reviewer: , or `none` when the reviewer reads the language and knows the domain + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | | Pass/Fail/N-A | | + +## Language and Domain Results + +Only for an artifact of a type written in the PO language: the rows of +`QC-LANG-001`, in order. Delete this section for a technical type. + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | | Pass/Fail/N-A | | + +## Overall Verdict + + — + +## Action Items + +| Action | Owner | Due | +| --- | --- | --- | +| | | | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/SA.md b/.claude/skills/artifact/templates/SA.md new file mode 100644 index 0000000..2b18ecc --- /dev/null +++ b/.claude/skills/artifact/templates/SA.md @@ -0,0 +1,56 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Stakeholder Summary Table + +| ID | Name | Role/Title | Organization | Power Level | Interest Level | Quadrant | Primary Concern (Business Language) | +| --- | --- | --- | --- | --- | --- | --- | --- | +| S01 | | | | HIGH / MEDIUM / LOW | HIGH / MEDIUM / LOW | Manage Closely / Keep Satisfied / Keep Informed / Monitor | | + +## Power/Interest Classification Rationale + +## Primary Concerns and FURPS+ Mapping + +| ID | Concern | FURPS+ attribute | +| --- | --- | --- | + +## Communication Requirements + +| ID | Channel | Frequency | Deliverable | Phase / Milestone | +| --- | --- | --- | --- | --- | + +## Conflicting Interests and Mitigations + +| Conflict | Stakeholders | Mitigation | +| --- | --- | --- | + +## Traceability Analysis + +### Business Goal Alignment + +| Stakeholder | Concern | Business Case objective | +| --- | --- | --- | + +## Sign-Off + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/SD.md b/.claude/skills/artifact/templates/SD.md new file mode 100644 index 0000000..70a02af --- /dev/null +++ b/.claude/skills/artifact/templates/SD.md @@ -0,0 +1,47 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Sequence: + +**Realizes:** `operationName` in [OC-] + +### Diagram + +```plantuml +@startuml +participant ":Controller" as C +participant ":Collaborator" as X +C -> X : message(args) +activate X +X --> C : result +deactivate X +@enduml +``` + +### Pattern Annotations + +| Pattern (GRASP / GoF) | Applied to | Rationale | +| --- | --- | --- | + +### Postcondition Coverage + +| Postcondition (from contract) | Satisfied by message | +| --- | --- | + +### Responsibility Check + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/SSD.md b/.claude/skills/artifact/templates/SSD.md new file mode 100644 index 0000000..7856d4c --- /dev/null +++ b/.claude/skills/artifact/templates/SSD.md @@ -0,0 +1,42 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Source Use Case + + ([UC-]) — scenario:
+ +## Diagram + +```plantuml +@startuml +actor Actor as A +participant ":System" as S +A -> S : verbPhrase(param) +S --> A : result +@enduml +``` + +## System Operations + +| Step | Message | Parameters | Return | Use case step | +| --- | --- | --- | --- | --- | + +## Lifecycle Notes + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/TM.md b/.claude/skills/artifact/templates/TM.md new file mode 100644 index 0000000..6e08eba --- /dev/null +++ b/.claude/skills/artifact/templates/TM.md @@ -0,0 +1,30 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + +Tracks backward/forward links between artifact instances so that the Business Case's +cross-artifact traceability success criterion is measurable. A row is added or +updated whenever an artifact instance is created or reviewed. + +## Traceability Table + +| Artifact Instance | Type | Language | Domain | Upstream (Backward Link) | Downstream (Forward Link) | Last Reviewed (RC-ID) | +| --- | --- | --- | --- | --- | --- | --- | +| [] | | | | [] | [] | [] | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/TRN.md b/.claude/skills/artifact/templates/TRN.md new file mode 100644 index 0000000..8dd6170 --- /dev/null +++ b/.claude/skills/artifact/templates/TRN.md @@ -0,0 +1,60 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose + + + +## Audience and Prerequisites + + + +## Learning Objectives + +By the end, a participant can: + +- + +## Agenda + +| Module | Topic | Minutes | +| --- | --- | --- | +| 1 | | | + +## Module Notes + +### Module 1: + + + +## Exercises + +### Exercise 1: + + + +## Assessment + + + +## Session Record + +What is recorded after each session, in the project (never in the framework): +date, facilitator, attendees, result against the assessment, feedback and +follow-ups. + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/TRR.md b/.claude/skills/artifact/templates/TRR.md new file mode 100644 index 0000000..f73f357 --- /dev/null +++ b/.claude/skills/artifact/templates/TRR.md @@ -0,0 +1,43 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Session + +| Item | Value | +| --- | --- | +| Training material | [] | +| Date | | +| Facilitator | | +| Format and place | | + +## Attendees and Assessment + +| Stakeholder ID (SA) | Attended | Assessment (Pass / Not yet) | Languages and domains | Notes | +| --- | --- | --- | --- | --- | +| | | | | | + +## Feedback + + + +## Follow-ups + +| Action | Owner | Due | +| --- | --- | --- | +| | | | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/UC.md b/.claude/skills/artifact/templates/UC.md new file mode 100644 index 0000000..b8b3388 --- /dev/null +++ b/.claude/skills/artifact/templates/UC.md @@ -0,0 +1,57 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +**Format:** Brief | Casual | Fully Dressed (delete the unused sections below) + +## Brief + + + +## Casual + + + +## Fully Dressed + +- **Scope:** +- **Level:** summary | user-goal | subfunction +- **Primary Actor:** ]> +- **Stakeholders and Interests:** + - — +- **Preconditions:** +- **Postconditions (success guarantee):** + +### Main Success Scenario + +1. +2. + +### Extensions (Alternative / Exception Flows) + +- 2a. : + 1. (`<>` / `<>` ) + +### Special Requirements / Business Rules + +| Step | Rule | +| --- | --- | + +### Open Issues + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/UCD.md b/.claude/skills/artifact/templates/UCD.md new file mode 100644 index 0000000..2fd7867 --- /dev/null +++ b/.claude/skills/artifact/templates/UCD.md @@ -0,0 +1,52 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + + + +## Diagram + +```plantuml +@startuml +left to right direction +actor "Name" as A1 +rectangle "System Name" { + usecase "Verb-phrase goal" as UC1 +} +A1 --> UC1 +@enduml +``` + +## Actor Table + +| Actor | Stereotype | Stakeholder ID (SA) | Goals (use cases) | +| --- | --- | --- | --- | + +## Use Case Table + +| Use Case | Actor(s) | Goal | +| --- | --- | --- | + +## Relationships + +| From | Relationship (`<>` / `<>`) | To | Justification | +| --- | --- | --- | --- | + +--- + +@LINKS@ diff --git a/.claude/skills/artifact/templates/US.md b/.claude/skills/artifact/templates/US.md new file mode 100644 index 0000000..f0fa723 --- /dev/null +++ b/.claude/skills/artifact/templates/US.md @@ -0,0 +1,38 @@ +# @TITLE@ + +## Metadata +| Key | Value | +| --- | --- | +| ID | @ID@ | +| CrossReference | @CROSSREF@ | +| Language | @LANGUAGE@ | +| Domain | @DOMAIN@ | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| @DATE@ | Proposed | @AUTHOR@ | | Initial version | pending | + +--- + +## Purpose and Scope + +## Story List + +### @ID@.01 — + +**As a** ]>, **I want** , **so that** . + +**Acceptance Criteria** + +- Given , when , then . + +| Traces to | Size | INVEST exceptions | +| --- | --- | --- | +| [UC-] or [MIL-] | fits one iteration | none | + +## INVEST Check + +--- + +@LINKS@ diff --git a/.claude/skills/coding-conventions/SKILL.md b/.claude/skills/coding-conventions/SKILL.md new file mode 100644 index 0000000..d207710 --- /dev/null +++ b/.claude/skills/coding-conventions/SKILL.md @@ -0,0 +1,98 @@ +--- +name: coding-conventions +description: Programming conventions for writing or reviewing source code — naming, layout, formatting and language idioms — for Python, C, C++, C# and Shell (bash). Use when writing, editing, reviewing or refactoring code in one of those languages, choosing names, setting up a formatter/linter config, or adding conventions for another language. Holds the rules shared by every language and points to a per-language sub-skill. +--- + +# Coding Conventions + +One skill for all languages. This file holds what is true in every language; +everything specific to one language is in a sub-skill that is read only when +that language is in hand: + +| Language | Sub-skill | QC checklist | +| --- | --- | --- | +| Python | `references/python.md` | `framework/qc/qc-programming-python.md` (`QC-PY-001`) | +| C | `references/c.md` | `framework/qc/qc-programming-c.md` (`QC-CL-001`) | +| C++ | `references/cpp.md` | `framework/qc/qc-programming-cpp.md` (`QC-CPP-001`) | +| C# | `references/csharp.md` | `framework/qc/qc-programming-csharp.md` (`QC-CS-001`) | +| Shell (bash) | `references/shell.md` | `framework/qc/qc-programming-shell.md` (`QC-SH-001`) | + +Read the sub-skill for the language you are working in, then write or review +the code. To review, use the language's QC checklist and record the result as +an `RC-*` (see the `artifact` skill). + +## Precondition: a planned task + +Before writing or editing code under `src/` or `tests/`, name the task row +(`MIL-NNN`, task N) or the synced issue, and the use case or design artifact +it implements, or say it is a plain technical task. If you cannot, refuse and +use the `project-planning` skill instead (rule: `framework/process/plan-first-gate.md`). +The task's milestone must be `Accepted` with a `Go` review record; if it is +still `Proposed`, or its latest review is conditional or No-Go, stop and say so. +Reviewing code needs no task. Before the pull request, code is reviewed against +the checklist of its language (`qc-programming-*`) and the result is recorded +as an `RC-*`. + +## Rules for every language + +1. **The existing code wins.** In a file or project that already has a + convention, follow it, even if the sub-skill says otherwise. Do not mix + styles within a file; do not reformat code you are not changing. +2. **Naming follows the language, not your habits.** Casing differs per + language (table below). Never carry one language's casing into another. +3. **A name says what, not how.** Name by purpose in the domain's language + (IT Professional English, as the registry's `Languages` section says), not by + type or implementation (`customer_list`, not `arr2`). +4. **Length follows scope.** Short names (`i`, `n`) only for tiny scopes; + wider scope, longer name. No abbreviations except ones the whole domain + uses (`id`, `url`, `http`). +5. **Booleans read as a question:** `is_valid`, `has_items`, `can_retry` + (cased per language). No negated names (`is_not_ready`). +6. **Functions are verbs, types are nouns.** A function that returns a value + without side effects may be a noun (`total`, `Total`) where the language + community does so. +7. **Formatting is done by the formatter,** not by hand and not in review + comments. Each sub-skill names the formatter and linter; commit its + configuration file with the code. +8. **Comments say why,** never what the code already says. Public APIs get + the language's documentation-comment form. +9. **No dead or commented-out code, no unexplained magic numbers.** Name the + constant. +10. **Errors are handled or propagated, never swallowed.** Each sub-skill says + how its language does this. + +## Casing at a glance + +| Element | Python | C | C++ | C# | +| --- | --- | --- | --- | --- | +| Type / class | `PascalCase` | `snake_case_t` | `PascalCase` | `PascalCase` | +| Function / method | `snake_case` | `snake_case` | `snake_case` | `PascalCase` | +| Variable / parameter | `snake_case` | `snake_case` | `snake_case` | `camelCase` | +| Constant | `UPPER_SNAKE` | `UPPER_SNAKE` | `kPascalCase` | `PascalCase` | +| Private member | `_leading` | file-scope `static` | `trailing_` | `_camelCase` | +| Namespace / module | `snake_case` module | `mod_` prefix | `snake_case` | `PascalCase` | +| File | `snake_case.py` | `snake_case.c/.h` | `snake_case.cpp/.h` | `PascalCase.cs` | + +The table is a summary; the sub-skill is authoritative. Shell (bash) is not in +the table: functions and variables are `snake_case`, constants and environment +variables `UPPER_SNAKE`, files `kebab-case.sh`. + +## Governance boundary + +Conventions are **defined and governed** here but **not enforced** by this +framework: writing a linter or CI job that enforces them is outside the +framework's scope. The formatter and linter names in each sub-skill are the +recommended tools, not a pipeline. A new or changed convention follows the +process in the project's Coding Standards Governance document and is recorded +in `framework/CHANGELOG.md`. No project data belongs in this skill. + +## Adding a language + +1. Add `references/.md` with these sections: Standard base, Naming, + Formatting, Language rules, Errors, Tests, Tooling. +2. Add `framework/qc/qc-.md` (`QC--001`) using the `QC` type + of the `artifact` skill, tagging every criterion with an ISO/IEC 25010:2023 + characteristic and a Level. +3. Add the row to the table above, the casing table, and a row for the + language in `framework/registry/artifact-catalog.md`; note it in + `framework/CHANGELOG.md`; run `bash framework/scripts/install-skills.sh`. diff --git a/.claude/skills/coding-conventions/references/c.md b/.claude/skills/coding-conventions/references/c.md new file mode 100644 index 0000000..14375ca --- /dev/null +++ b/.claude/skills/coding-conventions/references/c.md @@ -0,0 +1,71 @@ +# C conventions + +C has no single official style guide. This sub-skill fixes one consistent +style built on common practice; MISRA C and SEI CERT C are the references for +safety-critical or security-sensitive code. + +## Standard base + +ISO C11 or C17 as set by the project's compiler flag (`-std=c11`). Compile +with warnings on (`-Wall -Wextra -Wpedantic`); treat warnings as errors in +release builds. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Function, variable, parameter | `snake_case` | `parse_header`, `byte_count` | +| Public symbol | module prefix plus `snake_case` (C has no namespaces) | `stay_reader_open()` | +| File-local function / variable | `static`, no prefix needed | `static int next_token(...)` | +| Type (`struct`, `enum`, `typedef`) | `snake_case_t`, module prefix for public types | `stay_reader_t` | +| Enum constant, macro, constant | `UPPER_SNAKE` with the module prefix | `STAY_READER_OK` | +| Header / source file | `snake_case.h` / `snake_case.c`, same base name | `stay_reader.h` | +| Header guard | `MODULE_FILE_H` (or `#pragma once` if the project allows) | `STAY_READER_H` | + +- Do not use reserved identifiers: a leading underscore followed by an + uppercase letter, any double underscore, or a leading underscore at file + scope. +- POSIX reserves the `_t` suffix; keep the module prefix on public types so + they cannot collide. +- Macros are a last resort; prefer `static inline` functions and `enum` or + `const` values. + +## Formatting + +- One formatter configuration for the project (`clang-format`); 4 spaces (or + the project's setting), no tabs mixed in. +- Braces on every `if`, `else`, `for`, `while`, even for one statement. +- One declaration per line; declare variables at first use, initialised. +- Headers: include what you use, only what you use; public headers are + self-contained; add `extern "C"` guards when C++ code consumes them. + +## Language rules + +- Check every return value that can fail; check every allocation. +- Every `malloc`/`open`/`lock` has one clear owner and one matching release; + release on every exit path (single exit or `goto cleanup`). +- Use `size_t` for sizes and indices, fixed-width types (``) for + data layout, `const` wherever data is not modified, `restrict` only with + care. +- Bounds are explicit: pass a length with every buffer; use `snprintf`, + never `sprintf`, `strcpy` or `gets`. +- No undefined behaviour: no signed overflow, no out-of-range shifts, no use + after free, no uninitialised reads. +- Avoid global mutable state; if unavoidable, `static` and documented. + +## Errors + +Return a status code (an `enum`) or `-1`/`NULL` plus an error out-parameter; +document which in the header. Never ignore a failing call. Read `errno` +immediately after the failing call. + +## Tests + +A unit-test framework (for example Unity or CMocka); run under sanitizers +(`-fsanitize=address,undefined`) in at least one build. + +## Tooling + +`clang-format`, `clang-tidy` or `cppcheck`, compiler warnings, sanitizers. + +Review with `QC-CL-001`. diff --git a/.claude/skills/coding-conventions/references/cpp.md b/.claude/skills/coding-conventions/references/cpp.md new file mode 100644 index 0000000..8e164ab --- /dev/null +++ b/.claude/skills/coding-conventions/references/cpp.md @@ -0,0 +1,70 @@ +# C++ conventions + +## Standard base + +ISO C++17 or later as set by the project (`-std=c++20`); the C++ Core +Guidelines are the rule source. C++ has no official naming style, so the +style below is used unless the project already has another (rule 1 of the +overall skill). + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Class, struct, enum, concept, type alias | `PascalCase` | `StayReader`, `Stay` | +| Function, method, variable, parameter | `snake_case` | `read_stays()`, `byte_count` | +| Data member (private) | `snake_case` with trailing underscore | `buffer_` | +| Struct public data member | `snake_case`, no underscore | `check_in` | +| Constant (`constexpr`, namespace-scope `const`) | `kPascalCase` | `kMaxRetries` | +| Enum class value | `PascalCase` | `Status::NotFound` | +| Namespace | short `snake_case`; no `using namespace` in headers | `billing` | +| Template parameter | `PascalCase` | `typename ItemT` | +| Macro | `UPPER_SNAKE` with project prefix; avoid macros | `BILLING_ASSERT` | +| Header / source file | `snake_case.h` / `snake_case.cpp`, same base name | `stay_reader.h` | +| Header guard | `#pragma once` (or `PROJECT_PATH_FILE_H`) | | + +## Formatting + +- `clang-format` with one checked-in config; 4 spaces (or the project's + setting); braces on every control-flow body. +- Include order: matching header, project headers, third party, standard + library; each group sorted. +- One declaration per line; declare at first use, initialise with `{}`. + +## Language rules + +- **Ownership:** RAII everywhere. No owning raw pointers, no naked + `new`/`delete`; use `std::unique_ptr` by default, `std::shared_ptr` only for + real shared ownership, created with `std::make_unique`/`make_shared`. +- Follow the rule of zero; if you define one of destructor, copy or move, + define or delete all five. +- Pass by `const&` (large, read-only) or by value (small or sink); use + `std::span` and `std::string_view` for non-owning views, `std::optional` for + "maybe", `std::variant` for alternatives. +- `const` and `constexpr` by default; mark single-argument constructors + `explicit`; mark `override`/`final`; `[[nodiscard]]` on results that must + be used. +- Prefer algorithms and range-for over hand-written loops; `enum class` over + plain `enum`; `nullptr` over `NULL` or `0`; `using` over `typedef`. +- No C-style casts; use `static_cast` and friends. No mutable global state. +- Headers are self-contained and contain declarations, templates and + `inline` definitions only. + +## Errors + +Use exceptions for exceptional failures, or `std::expected` and error codes +where the project forbids exceptions; one choice per project. Destructors +never throw. Catch by `const&`; never `catch (...)` without rethrowing or +logging. + +## Tests + +GoogleTest, Catch2 or doctest; run under sanitizers (`address`, `undefined`) +in at least one build. + +## Tooling + +`clang-format`, `clang-tidy` with the Core Guidelines checks, compiler +warnings (`-Wall -Wextra -Wpedantic`), sanitizers. + +Review with `QC-CPP-001`. diff --git a/.claude/skills/coding-conventions/references/csharp.md b/.claude/skills/coding-conventions/references/csharp.md new file mode 100644 index 0000000..88abb3b --- /dev/null +++ b/.claude/skills/coding-conventions/references/csharp.md @@ -0,0 +1,72 @@ +# C# conventions + +## Standard base + +Microsoft's C# coding conventions and .NET Framework Design Guidelines, with +the analyzers that ship in the SDK. Target the language version of the +project's `LangVersion` / target framework. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Namespace | `PascalCase`, matches folder path | `Billing.Stays` | +| Class, struct, record, enum, delegate | `PascalCase` (nouns) | `StayReader` | +| Interface | `I` + `PascalCase` | `IStayReader` | +| Method, property, event, public field | `PascalCase` | `ReadStays()`, `CheckIn` | +| Constant, `static readonly` | `PascalCase` | `MaxRetries` | +| Enum value | `PascalCase`; `[Flags]` enums are plural | `Status.NotFound` | +| Parameter, local variable | `camelCase` | `byteCount` | +| Private / internal field | `_camelCase` | `_buffer` | +| Generic type parameter | `T` or `T` + `PascalCase` | `T`, `TKey` | +| Async method | ends in `Async` | `ReadStaysAsync()` | +| Exception, attribute | end in `Exception` / `Attribute` | `InvalidDateException` | +| Boolean | `Is`, `Has`, `Can` prefix | `IsActive` | +| File | the type's name, one top-level type per file | `StayReader.cs` | + +- Two-letter acronyms are upper case (`IO`); longer ones are `PascalCase` + (`Xml`, `Http`). + +## Formatting + +- `.editorconfig` checked in; `dotnet format` applies it. 4 spaces, Allman + braces, braces on every control-flow body. +- File-scoped namespaces (`namespace X;`); `using` directives outside the + namespace, `System` first. +- `var` when the type is obvious from the right-hand side, explicit type + otherwise. + +## Language rules + +- Enable nullable reference types (`enable`) and treat + nullable warnings as errors; do not suppress with `!` without a comment. +- `IDisposable` owners use `using`; implement the dispose pattern only when + needed. +- `async`/`await` all the way; no `.Result` or `.Wait()`; no `async void` + except event handlers; pass `CancellationToken` through public async APIs. +- Prefer properties over public fields, `readonly` and `init` for + immutability, `record` for value-like data, pattern matching and switch + expressions over long `if` chains. +- LINQ for queries, loops for side effects; do not enumerate a sequence twice. +- String interpolation over concatenation; `StringBuilder` in loops; + `DateTimeOffset` over `DateTime` for points in time; `decimal` for money. +- XML documentation comments (`///`) on public types and members. + +## Errors + +Throw specific exceptions (`ArgumentNullException`, custom types); validate +arguments at the public boundary (`ArgumentNullException.ThrowIfNull`). Catch +the narrowest type; `throw;` (not `throw ex;`) to rethrow. Never an empty +`catch`. + +## Tests + +xUnit, NUnit or MSTest; names like `Method_Condition_Expected`; one behaviour +per test; no dependence on order, time or the network. + +## Tooling + +`dotnet format`, the .NET analyzers (`AnalysisLevel`, `EnforceCodeStyleInBuild`) +and optionally StyleCop.Analyzers, configured in `.editorconfig`. + +Review with `QC-CS-001`. diff --git a/.claude/skills/coding-conventions/references/python.md b/.claude/skills/coding-conventions/references/python.md new file mode 100644 index 0000000..e262961 --- /dev/null +++ b/.claude/skills/coding-conventions/references/python.md @@ -0,0 +1,66 @@ +# Python conventions + +## Standard base + +PEP 8 (style), PEP 257 (docstrings), PEP 484 and later (type hints). Target +the Python version declared in `pyproject.toml`. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Package / module | short `snake_case` | `billing`, `stay_reader.py` | +| Class, exception | `PascalCase`; exceptions end in `Error` | `StayReader`, `InvalidDateError` | +| Function, method, variable, parameter | `snake_case` | `total_price`, `read_stays()` | +| Constant (module level) | `UPPER_SNAKE` | `MAX_RETRIES` | +| Internal (not public API) | one leading underscore | `_parse_row` | +| Type variable | short `PascalCase`; `_co` / `_contra` for variance | `T`, `KeyT`, `ItemT_co` | +| Boolean | `is_`, `has_`, `can_` prefix | `is_active` | +| Test file / function | `test_.py` / `test__` | `test_total_when_empty` | + +- Avoid name mangling (`__name`) unless you need it to prevent a subclass clash. +- Never use `l`, `O` or `I` as single-letter names. +- Do not shadow builtins (`list`, `id`, `type`); add a trailing underscore + (`type_`) only as a last resort. + +## Formatting + +- 4 spaces, no tabs. Maximum line length set once in the formatter config + (88 with `ruff format`; 79 if the project follows PEP 8 strictly). +- Imports at the top, grouped standard library / third party / local, one + blank line between groups, no wildcard imports. +- Double quotes for strings unless the formatter says otherwise. +- Trailing commas in multi-line literals and calls. + +## Language rules + +- Annotate every function signature (parameters and return, `-> None` too). + Modern syntax: `X | None`, `list[str]`. Avoid `Any` without a comment. +- `pathlib` over `os.path`; f-strings over `%` or `.format`; `enum` over + magic strings; `dataclass` (frozen where possible) over ad-hoc dicts. +- No mutable default arguments; no bare `except:`; no `print` for logging + (use `logging`). +- Use context managers (`with`) for files, locks and connections. +- Prefer comprehensions over `map`/`filter` with lambdas; keep them simple. +- Docstrings (PEP 257) on public modules, classes and functions; say what, + not how. + +## Errors + +Raise specific exceptions; catch the narrowest type; re-raise with +`raise ... from err` to keep the cause. Do not use exceptions for normal +control flow. + +## Tests + +`pytest`; one behaviour per test; `tmp_path` for files; fakes over mocks where +a simple fake is possible; tests do not depend on order or on the network. + +## Tooling + +`ruff` (lint and format) and `mypy --strict` (types), configured in +`pyproject.toml`. Architecture rules (layers, ports, dataframes) are in +`.agents/rules/python.md` and the `python-developer` agent; this sub-skill +covers naming and style only. + +Review with `QC-PY-001`. diff --git a/.claude/skills/coding-conventions/references/shell.md b/.claude/skills/coding-conventions/references/shell.md new file mode 100644 index 0000000..aa8a728 --- /dev/null +++ b/.claude/skills/coding-conventions/references/shell.md @@ -0,0 +1,62 @@ +# Shell conventions (bash) + +## Standard base + +Bash 4 or later, POSIX `test` semantics through `[[ ]]`. Start every script +with `#!/usr/bin/env bash` and `set -euo pipefail`. State the bash version and +the external tools it needs in the header comment. + +## Naming + +| Element | Convention | Example | +| --- | --- | --- | +| Script file | `kebab-case.sh`, executable | `check-plan.sh`, `new-artifact.sh` | +| Function | `snake_case`, a verb | `read_registry`, `die` | +| Local variable, parameter | `snake_case`, declared with `local` | `msgfile`, `changed` | +| Constant, environment variable | `UPPER_SNAKE` | `PLAN_GATE`, `PROJECT_ROOT` | +| Boolean | `is_` / `has_` prefix, value `0` or `1` | `is_enabled=1` | + +- Name a script and its functions by what they do, not how. +- Do not shadow a command with a function of the same name. + +## Formatting + +- 2 spaces, no tabs. One command per line; `then` and `do` on the same line as + `if` and `for`. Keep lines near 80 characters; break long pipelines at `|`. +- Format with `shfmt -i 2 -ci`; lint with `shellcheck`. Commit the project's + `.editorconfig` or `.shellcheckrc` with the code. + +## Language rules + +- **Quote every expansion** (`"$var"`, `"${arr[@]}"`); use arrays, not + space-separated strings, for lists. Use `[[ ]]`, not `[ ]`, and `$(...)`, + not backticks. +- Declare function variables `local`; no globals except constants. +- Parse options with `case` and `shift`, check required arguments with + `"${1:?usage: ...}"`, and print a usage line on bad input. +- Read input with `read -r`; iterate files with globs or `find -print0`, never + by parsing `ls`. +- Use `mktemp` for temporary files and remove them with `trap ... EXIT`; never + a fixed `/tmp` name. + +## Errors and exit codes + +- Errors go to standard error, start with `error:`, say what is wrong and what + to do, and end the script with a non-zero exit code (`die` helper). +- `0` is success, `1` a failed check or bad input, `2` a usage error; document + any other code in the header. +- Never swallow a failure with `|| true` without a comment that says why. + +## Safety + +- A script that changes state outside its own directory defaults to a dry run + or asks for an explicit flag (`--apply`, `--force`); say so in the header. +- Never echo a token or password, put one on a command line, or commit one; + read secrets from the environment or a gitignored file. +- Do not `eval` input; do not build a command from unvalidated text. + +## Tools + +Formatter `shfmt`, linter `shellcheck`, syntax check `bash -n`. These are the +recommended tools, not a pipeline: enforcing them in CI is outside this +framework's scope. diff --git a/.claude/skills/project-planning/SKILL.md b/.claude/skills/project-planning/SKILL.md new file mode 100644 index 0000000..561235c --- /dev/null +++ b/.claude/skills/project-planning/SKILL.md @@ -0,0 +1,283 @@ +--- +name: project-planning +description: Plan a project (or a new phase of one) in phases with tasks, and sync those phases and tasks to Gitea or GitHub as Milestones and Issues. Use when starting a new project, adding or updating a phase/gateway, breaking a phase down into tasks, deciding whether a task needs its own use case or user story, or asked to create/update project milestones or issues on the git host. +--- + +# Project Planning + +Plans a project as phases (gateways), each phase as a set of tasks, and +keeps those phases and tasks in sync with the git host's own project +management (Milestones and Issues). It builds on the `artifact` skill's +`PP` (Project Plan) and `MIL` (Milestone/Gateway) types. + +## Domain language first + +Before planning, find the Product Owner's (PO's) domain language. Take it from +the first of these that states it: + +1. the user's prompt; +2. a file the user included or that the project already has (the `Languages` + section of `docs/artifact-registry.md`). + +If none states it, **ask the user for it** and stop planning until it is +answered; never assume one. Record the answer in the registry's `Languages` +section. The artifact types the registry marks "Written in the PO language" +are written in it, once, with no translated copy. + +## Start here + +Before planning or building anything, check that the baseline exists (the +`docs/artifact-registry.md` rows and files): the Business Case (`BC`), the +Stakeholder Analysis (`SA`), the Project Plan (`PP`) and at least one +milestone (`MIL`). If one is missing, create it first, in that order, with +`new-artifact.sh` (it refuses when a type in the catalog's `Requires` column +is missing). + +Each of these steps ends with a review: the document counts only once its +review record (`RC-*`) says `Go` and its Version History row is `Accepted` +(`process/review-checklist-process.md`). Do not start a step on a document that +is still `Proposed`; ask for the review first. + +A "build X" request is planning-first. Produce the phases, tasks and issues, +show the dry run of `sync-project.sh`, and ask for a go-ahead before any code +is written; running `--apply` is the user's decision. Only the user can waive +the plan, in chat, for that request; the waiver does not carry over. The rule +is defined in `framework/process/plan-first-gate.md`. + +## The hierarchy + +``` +Business goal → Feature/requirement → Use case/user story → Tasks +``` + +The use case/user story explains **why** a feature exists; tasks explain +**how** the team implements it. Not every task needs a use case: + +- **Needs a use case/user story** — the task is something a user, or + another system, actually does: "User resets password", "Admin exports + customer report", "Payment service processes refund". +- **Plain task, no use case** — purely technical/implementation work: + "Refactor authentication middleware", "Add database index", "Upgrade + React version", "Fix null-pointer bug", "Add unit tests", "Configure CI + pipeline", "Optimize SQL query". + +When in doubt, ask: does this row describe a goal an actor is pursuing, or +a step the team takes to build something? Goals get a use case; steps stay +a plain task. + +## Use cases get their own folder + +Each use case lives in `docs/uc-NNN/` (`UC-001` → `docs/uc-001/`), together +with the artifacts that belong to it. Nothing about a use case goes in a +shared `docs/use-cases/` folder. + +1. **Create the folder and the use case:** + `bash framework/scripts/new-artifact.sh UC --file docs/uc-001/uc.md`. +2. **Create only the artifacts this use case affects,** in this order, each in + the same folder with a fixed file name. Skip any it does not touch (no + `erd.md` if nothing is stored; no `dm.md` if it adds no concept). + + | Order | Type | File | Create when the use case… | + | --- | --- | --- | --- | + | 1 | `SSD` | `ssd.md` | has system interaction to show (almost always) | + | 2 | `DM` | `dm.md` | introduces or changes domain concepts | + | 3 | `OC` | `oc.md` | has system operations that change state | + | 4 | `SD` | `sd.md` | needs a collaboration design for an operation | + | 5 | `DCD` | `dcd.md` | adds or changes design classes | + | 6 | `ERD` | `erd.md` | adds or changes persisted data | + + ```bash + bash framework/scripts/new-artifact.sh SSD --file docs/uc-001/ssd.md + bash framework/scripts/new-artifact.sh DM --file docs/uc-001/dm.md --cite UC-001=docs/uc-001/uc.md + ``` + + `new-artifact.sh` cites only the artifacts in the same use-case folder + (and project-level ones); for `DM`, `DCD` and `ERD`, add the use case and + sibling artifacts by hand with `--cite`. Each gets its own ID (the next + version in the registry, e.g. `DM-002`) and its own `RC-*` review. +3. **Reconcile with the project models.** The use-case artifacts are a scoped + view; the project-level `docs/domain-model.md`, `docs/dcd.md` and + `docs/erd.md` are the consolidated truth. When the use case's `DM`, `DCD` + or `ERD` are done, compare each with its project-level document: + - add the new concepts, classes, attributes and entities; change the ones + the use case modified; + - keep names identical to the existing ones, and resolve any conflict + with another use case's model instead of duplicating the element; + - give each project-level document a new `## Version History` row (Change: + which use case caused it), and create it from the first use case's + document if it does not exist yet; + - if nothing needs to change, say so in the use case's task and PR + description ("project DM/DCD/ERD unchanged: "). + +## Planning a project (or a new phase) + +1. **Phases are gateways.** Each phase is a `MIL-*` document (the `artifact` + skill's `MIL` type): purpose, deliverable, Go/No-Go criteria, dependencies, + ownership, target date. Create one with + `bash framework/scripts/new-artifact.sh MIL --file docs/milestones/mil--.md`. +2. **The plan schedules the phases.** `docs/project-plan.md` (the `artifact` + skill's `PP` type) lists every phase with its window and owner, and holds + the overall timeline diagram. Create it once with + `bash framework/scripts/new-artifact.sh PP`. +3. **Break each phase into tasks.** In the phase's `## Tasks` section, add + one row per task using the hierarchy rule above. Tasks that need a use + case or user story get one created first (`UC`/`US` types; a use case + follows "Use cases get their own folder" below), then are referenced from + the Tasks row; plain tasks just describe the work. +4. **Sync to the git host.** Run the sync tool (below) to create or update + the corresponding Milestones and Issues. Do this whenever a phase or its + tasks change — the tool is idempotent (matches by title, never creates a + duplicate). + +## Syncing to Gitea/GitHub + +```bash +bash framework/scripts/sync-project.sh # prints the plan only — no network calls +bash framework/scripts/sync-project.sh --milestone MIL-002 # limit to one phase +bash framework/scripts/sync-project.sh --apply # actually create/update on the git host +``` + +- **Default is a dry run**: it parses `docs/milestones/mil-*.md` and prints + what would be created or updated. Nothing is sent anywhere. +- **`--apply`** performs the real requests. It needs: + - **Gitea** (default — detected from `origin`'s hostname): `GITEA_TOKEN` + env var, a personal access token. This is an HTTP API call (milestones, + issues) — a deploy key cannot be used, since deploy keys only + authenticate SSH git transport (clone/fetch/push), not the REST API. + Scope the token to issues only if your Gitea version supports scoped + tokens, rather than a full-account one. + - **GitHub** (detected when `origin` is on github.com, or pass + `--host github`): the `gh` CLI, already logged in (`gh auth login`). +- **`--with-project`** additionally tries to create/attach a Kanban Project + board. This is best-effort: some Gitea versions (confirmed on 1.27.3) show + Projects/Kanban only in the web UI and expose **no REST API for it at + all**, so this always fails there — check your server's own + `https:///swagger.v1.json` for any `project` path if unsure. GitHub + Projects (v2) needs `gh` with the `project` scope. Either way the script + warns and continues; Milestones and Issues are unaffected. Where there's + no API, create the board by hand at `https://///projects`. +- Read the script's header comment for the full flag list, including + `--owner`, `--repo`, `--api-base` to override auto-detection. + +`--apply` is an outward-facing, hard-to-reverse action (it creates real +Milestones/Issues on the git host) — run it yourself once you're ready, or +ask explicitly for it to be run. + +## Branching before commits + +**Never commit, push or open a PR unless the user asks.** The user reviews +the working-tree changes first; finish the edits, summarise them and stop. +Everything below (branching, closing keywords, resolving commit links, the +PR) describes how to do those steps once the user has asked for them; it is +not permission to start them. + +Never commit directly on `main`. This is enforced locally by a pre-commit +hook (`framework/githooks/pre-commit`) once +`bash framework/scripts/install-git-hooks.sh` has been run in this clone — +run it once per clone; it points `core.hooksPath` at the versioned +`framework/githooks/` instead of the per-clone, untracked `.git/hooks/`. +The hook refuses the commit with a pointer back to this section; a rare, +deliberate exception can bypass it with `ALLOW_MAIN_COMMIT=1 git commit`. + +Before the first commit of a piece of work, create and switch to a new +branch, then commit there: + +```bash +git checkout -b +# ... commits ... +git push -u origin +``` + +- Name the branch for the gateway/task it covers, kebab-case, e.g. + `g2-kpi-bmc-bpmn` or `mil-002-kpi-baseline` — short enough to read in a + PR list, specific enough to say what it's for. +- Open a PR (`gh pr create` on GitHub, or the Gitea equivalent) instead of + merging straight to `main`; this project's own history is a merged PR + per phase (e.g. "Merge pull request 'Close G1 inception baseline...'"), + so keep following that pattern. +- This applies to every commit, not just gateway/task work — if in doubt + whether the current branch is `main`, check (`git branch --show-current`) + before committing. + +## Closing tasks from commits + +When a commit finishes the work for a task that `sync-project.sh` has +already synced as an Issue, reference that Issue number in the commit +message so Gitea/GitHub auto-closes it once that commit lands on the +default branch (`main`) — via the PR merge required by "Branching before +commits" above, not by pushing the closing keyword to a feature branch. +Use `Refs #N` instead when the commit only touches the task without +finishing it — that links the commit without closing. + +- **One issue per commit:** use `Closes #5`. +- **Several issues closed by the same commit:** put one `Closes #N` per + line, not a comma-separated list (`Closes #5, #6, #7` only closed `#5` on + a confirmed live Gitea instance — the comma form is not reliably parsed): + + ``` + Closes #5 + Closes #6 + Closes #7 + ``` +- Get each issue number either from the most recent `sync-project.sh` / + `sync-project.sh --apply` output (it prints `updated issue #N` / + `created issue #N` per task) or, if that's not at hand, from the git + host's issue list for the milestone. +- Match commits to issues by the task row they implement — one task row in + a `MIL-*` document's `## Tasks` section is one Issue, so a commit that + completes that row's work closes that Issue. +- Don't guess an issue number; if it isn't known from a recent sync or a + lookup, ask rather than omit or fabricate one. +- **After pushing a multi-issue closing commit, verify** each issue's state + came back `closed` (e.g. `GET /repos///issues/` with + `GITEA_TOKEN`) rather than assuming the whole list closed; close any that + didn't with a direct `PATCH .../issues/` `{"state":"closed"}` call. + +## Pull requests close the issues they complete + +Every PR must close the Issues its work finishes, so the board matches +reality once it merges. Before opening (or updating) a PR: + +1. **List the issues the branch completes.** Go through the task rows the + branch implements (`git log main..HEAD`, plus the `## Tasks` of the + `MIL-*` it belongs to) and get each Issue number as described in "Closing + tasks from commits". +2. **Put one closing line per issue in the PR description**, one per line, + never comma-separated: + + ``` + Closes #5 + Closes #6 + ``` + + Use `Refs #N` for an issue the PR only touches. A PR that finishes no + issue says so explicitly ("No issue closed: ") instead of saying + nothing. +3. **Ask, don't guess.** If an issue number is unknown or a task is only + partly done, ask rather than omit or invent one. Leave a partly done task + open and note what remains. +4. **After the merge, verify** that each issue is `closed` (the check in + "Closing tasks from commits") and close any stragglers by hand. Also make + sure the finished task rows are reflected in the `MIL-*` document and, when + every task of a phase is closed, that the Milestone is closed too. + +5. **Resolve pending commit links before the PR** (see "Version History rule" + in the `artifact` skill): commit, run + `bash framework/scripts/resolve-pending-commits.sh `, then + commit the result as a follow-up commit. +6. **Never ask for, offer or perform the merge.** A PR needs a reviewer: ask + the user to open the PR / request a review, and stop there. Merging, + enabling auto-merge and "shall I merge?" are not yours to do. + +Closing keywords in the PR description and in commit messages both count on +Gitea and GitHub; they only take effect when the PR merges into the default +branch. + +## Files this skill touches + +- `docs/project-plan.md` — `PP`, via the `artifact` skill. +- `docs/milestones/mil-*.md` — `MIL`, via the `artifact` skill, each with a + `## Tasks` section. +- `docs/uc-NNN/*.md` (the use case and its `SSD`, `DM`, `OC`, `SD`, `DCD`, `ERD`), `docs/user-stories.md` — only for tasks that need one; plus `docs/domain-model.md`, `docs/dcd.md`, `docs/erd.md` when reconciling. +- `framework/scripts/sync-project.sh` — never edited per project; propose + changes upstream in the framework. diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..a759dc9 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "framework"] + path = framework + url = ssh://git@git.tirsystem.com:10022/TirSystem/SQA-QC-Framework.git diff --git a/AGENTS.md b/AGENTS.md new file mode 100644 index 0000000..8437b2b --- /dev/null +++ b/AGENTS.md @@ -0,0 +1,60 @@ +# AGENTS.md + +This project uses the SQA and QC framework mounted at `framework/`. + +For any document under `docs/` (create, edit or review) use the `artifact` +skill. For planning a project or a phase into tasks, and syncing phases and +tasks to Gitea/GitHub as Milestones and Issues, use the `project-planning` +skill. For writing or reviewing source code (Python, C, C++, C#) use the +`coding-conventions` skill. Skills are read from `.agents/skills/` (this harness and Codex CLI) +and `.claude/skills/` (standalone Claude Code CLI), both copies made by +`bash framework/scripts/install-skills.sh` — re-run it after updating the +framework. + +**Never commit, push or open a PR unless asked.** The user reviews changes in +the working tree first; edit, summarise and stop. The commit/PR rules below +apply once a commit has been asked for. + +## Workflow order + +Business Case, Stakeholder Analysis, Project Plan, milestones, tasks synced as +issues, then code, each step reviewed before the next (an artifact is done when +its `RC-*` says `Go` and its row is `Accepted`; code is reviewed against its +`qc-programming-*` checklist before the pull request). Nothing goes under `src/` +or `tests/` unless a milestone document (`MIL-*`) is accepted, its latest review +is a `Go`, and the task is a row in it (ideally a synced issue). If those are missing, plan with the `project-planning` skill, show the +dry-run output of `bash framework/scripts/sync-project.sh`, and stop. The rule +is defined once in `framework/process/plan-first-gate.md`. + +A "build X" request is planning-first: produce the plan and issues, then ask +for a go-ahead. Only the user can waive the plan, in chat, for that request. +Before planning, find the Product Owner's language (the prompt, or the +`Languages` section of `docs/artifact-registry.md`); if neither states it, ask. +Each artifact type the registry marks "Written in the PO language" exists once, +in that language, under its normal name; there is no translated twin. Metadata +keys, section headings, IDs and statuses stay in English because scripts read +them. + +To enforce the gate at commit time, run +`bash framework/scripts/install-git-hooks.sh --enable-plan-gate`: a commit that +changes `src/` or `tests/` then needs a `Task: MIL-NNN#N` trailer for an +accepted, reviewed milestone. + +Rules that apply to every document: + +1. Get the short name from `framework/registry/artifact-catalog.md` and the + next version from `docs/artifact-registry.md`. Create files with + `bash framework/scripts/new-artifact.sh `. +2. Owners, reviewers and RACI use stakeholder IDs from the project's + Stakeholder Analysis, never invented role names. +3. Every QC criterion is tagged with an ISO/IEC 25010:2023 characteristic. +4. Every reviewed instance gets an `RC-*` record in `docs/sqa/reviews/`. +5. Do not edit `framework/` from this project; propose changes upstream. +6. Every PR description closes the issues its work completes, one + `Closes #N` per line (`Refs #N` for partial work); see the + `project-planning` skill. +7. Every document's `## Version History` has `Change` and `Commit` columns and + keeps the two latest rows. After committing, run + `framework/scripts/resolve-pending-commits.sh` and commit the result before + opening the PR (no amend). Never ask for or perform the merge: a reviewer + merges. diff --git a/framework b/framework new file mode 160000 index 0000000..ce1f9dd --- /dev/null +++ b/framework @@ -0,0 +1 @@ +Subproject commit ce1f9ddd3c7a8d32ff30a50755fdc777f5a0c183 -- 2.54.0 From b823d1f405ebac6b0198605edd9d802518529725 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Wed, 7 Oct 2026 23:49:16 +0800 Subject: [PATCH 2/4] Plan the Hirst painting project and review the plan documents Add the Business Case (BC-001), Stakeholder Analysis (SA-001), Project Plan (PP-001) and two milestones: MIL-001 Colour Palette and MIL-002 Spot Painting. Record their reviews as RC-001 to RC-004 (all Go, self-review accepted by S01). The PO language is English and the domain is IT. The milestones and their 13 tasks are synced as Gitea milestones 68 and 69 and issues #1 to #13. --- docs/artifact-registry.md | 58 ++++++ docs/business-case.md | 169 ++++++++++++++++++ docs/milestones/mil-001-palette.md | 83 +++++++++ docs/milestones/mil-002-painting.md | 86 +++++++++ docs/project-plan.md | 92 ++++++++++ docs/sqa/reviews/rc-001-business-case.md | 73 ++++++++ .../reviews/rc-002-stakeholder-analysis.md | 73 ++++++++ docs/sqa/reviews/rc-003-milestone-mil-001.md | 69 +++++++ docs/sqa/reviews/rc-004-milestone-mil-002.md | 69 +++++++ docs/stakeholder-analysis.md | 94 ++++++++++ 10 files changed, 866 insertions(+) create mode 100644 docs/artifact-registry.md create mode 100644 docs/business-case.md create mode 100644 docs/milestones/mil-001-palette.md create mode 100644 docs/milestones/mil-002-painting.md create mode 100644 docs/project-plan.md create mode 100644 docs/sqa/reviews/rc-001-business-case.md create mode 100644 docs/sqa/reviews/rc-002-stakeholder-analysis.md create mode 100644 docs/sqa/reviews/rc-003-milestone-mil-001.md create mode 100644 docs/sqa/reviews/rc-004-milestone-mil-002.md create mode 100644 docs/stakeholder-analysis.md diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md new file mode 100644 index 0000000..5881609 --- /dev/null +++ b/docs/artifact-registry.md @@ -0,0 +1,58 @@ +# Artifact Registry + +This project's artifact state. Types, short names and `CrossReference +Candidates` come from the framework catalog +(`framework/registry/artifact-catalog.md`); this file only records where +each document lives in *this* project and the next version to use. + +Delete rows for types you don't use. Add a row the first time you create a +document of a type. `Primary File` may contain a glob (e.g. +`docs/uc-*/uc.md`); `framework/scripts/find-crossreferences.sh` reads it. + +| Short Name | Artifact Type | Primary File | Next Available Version | +| --- | --- | --- | --- | +| BC | Business Case | docs/business-case.md | 002 | +| SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 | +| PP | Project Plan | docs/project-plan.md | 002 | +| MIL | Milestone / Gateway | docs/milestones/*.md | 003 | +| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 005 | + +## Languages + +Set the PO language when the project starts; `project-planning` asks for it +if it is missing. An artifact of a type marked "Written in the PO language" +exists once, in that language, under its normal name (`business-case.md`); the +`artifact` skill ("One file per artifact") has the rule. + +| Setting | Value | +| --- | --- | +| PO language | en | +| PO domain | it | +| High-level register | IT Executive English | +| Technical register | IT Professional English | + +Every artifact of a type marked "Yes" below states its language and domain in +its `Language` and `Domain` Metadata rows; `new-artifact.sh` fills them from +`PO language` and `PO domain`. `Language` is a BCP 47 code (`da`, `en`). +`Domain` is a value from this list; add a row to introduce a domain, so a +reviewer can see at once which professional vocabulary a document uses. + +| Domain | Meaning | +| --- | --- | +| it | Software and IT | +| medical | Healthcare and medical devices | +| construction | Construction and civil engineering | + +| Artifact types | Register | Written in the PO language | +| --- | --- | --- | +| BC, KPI, PP, MIL | IT Executive English | Yes | +| SA, BMC, BPMN, UCD, US, UC, SSD, DM, RA, GOV, DICT | IT Professional English | Yes | +| OC, SD, DCD, ERD, ADR, TM, RC, QC, source code | IT Professional English | No | + +## Notes + +- "Next Available Version" is the zero-padded (3-digit) version to use the + *next* time a new document of that type is created. Increment it only when + a brand-new document is created, not when an existing document's + `## Version History` gets a row. +- `ADR` uses 4 digits (`0001`); `RC` is sequential across all artifact types. diff --git a/docs/business-case.md b/docs/business-case.md new file mode 100644 index 0000000..47a23e0 --- /dev/null +++ b/docs/business-case.md @@ -0,0 +1,169 @@ +# Business Case: Hirst Painting (Spot Painting with Turtle) + +## Metadata +| Key | Value | +| --- | --- | +| ID | BC-001 | +| CrossReference | [SA-001] | +| Language | en | +| Domain | it | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | + +--- + +## Executive Summary + +Day 18 of the 100 Days of Code course asks for a Python program that draws a +Hirst-style spot painting with the standard `turtle` module: a 10 by 10 grid of +round dots, each coloured at random from a palette taken from a reference image +with the `colorgram` library. The work is small and self-contained. This +business case proposes to deliver it as one script in two phases (palette, then +painting) under the software quality assurance and quality control (SQA and QC) framework, so that the exercise doubles as a +proportionate practice run of the plan-first workflow. The proposal is to +proceed. + +## Methodological and Standards Foundation + +- **Method:** the framework's plan-first workflow (Business Case, Stakeholder + Analysis, Project Plan, milestones, tasks as issues, code), with analysis and + design artifacts in the style of Larman's *Applying UML and Patterns*; only + the artifacts the work needs are created. +- **Quality standards:** every quality criterion is tagged with an ISO/IEC + 25010:2023 characteristic. The code is reviewed against `qc-programming-python` + before the pull request. +- **Source requirements:** the course lecture "The Hirst Painting Project + Part 2 - Drawing the Dots" (a written summary of the lecture is the only + source supplied; the reference image is not). + +## Problem Statement + +The course exercise has no working implementation in this repository yet. +Without one, the loops, the `turtle` positioning logic and the colour handling +taught in the lecture stay theory. The lecture itself names the usual +failures: a visible trail between dots, a missing last dot, white shades in +the palette that make dots invisible, and a slow, visibly drawing turtle. + +## Business Opportunity + +A finished and reviewed implementation gives S01 verified +practice of loops, `random`, third-party library use and `turtle` positioning, +and it shows the plan-first workflow applied to a deliberately small project, +so the effort the framework adds can be judged against the size of the work. + +## Objectives + +| # | Objective | +| --- | --- | +| O1 | Draw a 10 by 10 grid of 100 dots, each of size 20, with the centres of neighbouring dots 50 units apart. | +| O2 | Build the colour palette from a reference image with `colorgram`, excluding white shades. | +| O3 | Colour every dot by a random choice from that palette. | +| O4 | Keep the finished painting on screen until the user clicks, and present it cleanly: turtle hidden, no trail between dots. | +| O5 | Deliver the work through the framework: plan and issues first, then code reviewed against `qc-programming-python` (`RC-*` verdict `Go`) before the pull request. | + +## Scope + +### In Scope + +- One Python program using the standard `turtle` module, the `random` module and the `colorgram` library. +- Extraction of the palette from one reference image, with white shades removed. +- Drawing of the 10 by 10 dot grid: dot size, spacing, row change and heading handling. +- Screen handling: `exitonclick`, hiding the turtle, pen up between dots, drawing speed. +- The framework documents the plan-first workflow requires for this work (Business Case, Stakeholder Analysis, Project Plan, milestones), and the review records for them and for the code. + +### Out of Scope + +- Any grid size, dot size or spacing other than the values in O1, and any user-configurable parameters. +- A graphical interface, command-line options, or saving the painting to an image file. +- Packaging or publishing the program as a library or application. +- Painting any image other than the dot grid. +- Use cases, domain models, class and database designs: the program has no persistent data and no user interaction beyond the exit click. + +## Expected Benefits + +### Tangible Benefits + +- A working program that produces the painting described in O1 to O4. +- A reviewed codebase with a recorded review (`RC-*`) against the Python checklist. +- A traceable record from objective to milestone to issue to code. + +### Intangible Benefits + +- Practice with loops, random selection, library use and `turtle` positioning. +- Experience of how much framework process a small project needs. + +## Strategic Alignment + +The project completes one exercise of the 100 Days of Code course, which is +S01's learning goal for this repository, and it exercises the SQA and QC +framework that the repository is built on. + +## Success Criteria + +| # | Criterion | Target | Measure | +| --- | --- | --- | --- | +| SC1 | Number and layout of dots (O1) | Exactly 100 dots, in 10 rows of 10 | Count of dot calls in a run, and visual check of the window | +| SC2 | Dot size and spacing (O1) | Dot size 20; 50 units between neighbouring centres, in rows and in columns | Constants in the code, and visual check | +| SC3 | Palette content (O2) | At least 2 colours, and 0 colours with red, green and blue all at 240 or above | Print of the extracted palette checked against the threshold (value confirmed by S01) | +| SC4 | Colour choice (O3) | Every dot colour is a member of the palette | Check in code review | +| SC5 | Clean result (O4) | Turtle hidden at the end; no line drawn between dots; window stays open until a click | Visual check of one full run | +| SC6 | Run time (O4) | The full painting is drawn in 30 seconds or less | Timed run on the developer machine (value confirmed by S01) | +| SC7 | Review gate (O5) | `RC-*` for the code has verdict `Go`; the pull request closes every issue it completes | Review record and pull request description | + +## Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | +| The reference image is not available to the project | Palette cannot be extracted from the source the lecture uses | Ask S01 for the image before the palette phase starts; if it stays unavailable, S01 decides on another image and the change is recorded in the milestone | +| `colorgram` or the `turtle` graphical toolkit (Tk) cannot be installed or does not run on the developer machine | The program cannot be run or checked | Check both in the first task of the palette phase, before any other work depends on them | +| White or near-white shades stay in the palette | Dots are invisible on the white background, so SC1 looks failed | SC3 sets an explicit threshold and is checked on the printed palette | +| Off-by-one errors: last dot of a row or the last row missing, or a trail drawn | SC1 or SC5 fails | SC1 counts the dots and SC5 checks for a trail; both are verified in the code review | +| Framework process outweighs the size of the work | Schedule slips and the exercise stops being a small one | Keep to two phases and plain tasks; no use cases or design artifacts (see Out of Scope) | +| The only named reviewer is also the author | Reviews are not independent, as the review process requires | S01 accepted a documented self-review on 2026-10-07; each review record states it | + +## Assumptions + +- The Product Owner (S01) is the author, the owner of every phase, and the person who accepts the work. +- Python 3 with Tk support is available on the developer machine, and `colorgram` can be installed with `pip`. +- The reference image is supplied by S01 from the course material. +- The lecture summary is a faithful statement of the required behaviour. +- The phases are completed within one week of the start date, 2026-10-07 (confirmed by S01). + +## Constraints + +- Only the standard `turtle` and `random` modules plus `colorgram` are used. +- The program is one source file under `src/`; its tests, if any, are under `tests/`. +- No code is written until a milestone is `Accepted`, has a review record with verdict `Go`, and the task is a row in it (plan-first gate). +- Nothing is committed, pushed or opened as a pull request unless S01 asks. +- Project documents are written in English (PO language `en`, domain `it`). + +## Cost–Benefit Assessment + +The assessment is qualitative on purpose: this is a learning exercise with no +revenue, so a monetary return would be invented. The cost is S01's +time; the tools are free. + +| Costs | Benefits | +| --- | --- | +| S01's time for the documents, reviews and code (hours, not tracked) | Working painting program meeting SC1 to SC6 | +| No licence or infrastructure cost (Python, `turtle`, `colorgram`) | Verified practice of the lecture's techniques | +| Overhead of the framework documents relative to a one-script program | Experience of the plan-first workflow at the smallest practical size | + +## Stakeholders + +Roles, power and interest are in [SA-001]; they are not repeated here. + +| Stakeholder ID (SA) | Interest in this project | +| --- | --- | +| S01 | Owner of all objectives (O1 to O5); accepts the work and answers the open questions above | + +## Recommendation + +Proceed — the work is small, the cost is only S01's time, and every objective can be verified against an explicit target. + +--- + +[SA-001]: ./stakeholder-analysis.md diff --git a/docs/milestones/mil-001-palette.md b/docs/milestones/mil-001-palette.md new file mode 100644 index 0000000..c1d7966 --- /dev/null +++ b/docs/milestones/mil-001-palette.md @@ -0,0 +1,83 @@ +# MIL-001: Colour Palette + +## Metadata +| Key | Value | +| --- | --- | +| ID | MIL-001 | +| CrossReference | [BC-001] | +| Language | en | +| Domain | it | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | + +--- + +## Purpose + +This gate decides whether the colour palette is usable, so that the painting +phase can build on it. The palette must come from the reference image through +`colorgram`, must contain no white shades, and the toolchain it depends on +(`colorgram` and the `turtle` graphical toolkit) must work on the developer +machine. + +## Deliverable + +A palette function in the program's source file under `src/` that reads the +reference image with `colorgram`, drops white shades and returns the remaining +colours as RGB tuples, together with the tests that check it and the printed +palette of the real image. + +## Go / No-Go Criteria + +| # | Criterion (objectively checkable) | Go | No-Go | +| --- | --- | --- | --- | +| 1 | `import colorgram` and `import turtle` succeed and a `turtle` window opens on the developer machine | Both imports succeed and the window opens | An import fails or no window opens | +| 2 | The palette of the reference image has at least 2 colours (SC3) | 2 or more colours printed | 0 or 1 colour | +| 3 | The palette has no colour with red, green and blue all at 240 or above (SC3) | 0 such colours in the printed palette | 1 or more such colours | +| 4 | The tests of the white-shade filter and of the palette pass | All tests pass | Any test fails | +| 5 | The phase code has a review record against `qc-programming-python` with verdict `Go` (SC7) | `RC-*` verdict `Go` | `Go-with-conditions` or `No-Go`, or no record | + +## Dependencies + +| Depends on | Reason | +| --- | --- | +| None | The baseline documents are `Accepted`; this is the first gateway | + +## Traceability + +| Business Case objective / KPI / user story | Reference | +| --- | --- | +| O2: palette from a reference image with `colorgram`, without white shades | [BC-001]; criteria 1 to 4 and SC3 | +| O5: delivery through the framework, code reviewed against `qc-programming-python` | [BC-001]; criterion 5 and SC7 | + +No use case or user story applies: every task is a technical step, so the +Tasks column "Needs its own Use Case/User Story?" says `No` throughout. + +## Ownership + +| Role | Stakeholder ID (SA) | +| --- | --- | +| Owner | S01 | +| Approving reviewer | S01 | + +## Target Date + +2026-10-09 — inside the one-week limit of [BC-001] (2026-10-07 to 2026-10-14), and leaves five days for the painting phase. + +## Tasks + +| # | Task | Summary | Needs its own Use Case/User Story? | Reference | +| --- | --- | --- | --- | --- | +| 1 | Check the toolchain | Confirm that Python 3 with Tk (the graphical toolkit behind `turtle`) runs `import turtle` and opens a window, install `colorgram` with `pip`, and record the versions. The Business Case lists a missing toolchain as a risk, so this comes before any other work depends on it. | No | | +| 2 | Write the white-shade filter | Write a function that drops every colour whose red, green and blue are all at or above 240; the threshold is a named constant (SC3 of the Business Case). It needs no image, so it can be written and tested first. | No | | +| 3 | Add tests for the white-shade filter | Add tests under `tests/` that feed the filter synthetic colours (pure white, near-white, an ordinary colour) and check which are kept. They pin down the threshold before the real image is involved. | No | | +| 4 | Get the reference image into the project | S01 supplies the reference image the lecture uses; store it in the repository and note its path. Without it the palette cannot be extracted, and the location is an open issue of the Project Plan. | No | | +| 5 | Write the palette extraction and check the palette | Write a function that reads the image with `colorgram`, turns each extracted colour into a red-green-blue (RGB) tuple, applies the filter and returns the list (O2). Print the palette of the real image and check it against SC3: at least 2 colours and no white shade. The painting phase imports this function. | No | | +| 6 | Review the phase code against qc-programming-python | Check the code of this phase against `framework/qc/qc-programming-python.md` and record the result as an `RC-*`; a `Go` verdict is Go criterion 5 (SC7). | No | | + +--- + +[BC-001]: ../business-case.md diff --git a/docs/milestones/mil-002-painting.md b/docs/milestones/mil-002-painting.md new file mode 100644 index 0000000..b3004a4 --- /dev/null +++ b/docs/milestones/mil-002-painting.md @@ -0,0 +1,86 @@ +# MIL-002: Spot Painting + +## Metadata +| Key | Value | +| --- | --- | +| ID | MIL-002 | +| CrossReference | [BC-001] | +| Language | en | +| Domain | it | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | + +--- + +## Purpose + +This gate decides whether the program draws the finished painting as the +Business Case requires: a 10 by 10 grid of 100 dots of size 20 spaced 50 units +apart, each coloured at random from the palette, left on screen until a click, +with the turtle hidden and no trail between dots, and drawn in 30 seconds or +less. + +## Deliverable + +The runnable program in the single source file under `src/`, using the palette +function of the colour palette phase, together with its tests and one timed run +that shows the finished painting. + +## Go / No-Go Criteria + +| # | Criterion (objectively checkable) | Go | No-Go | +| --- | --- | --- | --- | +| 1 | The painting has exactly 100 dots in 10 rows of 10 (SC1) | 100 dots counted in the tests and in the window | Any other count, or a missing last dot or row | +| 2 | Dot size is 20 and neighbouring centres are 50 units apart in rows and in columns (SC2) | Values match in the code, the tests and the window | Any other size or spacing | +| 3 | Every dot colour is a member of the palette (SC4) | The test passes for all 100 dots | A colour outside the palette | +| 4 | The turtle is hidden at the end, no line joins the dots, and the window stays open until a click (SC5) | All three seen in one full run | A visible turtle, a trail, or a window that closes by itself | +| 5 | One full run draws the painting in 30 seconds or less (SC6) | 30 seconds or less by stopwatch | More than 30 seconds | +| 6 | The phase code has a review record against `qc-programming-python` with verdict `Go` (SC7) | `RC-*` verdict `Go` | `Go-with-conditions` or `No-Go`, or no record | + +## Dependencies + +| Depends on | Reason | +| --- | --- | +| MIL-001 | The painting draws with the palette function that phase delivers, and starts only after it has a `Go` decision | + +## Traceability + +| Business Case objective / KPI / user story | Reference | +| --- | --- | +| O1: 10 by 10 grid of dots, size 20, spacing 50 | [BC-001]; criteria 1 and 2, SC1 and SC2 | +| O3: random colour from the palette for every dot | [BC-001]; criterion 3, SC4 | +| O4: painting kept on screen and presented cleanly | [BC-001]; criteria 4 and 5, SC5 and SC6 | +| O5: delivery through the framework, code reviewed against `qc-programming-python` | [BC-001]; criterion 6, SC7 | + +No use case or user story applies: every task is a technical step, so the +Tasks column "Needs its own Use Case/User Story?" says `No` throughout. + +## Ownership + +| Role | Stakeholder ID (SA) | +| --- | --- | +| Owner | S01 | +| Approving reviewer | S01 | + +## Target Date + +2026-10-14 — the last day of the one-week limit of [BC-001] (2026-10-07 to 2026-10-14). + +## Tasks + +| # | Task | Summary | Needs its own Use Case/User Story? | Reference | +| --- | --- | --- | --- | --- | +| 1 | Set up the screen and the turtle | Create the screen and the turtle, switch to the 255-level red-green-blue (RGB) colour mode so the palette tuples work, hide the turtle and lift the pen. This removes the cursor and the trail from the result (O4, SC5). | No | | +| 2 | Compute the dot positions | Write a function that returns the 100 dot centres for 10 rows of 10 with 50 units between neighbours, starting from a corner chosen so the whole grid fits in the window. It draws nothing, so tests can check the count and the spacing without a window (O1, SC1, SC2). | No | | +| 3 | Draw the dots with random palette colours | Move to each position with the pen up and draw a dot of size 20 coloured by `random.choice` over the palette from the colour palette phase (O1, O3). The last dot of every row and the last row must be drawn, which the lecture names as a common mistake. | No | | +| 4 | Keep the window open until a click | End the program with `exitonclick` so the finished painting stays on screen until the user clicks (O4, SC5). | No | | +| 5 | Speed up the drawing | Set the turtle speed, or turn off screen updates until the end, so the full painting is drawn in 30 seconds or less, and time one full run (SC6). | No | | +| 6 | Add tests for the geometry and the colours | Add tests under `tests/` that check 100 positions in 10 rows of 10, a spacing of 50 in rows and in columns, and that every colour picked is in the palette (SC1, SC2, SC4). | No | | +| 7 | Review the phase code and check the painting | Check the code of this phase against `framework/qc/qc-programming-python.md` and record the result as an `RC-*`; then run the program once and check criteria 1 to 5 against it (SC7). | No | | + +--- + +[BC-001]: ../business-case.md diff --git a/docs/project-plan.md b/docs/project-plan.md new file mode 100644 index 0000000..a94d35a --- /dev/null +++ b/docs/project-plan.md @@ -0,0 +1,92 @@ +# Project Plan: Hirst Painting + +## Metadata +| Key | Value | +| --- | --- | +| ID | PP-001 | +| CrossReference | [BC-001], [SA-001], [MIL-001], [MIL-002] | +| Language | en | +| Domain | it | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | + +--- + +## Purpose + +This plan schedules the two phases of the Hirst painting project, the colour +palette and the spot painting, inside the one-week limit that [BC-001] sets +(2026-10-07 to 2026-10-14). Each phase is a gateway with its own milestone +document, and each gateway ends with a Go/No-Go decision by S01. + +## Planning Assumptions + +- Week 1 starts 2026-10-07; the plan ends by 2026-10-14, per the Business Case assumption confirmed by S01. +- The baseline documents ([BC-001], [SA-001] and their review records) are finished on 2026-10-07, before the first phase starts. +- Phase length: two to five days, because the work is one small script. The palette phase is shorter because it is the smaller of the two. +- S01 owns both phases and takes both Go/No-Go decisions; S01 is the only stakeholder in [SA-001], so communication follows the review gates in that document. +- No user stories exist: the program has no user interaction beyond the exit click, so the Stories column is empty. + +## Gateway Schedule + +| Gateway | Document | Window | Decision date | Owner | Stories | Main deliverable | Milestone | +| --- | --- | --- | --- | --- | --- | --- | --- | +| Colour palette | [MIL-001] | 2026-10-07 to 2026-10-09 | 2026-10-09 | S01 | none | Palette function that returns the reference image's colours without white shades | [MIL-001 milestone] | +| Spot painting | [MIL-002] | 2026-10-10 to 2026-10-14 | 2026-10-14 | S01 | none | Program that draws the 10 by 10 painting and meets SC1 to SC7 of [BC-001] | [MIL-002 milestone] | + +```plantuml +@startgantt +Project starts 2026-10-07 +[Colour palette] starts 2026-10-07 and ends 2026-10-09 +[Colour palette Go/No-Go] happens 2026-10-09 +[Spot painting] starts 2026-10-10 and ends 2026-10-14 +[Spot painting Go/No-Go] happens 2026-10-14 +@endgantt +``` + +## Scope Coverage + +| Business Case scope item | Gateway | +| --- | --- | +| One Python program using `turtle`, `random` and `colorgram` | [MIL-001] (`colorgram` part), [MIL-002] (`turtle` and `random` part) | +| Palette extracted from one reference image, white shades removed | [MIL-001] | +| Drawing the 10 by 10 dot grid: size, spacing, row change, heading | [MIL-002] | +| Screen handling: `exitonclick`, hidden turtle, pen up, drawing speed | [MIL-002] | +| Framework documents and review records for the plan-first workflow | Finished before the first gateway ([BC-001], [SA-001], this plan, [MIL-001], [MIL-002] and their reviews); the review of each phase's code is a task in that phase | + +## Dependencies + +``` +Baseline documents → [MIL-001] Colour palette → [MIL-002] Spot painting +``` + +[MIL-002] needs the palette function from [MIL-001]. A No-Go on [MIL-001] moves +the start of [MIL-002] by the days needed to rework and re-review; the +one-week limit then has no slack, so S01 decides whether to extend it. + +## Plan Risks + +| Risk | Impact | Mitigation | +| --- | --- | --- | +| The reference image is not supplied before the palette phase starts | [MIL-001] cannot start its extraction task; the window of two to three days is lost | The tasks that do not need the image (toolchain check, white filter and its tests) come first; S01 decides on another image if this one stays unavailable | +| A No-Go on the palette phase | [MIL-002] starts late and the one-week limit is missed | The palette phase is the smaller one and ends 2026-10-09, leaving five days for the painting phase | +| Review effort is large compared with the code | Time goes to documents instead of the program | Two phases, plain tasks, no use cases or design artifacts, as set out in [BC-001] | +| The Project Plan has no quality checklist | The plan is accepted on S01's judgement alone | S01 checks the plan against the one-week limit of [BC-001] and the Go/No-Go criteria of both milestones | + +## Open Issues + +- **Reference image:** its location is still unknown. S01 supplies it before task 4 of [MIL-001]. +- **Project Plan review:** the Project Plan has no quality checklist yet, so it is `Accepted` when S01 says so in chat (done on 2026-10-07); no review record is written for it. +- **Milestone links:** `sync-project.sh --apply` ran on 2026-10-07 and created the two Milestones and issues #1 to #13 on the git host; the Milestone column links to them. + +--- + +[BC-001]: ./business-case.md +[SA-001]: ./stakeholder-analysis.md +[MIL-001]: ./milestones/mil-001-palette.md +[MIL-002]: ./milestones/mil-002-painting.md +[MIL-001 milestone]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/milestone/68 +[MIL-002 milestone]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/milestone/69 diff --git a/docs/sqa/reviews/rc-001-business-case.md b/docs/sqa/reviews/rc-001-business-case.md new file mode 100644 index 0000000..7da30e5 --- /dev/null +++ b/docs/sqa/reviews/rc-001-business-case.md @@ -0,0 +1,73 @@ +# Review Record: Business Case BC-001 + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-001 | +| CrossReference | [BC-001], [SA-001], [QC-BC-001], [QC-LANG-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Self-review and defaults confirmed by S01 | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [BC-001] +- Checklist used: [QC-BC-001], together with [QC-LANG-001] because the Business Case is written in the PO language +- Scope: full review +- Language and domain: en / it (the artifact's Metadata rows) +- Language reviewer: none. S01 reads English and knows the IT domain. S01 is also the author; S01 accepted a documented self-review on 2026-10-07 (Action Item 1) + +**Status of this record:** final. The assistant read the document against +every criterion and recorded what it found; S01 confirmed the open points on +2026-10-07 and the verdict is `Go`. + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | ROI/Cost-Benefit analysis is quantitative, or where qualitative, is explicitly justified | Pass | Cost–Benefit Assessment states it is qualitative "on purpose: this is a learning exercise with no revenue, so a monetary return would be invented", and lists costs and benefits | +| 2 | Risks are identified with documented impact and mitigation | Pass | Risks table has 6 rows; each has an Impact and a Mitigation | +| 3 | Success criteria are measurable, stating explicit targets rather than vague aspirations | Pass | SC1 to SC7 each have a target and a measure. SC3 (colour threshold 240) and SC6 (30 seconds) were confirmed by S01 on 2026-10-07 (Action Item 3) | +| 4 | Scope explicitly separates In Scope vs Out of Scope | Pass | `### In Scope` and `### Out of Scope` subsections, with distinct content | +| 5 | Stakeholders are cross-referenced to Stakeholder Analysis IDs rather than re-described inline | Pass | Stakeholders table cites S01 and says roles are in [SA-001]. Prose refers to S01 by ID, not by role | +| 6 | Methodology and quality-standard foundation are stated explicitly (e.g. ISO/IEC 25010, Larman) | Pass | "Methodological and Standards Foundation" names the plan-first workflow, Larman, ISO/IEC 25010:2023 and `qc-programming-python` | +| 7 | Assumptions and constraints are explicit and clearly distinguished from one another | Pass | Separate `## Assumptions` and `## Constraints` lists. The one-week timeline is an assumption, confirmed by S01 on 2026-10-07 (Action Item 3) | +| 8 | Document supports executive decision-making with a clear, unambiguous recommendation | Pass | Recommendation is a single "Proceed" with a one-sentence rationale | + +## Language and Domain Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | The Metadata table has a `Language` row and a `Domain` row, and neither is a placeholder | Pass | The `Language` row says `en` and the `Domain` row says `it`; `check-languages.sh --list` reports both | +| 2 | `Language` is a BCP 47 code and `Domain` is a value from the registry's domain list | Pass | `en` is a BCP 47 code; `it` is in the registry's domain list ("Software and IT") | +| 3 | The content (prose and table cells) is written in the stated language | Pass | All prose and table cells are English; tool names (`turtle`, `colorgram`, `exitonclick`) are established technical terms | +| 4 | The register matches the one the registry gives for the artifact type | Pass | Registry gives IT Executive English for BC. The text states decisions, targets and risks; implementation detail was removed from the risks table during drafting. Tool names stay where an objective needs them | +| 5 | Domain terms are the PO terms of the domain's dictionary, with no synonyms | Pass | The project has no Domain Dictionary (`DICT`), so there is no list to check against; the terms *dot*, *palette*, *painting*, *grid* and *phase* are used consistently here and in [SA-001]. "Spot painting" is the name of the genre; the elements are always "dots". S01 confirmed on 2026-10-07 that a dictionary is not needed (Action Item 2) | +| 6 | Metadata keys, section headings, IDs and statuses are in English | Pass | Headings follow the BC reference exactly | +| 7 | No translated twin (`..md`) exists beside the document | Pass | Only `docs/business-case.md` exists | +| 8 | A change of language or domain since the previous accepted version has a Version History row and was reviewed again | N-A | First version; there is no previous accepted version | +| 9 | A reviewer competent in the domain, and in the language, has confirmed that the domain terms are used correctly | Pass | S01 confirmed on 2026-10-07 that the domain terms are used correctly (Action Item 2). S01 is also the author; the self-review was accepted by S01 (Action Item 1) | +| 10 | Abbreviations are spelled out on first use, in the stated language | Pass | SQA and QC are spelled out in the Executive Summary; ISO/IEC, SC and O numbering are defined where introduced | + +## Overall Verdict + +Go — every criterion of [QC-BC-001] and of [QC-LANG-001] passes (criterion 8 of [QC-LANG-001] is N-A for a first version). S01 accepted the documented self-review, confirmed the defaults and the stakeholder list, and confirmed on 2026-10-07 that the domain terms are used correctly and that no Domain Dictionary is needed. All action items are closed. + +## Action Items + +| # | Action | Owner | Due | +| --- | --- | --- | --- | +| 1 | Name a reviewer who is not the author, or record here that the review is a documented self-review accepted by S01. **Closed 2026-10-07:** S01 accepted a documented self-review | S01 | 2026-10-09 | +| 2 | Confirm that the domain terms in [BC-001] are used correctly (QC-LANG-001 criterion 9), and that a Domain Dictionary is not needed for this project. **Closed 2026-10-07:** S01 confirmed both points | S01 | 2026-10-09 | +| 3 | Confirm or change the defaults in [BC-001]: one-week timeline from 2026-10-07, colour threshold 240 (SC3), 30-second run time (SC6); say where the reference image is. **Closed 2026-10-07 for the defaults:** S01 confirmed all three. The reference image location is still open; it is a risk of [BC-001] and goes to the Open Issues of the Project Plan | S01 | 2026-10-09 | + +--- + +[BC-001]: ../../business-case.md +[SA-001]: ../../stakeholder-analysis.md +[QC-BC-001]: ../../../framework/qc/qc-business-case.md +[QC-LANG-001]: ../../../framework/qc/qc-language-domain.md diff --git a/docs/sqa/reviews/rc-002-stakeholder-analysis.md b/docs/sqa/reviews/rc-002-stakeholder-analysis.md new file mode 100644 index 0000000..4a81961 --- /dev/null +++ b/docs/sqa/reviews/rc-002-stakeholder-analysis.md @@ -0,0 +1,73 @@ +# Review Record: Stakeholder Analysis SA-001 + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-002 | +| CrossReference | [SA-001], [BC-001], [QC-SA-001], [QC-LANG-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
S02 removed on S01's instruction; self-review confirmed by S01 | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [SA-001] +- Checklist used: [QC-SA-001], together with [QC-LANG-001] because the Stakeholder Analysis is written in the PO language +- Scope: full review +- Language and domain: en / it (the artifact's Metadata rows) +- Language reviewer: none. S01 reads English and knows the IT domain. S01 is also the author; S01 accepted a documented self-review on 2026-10-07 (Action Item 1) + +**Status of this record:** final. The assistant read the document against +every criterion and recorded what it found; S01 confirmed the open points on +2026-10-07 and the verdict is `Go`. + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | Power/Interest grid is filled for every stakeholder, with no gaps or unclassified entries | Pass | S01, the only stakeholder, is classified HIGH/HIGH, Manage Closely; the quadrant follows the standard grid | +| 2 | Each stakeholder is assigned a unique, stable ID (e.g. S01-S11 style) reusable for RACI assignments in other artifacts | Pass | S01, unique; [BC-001] and the review records already cite it. S02 was removed on S01's instruction before any review gave `Go`, so no ID was reused | +| 3 | Roles and organizational context are defined with explicit Power and Interest levels, not just narrative description | Pass | Role and Organization columns filled, with explicit levels | +| 4 | Communication needs (channel, frequency, deliverable type) are mapped to project phases or milestones | Pass | Mapped to the review gates of the workflow, since no milestone document exists yet. The mapping to `MIL-*` IDs can be added in a later version once they exist | +| 5 | Conflicting stakeholder interests are identified with documented mitigation or resolution strategies | Pass | 3 conflicts, each with a mitigation. The reviewer-independence conflict is mitigated by S01's accepted self-review (Action Item 1) | +| 6 | Stakeholder concerns are explicitly traced to Business Case objectives | Pass | Business Goal Alignment table traces S01's concerns to O1 to O5 of [BC-001]; the objective numbers match the Business Case | +| 7 | Primary concerns are expressed in both business language and a recognized quality-attribute mapping (e.g. FURPS+) | Pass | Every concern has a FURPS+ attribute; the acronym is spelled out in the Purpose | +| 8 | Document is understandable and navigable by non-technical stakeholders reviewing their own entry | Pass | Short, plain table entries; S01 has a single row to read | + +## Language and Domain Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | The Metadata table has a `Language` row and a `Domain` row, and neither is a placeholder | Pass | The `Language` row says `en` and the `Domain` row says `it`; `check-languages.sh --list` reports both | +| 2 | `Language` is a BCP 47 code and `Domain` is a value from the registry's domain list | Pass | `en` is a BCP 47 code; `it` is in the registry's domain list ("Software and IT") | +| 3 | The content (prose and table cells) is written in the stated language | Pass | All prose and table cells are English; tool names are established technical terms | +| 4 | The register matches the one the registry gives for the artifact type | Pass | Registry gives IT Professional English for SA; the text uses the grid, FURPS+ and RACI terms without over-explaining them | +| 5 | Domain terms are the PO terms of the domain's dictionary, with no synonyms | Pass | The project has no Domain Dictionary (`DICT`), so there is no list to check against; the terms *dot*, *palette*, *painting*, *grid* and *phase* match [BC-001]. S01 confirmed on 2026-10-07 that a dictionary is not needed (Action Item 2) | +| 6 | Metadata keys, section headings, IDs and statuses are in English | Pass | Headings follow the SA reference exactly | +| 7 | No translated twin (`..md`) exists beside the document | Pass | Only `docs/stakeholder-analysis.md` exists | +| 8 | A change of language or domain since the previous accepted version has a Version History row and was reviewed again | N-A | First version; there is no previous accepted version | +| 9 | A reviewer competent in the domain, and in the language, has confirmed that the domain terms are used correctly | Pass | S01 confirmed on 2026-10-07 that the domain terms are used correctly (Action Item 2). S01 is also the author; the self-review was accepted by S01 (Action Item 1) | +| 10 | Abbreviations are spelled out on first use, in the stated language | Pass | FURPS+ and RACI are spelled out in the Purpose; S01 and BC-001 are IDs defined by the document set | + +## Overall Verdict + +Go — every criterion of [QC-SA-001] and of [QC-LANG-001] passes (criterion 8 of [QC-LANG-001] is N-A for a first version). S01 accepted the documented self-review, confirmed the defaults and the stakeholder list, and confirmed on 2026-10-07 that the domain terms are used correctly and that no Domain Dictionary is needed. All action items are closed. + +## Action Items + +| # | Action | Owner | Due | +| --- | --- | --- | --- | +| 1 | Name a reviewer who is not the author, or record here that the review is a documented self-review accepted by S01 (the same decision as RC-001 Action Item 1). **Closed 2026-10-07:** S01 accepted a documented self-review | S01 | 2026-10-09 | +| 2 | Confirm that the domain terms in [SA-001] are used correctly (QC-LANG-001 criterion 9), and that a Domain Dictionary is not needed for this project. **Closed 2026-10-07:** S01 confirmed both points | S01 | 2026-10-09 | +| 3 | Confirm that S01 and S02 are the only stakeholders, or name others (for example a second reviewer). **Closed 2026-10-07:** S01 is the only stakeholder; S02 was removed from [SA-001] and [BC-001] | S01 | 2026-10-09 | + +--- + +[SA-001]: ../../stakeholder-analysis.md +[BC-001]: ../../business-case.md +[QC-SA-001]: ../../../framework/qc/qc-stakeholder-analysis.md +[QC-LANG-001]: ../../../framework/qc/qc-language-domain.md diff --git a/docs/sqa/reviews/rc-003-milestone-mil-001.md b/docs/sqa/reviews/rc-003-milestone-mil-001.md new file mode 100644 index 0000000..e44d23f --- /dev/null +++ b/docs/sqa/reviews/rc-003-milestone-mil-001.md @@ -0,0 +1,69 @@ +# Review Record: Milestone MIL-001 + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-003 | +| CrossReference | [MIL-001], [BC-001], [QC-MIL-001], [QC-LANG-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [MIL-001] +- Checklist used: [QC-MIL-001], together with [QC-LANG-001] because the milestone is written in the PO language +- Scope: full review +- Language and domain: en / it (the artifact's Metadata rows) +- Language reviewer: none. S01 reads English and knows the IT domain. S01 is also the author; S01 accepted a documented self-review on 2026-10-07 (see [BC-001] review record, Action Item 1) + +**Status of this record:** final. The assistant read the milestone against +every criterion and recorded what it found; S01 confirmed the open point on +2026-10-07 and the verdict is `Go`. + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable: the palette function in the source file under `src/`, its tests and the printed palette of the real image. It is a tangible output, not a date | +| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Five criteria, each with a Go and a No-Go column and a check that can be run (imports, colour count, white threshold 240, tests, review record) | +| 3 | Dependencies on other milestones are explicitly mapped | Pass | Dependencies table says "None" with the reason: it is the first gateway and the baseline documents are `Accepted` | +| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability table maps O2 and O5 of [BC-001] to the criteria and to SC3 and SC7 | +| 5 | Milestone owner and approving reviewer are identified | Pass | Ownership table gives S01 for both; the shared identity is the accepted self-review | +| 6 | Milestone has a defined target date consistent with project constraints | Pass | 2026-10-09, inside the one-week limit of [BC-001] (2026-10-07 to 2026-10-14) | + +## Language and Domain Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | The Metadata table has a `Language` row and a `Domain` row, and neither is a placeholder | Pass | The `Language` row says `en` and the `Domain` row says `it`; `check-languages.sh --list` reports both | +| 2 | `Language` is a BCP 47 code and `Domain` is a value from the registry's domain list | Pass | `en` is a BCP 47 code; `it` is in the registry's domain list ("Software and IT") | +| 3 | The content (prose and table cells) is written in the stated language | Pass | All prose and table cells are English; tool names are established technical terms | +| 4 | The register matches the one the registry gives for the artifact type | Pass | Registry gives IT Executive English for MIL. Purpose, Deliverable and the Go/No-Go table state decisions and checks. The Tasks table is more technical (`import turtle`, `pip`) because the milestone reference requires each summary to be understood as an Issue without opening the file. S01 confirmed this reading on 2026-10-07 (Action Item 1) | +| 5 | Domain terms are the PO terms of the domain's dictionary, with no synonyms | Pass | S01 confirmed on 2026-10-07 that no dictionary is needed; the terms *dot*, *palette*, *painting*, *grid* and *phase* match [BC-001] | +| 6 | Metadata keys, section headings, IDs and statuses are in English | Pass | Headings follow the MIL reference exactly | +| 7 | No translated twin (`..md`) exists beside the document | Pass | Only `docs/milestones/mil-001-palette.md` exists | +| 8 | A change of language or domain since the previous accepted version has a Version History row and was reviewed again | N-A | First version; there is no previous accepted version | +| 9 | A reviewer competent in the domain, and in the language, has confirmed that the domain terms are used correctly | Pass | S01 confirmed on 2026-10-07 that the domain terms are used correctly (Action Item 1). S01 is also the author; the self-review was accepted by S01 | +| 10 | Abbreviations are spelled out on first use, in the stated language | Pass | SC3, SC7, O2 and O5 are defined in [BC-001] and cited with it; RGB is spelled out at its first use in task 5, and Tk is explained in task 1 as the graphical toolkit behind `turtle` | + +## Overall Verdict + +Go — every criterion of [QC-MIL-001] and of [QC-LANG-001] passes (criterion 8 of [QC-LANG-001] is N-A for a first version). S01 accepted the documented self-review and confirmed on 2026-10-07 that the domain terms are used correctly and that the technical wording of the Tasks table is acceptable. The action item is closed. The milestone may now be synced to the git host and its tasks may start (plan-first gate). + +## Action Items + +| # | Action | Owner | Due | +| --- | --- | --- | --- | +| 1 | Confirm that the domain terms are used correctly (QC-LANG-001 criterion 9), and that the technical wording of the Tasks table is acceptable in a milestone of register IT Executive English. **Closed 2026-10-07:** S01 confirmed both points | S01 | 2026-10-08 | + +--- + +[MIL-001]: ../../milestones/mil-001-palette.md +[BC-001]: ../../business-case.md +[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md +[QC-LANG-001]: ../../../framework/qc/qc-language-domain.md diff --git a/docs/sqa/reviews/rc-004-milestone-mil-002.md b/docs/sqa/reviews/rc-004-milestone-mil-002.md new file mode 100644 index 0000000..7407437 --- /dev/null +++ b/docs/sqa/reviews/rc-004-milestone-mil-002.md @@ -0,0 +1,69 @@ +# Review Record: Milestone MIL-002 + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-004 | +| CrossReference | [MIL-002], [BC-001], [QC-MIL-001], [QC-LANG-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: [MIL-002] +- Checklist used: [QC-MIL-001], together with [QC-LANG-001] because the milestone is written in the PO language +- Scope: full review +- Language and domain: en / it (the artifact's Metadata rows) +- Language reviewer: none. S01 reads English and knows the IT domain. S01 is also the author; S01 accepted a documented self-review on 2026-10-07 (see [BC-001] review record, Action Item 1) + +**Status of this record:** final. The assistant read the milestone against +every criterion and recorded what it found; S01 confirmed the open point on +2026-10-07 and the verdict is `Go`. + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | A concrete deliverable is defined for every gate | Pass | Deliverable: the runnable program in the source file under `src/`, its tests and one timed run showing the finished painting | +| 2 | Explicit Go/No-Go criteria are stated for each gate | Pass | Six criteria, each with a Go and a No-Go column and a check that can be run (dot count, size and spacing, palette membership, clean result, 30 seconds, review record) | +| 3 | Dependencies on other milestones are explicitly mapped | Pass | Dependencies table names MIL-001 and the reason: the painting draws with the palette function that phase delivers | +| 4 | Each milestone is traceable to a Business Case objective or KPI | Pass | Traceability table maps O1, O3, O4 and O5 of [BC-001] to the criteria and to SC1, SC2 and SC4 to SC7; SC3 belongs to MIL-001 | +| 5 | Milestone owner and approving reviewer are identified | Pass | Ownership table gives S01 for both; the shared identity is the accepted self-review | +| 6 | Milestone has a defined target date consistent with project constraints | Pass | 2026-10-14, the last day of the one-week limit of [BC-001] (2026-10-07 to 2026-10-14). There is no slack; the Project Plan names this as a risk | + +## Language and Domain Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | The Metadata table has a `Language` row and a `Domain` row, and neither is a placeholder | Pass | The `Language` row says `en` and the `Domain` row says `it`; `check-languages.sh --list` reports both | +| 2 | `Language` is a BCP 47 code and `Domain` is a value from the registry's domain list | Pass | `en` is a BCP 47 code; `it` is in the registry's domain list ("Software and IT") | +| 3 | The content (prose and table cells) is written in the stated language | Pass | All prose and table cells are English; tool names are established technical terms | +| 4 | The register matches the one the registry gives for the artifact type | Pass | Registry gives IT Executive English for MIL. Purpose, Deliverable and the Go/No-Go table state decisions and checks. The Tasks table is more technical (`random.choice`, `exitonclick`) because the milestone reference requires each summary to be understood as an Issue without opening the file. S01 confirmed this reading on 2026-10-07 (Action Item 1) | +| 5 | Domain terms are the PO terms of the domain's dictionary, with no synonyms | Pass | S01 confirmed on 2026-10-07 that no dictionary is needed; the terms *dot*, *palette*, *painting*, *grid* and *phase* match [BC-001] | +| 6 | Metadata keys, section headings, IDs and statuses are in English | Pass | Headings follow the MIL reference exactly | +| 7 | No translated twin (`..md`) exists beside the document | Pass | Only `docs/milestones/mil-002-painting.md` exists | +| 8 | A change of language or domain since the previous accepted version has a Version History row and was reviewed again | N-A | First version; there is no previous accepted version | +| 9 | A reviewer competent in the domain, and in the language, has confirmed that the domain terms are used correctly | Pass | S01 confirmed on 2026-10-07 that the domain terms are used correctly (Action Item 1). S01 is also the author; the self-review was accepted by S01 | +| 10 | Abbreviations are spelled out on first use, in the stated language | Pass | SC1 to SC7 and O1 to O5 are defined in [BC-001] and cited with it; RGB is spelled out at its first use in task 1 | + +## Overall Verdict + +Go — every criterion of [QC-MIL-001] and of [QC-LANG-001] passes (criterion 8 of [QC-LANG-001] is N-A for a first version). S01 accepted the documented self-review and confirmed on 2026-10-07 that the domain terms are used correctly and that the technical wording of the Tasks table is acceptable. The action item is closed. The milestone may now be synced to the git host and its tasks may start (plan-first gate). + +## Action Items + +| # | Action | Owner | Due | +| --- | --- | --- | --- | +| 1 | Confirm that the domain terms are used correctly (QC-LANG-001 criterion 9), and that the technical wording of the Tasks table is acceptable in a milestone of register IT Executive English. **Closed 2026-10-07:** S01 confirmed both points | S01 | 2026-10-08 | + +--- + +[MIL-002]: ../../milestones/mil-002-painting.md +[BC-001]: ../../business-case.md +[QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md +[QC-LANG-001]: ../../../framework/qc/qc-language-domain.md diff --git a/docs/stakeholder-analysis.md b/docs/stakeholder-analysis.md new file mode 100644 index 0000000..103f01a --- /dev/null +++ b/docs/stakeholder-analysis.md @@ -0,0 +1,94 @@ +# Stakeholder Analysis: Hirst Painting + +## Metadata +| Key | Value | +| --- | --- | +| ID | SA-001 | +| CrossReference | [BC-001] | +| Language | en | +| Domain | it | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | + +--- + +## Purpose + +This analysis identifies who has a stake in the Hirst painting program and +classifies each stakeholder by power and interest, so that other artifacts can +cite stable stakeholder IDs for ownership, review and RACI (responsible, +accountable, consulted, informed). It follows the power/interest grid and maps +each concern to a FURPS+ attribute (functionality, usability, reliability, +performance, supportability, plus further constraints). The project is a +one-person learning exercise, so the list has a single entry on purpose; a +stakeholder is added only when someone or something actually shapes the work. + +## Stakeholder Summary Table + +| ID | Name | Role/Title | Organization | Power Level | Interest Level | Quadrant | Primary Concern (Business Language) | +| --- | --- | --- | --- | --- | --- | --- | --- | +| S01 | Jens Tirsvad Nielsen | Product Owner, developer and reviewer | Tirsvad (individual) | HIGH | HIGH | Manage Closely | Finish the Day 18 exercise as a working, reviewed painting program that behaves as the lecture teaches, and learn from it without more process than the work needs | + +## Power/Interest Classification Rationale + +- **Manage Closely (S01):** S01 decides scope, accepts every artifact, owns + every phase and is the only person who works on the project, so power and + interest are both high. S01 is consulted at every review gate. +- **Other quadrants:** no stakeholder falls in Keep Satisfied, Keep Informed or + Monitor. The course lecture is the source of the required behaviour, but it + is a document, not a stakeholder: it is cited as the requirements source in + [BC-001] and takes no part in the project. + +## Primary Concerns and FURPS+ Mapping + +| ID | Concern | FURPS+ attribute | +| --- | --- | --- | +| S01 | The program draws the painting correctly and as the lecture teaches: 100 dots, right size and spacing, palette without white, random colours | Functionality | +| S01 | The result looks clean and the window stays open until a click | Usability | +| S01 | The drawing finishes quickly enough to not be tedious | Performance | +| S01 | The code is readable and passes the Python checklist | Supportability | +| S01 | The process stays proportionate to a one-script project | Supportability | + +## Communication Requirements + +| ID | Channel | Frequency | Deliverable | Phase / Milestone | +| --- | --- | --- | --- | --- | +| S01 | Chat with the assistant, working-tree review | At each review gate: Business Case and Stakeholder Analysis, Project Plan, each milestone, the code | Documents in `docs/`, review records `RC-*`, dry-run output of `sync-project.sh` | Every review gate before the next step starts | +| S01 | Pull request on the git host | Once, when S01 asks for it | Pull request closing the completed issues | Code review before the pull request | + +## Conflicting Interests and Mitigations + +With one stakeholder there is no conflict between people; the conflicts below +are tensions inside S01's own interests, and one process conflict. + +| Conflict | Stakeholders | Mitigation | +| --- | --- | --- | +| S01 wants to practise the full framework workflow but also to finish a small exercise quickly | S01 | Two phases only, plain technical tasks, no use cases or design artifacts; the Business Case names this as a risk and as Out of Scope | +| S01 may want to change values from the lecture (dot size, spacing, thresholds) while the Business Case fixes them as targets | S01 | S01 owns the decision; a change is made through a new Version History row of [BC-001] and a re-review | +| The framework asks for a reviewer who is not the author, but S01 is the only person on the project | S01 | S01 accepted a documented self-review on 2026-10-07, recorded in the review records of [BC-001] and this document; the assistant never marks a document `Accepted` itself | + +## Traceability Analysis + +### Business Goal Alignment + +| Stakeholder | Concern | Business Case objective | +| --- | --- | --- | +| S01 | Correct painting, as the lecture teaches | O1, O2, O3 of [BC-001] | +| S01 | Clean, quick result | O4 of [BC-001] | +| S01 | Reviewed, proportionate delivery | O5 of [BC-001] | + +Actor mapping: S01 is the only actor, as the person who runs the program and +clicks the window to close it. The program has no other user, so no use cases +are modelled; this is recorded as out of scope in [BC-001]. + +## Sign-Off + +Pending. Sign-off is recorded by the review record (`RC-*`) of this document. +The assistant does not mark the document `Accepted`. + +--- + +[BC-001]: ./business-case.md -- 2.54.0 From b7119ef907b1ae56d7aa231a460aeab8fa2f2e11 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Wed, 7 Oct 2026 23:49:38 +0800 Subject: [PATCH 3/4] Add the colour palette: white-shade filter and palette extraction MIL-001 tasks 1 to 6. - Pin the toolchain and tool configuration in pyproject.toml: Python 3.13, colorgram.py 1.2.0, and ruff, mypy (strict) and pytest as dev tools. - Add is_white_shade and remove_white_shades (threshold 240) with tests that pin the boundary at 239 and 240. - Add extract_palette, which reads the reference image with colorgram and drops white shades, with tests on synthetic images and on the reference image. - Add the reference image assets/20260524_132700.jpg and a note on it. - Record the code review against QC-PY-001 as RC-005 (verdict Go). Closes #1 Closes #2 Closes #3 Closes #4 Closes #5 Closes #6 Task: MIL-001#1 Task: MIL-001#2 Task: MIL-001#3 Task: MIL-001#4 Task: MIL-001#5 Task: MIL-001#6 --- assets/20260524_132700.jpg | Bin 0 -> 172725 bytes assets/README.md | 5 + docs/artifact-registry.md | 2 +- docs/sqa/reviews/rc-005-code-mil-001.md | 73 ++++++++++++++ pyproject.toml | 53 +++++++++++ src/hirst_painting.py | 43 +++++++++ tests/test_hirst_painting.py | 120 ++++++++++++++++++++++++ 7 files changed, 295 insertions(+), 1 deletion(-) create mode 100644 assets/20260524_132700.jpg create mode 100644 assets/README.md create mode 100644 docs/sqa/reviews/rc-005-code-mil-001.md create mode 100644 pyproject.toml create mode 100644 src/hirst_painting.py create mode 100644 tests/test_hirst_painting.py diff --git a/assets/20260524_132700.jpg b/assets/20260524_132700.jpg new file mode 100644 index 0000000000000000000000000000000000000000..1171d94c26fd5649ab443d32e3476b1eb5b39366 GIT binary patch literal 172725 zcmbSxQ*dpOpJ+b+qN~C*qYdOW@1h<$<1&5pYB@s>3-dNcdcF3 zy{l_K?5?ltbM12vfGQ^?D+K@p0|3Cj4#4L=z*^GF#u5OKm8Ao~0ssJT04o?c0Om`g z`f4^2kpGzhn7-P7kalvivbXrp3Xli`fczR^VPj)~NCXF9e#r{|nS6nbg^i1kg_Dns zgOrt>kBys;g$3}r2?zy1fP=&Te*ggq9{%fuhX;dzgoK8IhKGiRM?pqFMnOSE`2rHs z|AenA(*N+^*#8rs{Qxvrh+=RA2rzO0I2srP8rbJBfaoh4P+(v9pZi}uGz26R3@kVp z9Nh6(@&2S7qXLO?@6fAwH!VBipF07!H)C}>tu7z}b%HWL?EOp5PGMPf}i>}sY1 zzxJ_QgKvT2$;EKk>dkJ03plux9A+UYC8;fv8V3?z&m;r`fJ6LWQ$j$3LqUVV0ARng zqG$jx2ncWpC>RKECRo8h1}EPwHO+<#%{B`!bXEbNSW5r2U8_C*V^D|C(gk zg0PX#b*c)eq$WERYKVe(JQDX2XG93`$(=+{7STc0K_qhseU0CZF%U~H)DmwrH37ed zjR^Oh^HY&|PMD}m{WN?Lp=a{GT`6G)sY|6RHX*&Nf>sku|j7se_$>djhm-Z^aVZNOu#uo}}^x{Z)& zzxUJPL}{15aK4Lb2yZ^!6}(A`bk~M4vjb&wB|6No{i$L|V$`2kS9n z^^`eIx_@P;j*3`|i?GE)@z2*li>Z^We=L?Tv#U4W6umn}YCzKP!M4vccEoO*LplB} zSV&$`W^{zY$RSe4mvROayQ11$mp5fWmb03X9mqvEOkKoE5>1xt!Aw^rp+Cbf-`seQQJ544YbLXV zZ^9Y}a9(1CN-;+VaNA#%ho*nR+bHQV6qyspzL!tSh_vCilUIe+r-)8$LA+j7J1uto zLYEF|OQ^qGqQH+YFW>sUKWIs*f8xQ;Hk7J6W$v0Zal(;yk!}EqyQs(@qsv-qYb9hl zDkT6q^k%rd8$&w4lf{0|0uil@xk)|=eo&H+n0^9|8!&rEtxcAny*SEK`KV?sOUXkf z?WW?_Jy4~JrwxM1q-_ptPa~5Rb{q+7t=*6JCq-#x{*h{Q&BjrXT*I0{$i13x;&Ux| zw&jh{>y1OaZJfNQ+j!bp-ERup1&(P3y>uX@FwGD)(V}!Z0C`@kU^d?d`V^N;kKY7_ zydxtU0SiTOO`EQP4aebryCcUgB^;GUdLt|kQ8;T!TWj3Aox7$5E%7EwCeUdHYu^Kw z7kB#v_13!#C>10=0hNBy6^CYu{QYNI<*PagbCwP0pyyigS=PTyq{!N*&p9S8*uc~2 zFm(g#PXL3|=*#)Y4*bd1v1r1AbJH{SZ6IsXv}JE)5F`Hwt@ulJnm@xfk~;~fL3@7( znU(%2WHNWX%5lVa4tS3$^?fhizcTMUG!nYj+J=)0C;!L>|67o&6>L77>w=}EEIWG5 z2mDLIkual(**yws?6*Y%oePTDsfRMWk5s=K?1(s))En$I_v`nj6^TorzjNuOw$6CI z?Qhyy{fRoPGcbbqpFiF|0s6dij-_ZCfhKPz74-5Z1FudA>(dj&Dbc*FWC|S?a{fh~ zsH~+WFG?x@e4S#qGZP44MuhNzQ!*t!IDdTucFWm_UFmwAcsRO?7k_CiH_vus9HL%7 z40O=kVr2_`n8Ggz$E+1(bP=H#*(2O4^sOZw$7cOH8M`Gb{{(cn7z7I2+m?dezEpqP zzX*)K>5!u64w_b52M96-6+LhS=rsDloeX8LSyE0(W=7K!cjHh1pe~kbD+R?C=M4v% zp`q{e?R438)A4N1-i;{ketZR9$u*Fve>7dWr&N_A_C)!Q!8gnXYd!zbr;eucXd8XP z!NuULdGY$XpD1jTuM;Ve%31sCR#O~}5~4BFvLs7c{&|o#XXNZUMs%+!^+q7P5)?{XsxveMm@xC!PzjrZ{byV4C)F7%7 zUv0#UXt95FsHGFK%Y?xKC?~y=MtQjG7>5q`=dGvvi*i^gVCTORXPHnf$;^5}NEk!@ z6bDoY-55mJd)`ot5S3y$d~D7C?&TI9l9CfBK0QzvpO4`X_Kck(TT%s8(qK;`So3ML zb=1HF@W04sE5v%2*~iWiFV^eu$;l@o%s8IYS8+W>Dz>On%X>};(cBUXBA72*W;I9G zw~&QJH)qG0+aPM4%5oL0{aS%8T>G~tP3xln@cO%F1XFeNnB685rMKvtPB8i=G_rwC z(Gtf6<+&#gj4fB^ZdKRls?@THtH?!x8*>nTr}7R8hIjem35N~5EWB(fYr-^ZmRXU= zR!!ixV`lhGM5J@%i>qj61}ioCG!CE2G&yiL4Ffik1UZ#7_?mRhNeBB#7{!v%Rw2f+ zcmim0lEQWcvI)Ra=E_jrvQ6YltT3fHFv{wMO?2TAzr~F=R6mV9U{L*DT0>72Wae1i zAEcRzu~1uxVB^*(Rdn-CV}92sCl0U= z-dpQN+Rf+Q6CaG?l4i*j)31_FGl^s$m1W>8q|jL^wTGiadca}LzX@g-^_+2}PDYa` zinmonm&``kkoiGZ8UvDXuJGqmx?mM%bP*z8ved8Ch=25cHHQ|2USlKk##49O9s!+8 zL&b{+TsHV61{6S22h|i1@P5gcKLK0sxwzDiG zq&A{E+vwYO&s>NOIuQKsk58t;Qc7J@+6HC!cP!lK0Qi<9cp^Q#M$$XcM z+EKccvj=o4mN>e_Gx?vA{JPqov?MLB7;?4q`R{wIAiWjostDkyO#bcvl5X=5E{F4D zx#4GJW2yn9I>q`l_d<;9N;n8 zU#bh+dAjOQe?x#e$hC@${+mMK|LglvXS$SQ2%+?#=OZDtFuGvl4&g5|oq3(0$}6UV zi8Rp!9J-Qr{bnb5x z?Q4#8y~8@e2QGWn_T_jp{R7XVO0*%Ky6dJ){hERJQ?bZ|KfaCh4D(7V_mSV}H{d## z8ZsZ$8{5uJiQOFuPPJHHgg&1KYaKqXJ`4z1eXq|3Ai`VndhMY9In2DEU4%@GBMX?i z<)AJn^Qw2qYAqbTG71H28U&3?*dxP_aqz=3+r_wrq2W-;8 z%qT-1WD;lhp6ZpZBtr5tuSg1{`y_}yvb_W2Ka1#zJkNvSg3#wHlf(=K&ZPsGxOkH% zLDvbu6QVN{S#q~ojHcLN3(xupYwGyvh=hy^%W(| zZUuxLl-s~HB75}l7}gBM7$6XGH~yuEwm;jSqtg0@ikI1{CR`>@oZlh`cgFDDm$)O^ zYFHSRX9%{_Cf&v?U6hcexXLDP?F?R#L{(50zA(M9UGjX6nCGZeYX9AdVMb3nTXNOx z1@v%E&!Wqueq`q}f|GsofpM|Vd&n7_j|9VE-~5d6=8bJ6 z($a)9Fl?ul-U-2YHLLx}Jv0<>mvl0RdVu#w*n2vumLAH2BvL+kvQ{k2)8ZjmU99j+ zC5vuh&NU6DDAb^JEVYSo0dPN$Q-*UZ^7`dR5VFe6^+su%35<+zZ~kk^1M6IO1vW&? zIc1F0m_X^W%4;1PB7cT080S7zPdyeKtZb|#EG70T3+1KpC!&|PaFrCF(@RquFC*t2 zlzBP?*wG!w1X!agr32lYT2TB2CI1w;5l%*-WDlb#7Nl?AnkniLi3EgP<&f-7c_?$z z7c>6k)N%O<;i@kmMQ4RTXG5{zzKM3${<}h!piQDO*}-i>(Q2 zch9-DR3(V`h3nvbCzQlq7e2xQjnn^=h>fKA}iVMA!34glEe=k zZ?YDz;k=bXT1TDy(4VBGfmfdZ1&X{J)n3nj&yg2?@Cr5`X*G`-KZ0z2==||Ik`#wCOo+Rx1l) zk0Ex+XT=-Zk^hS0;g9|V*efc}@A1cp|2X`$-!fnWs`BlEKRy&_usI!YLfa0yi0G_) zdb)f)U`}Uuyc@$#p~KelTD7#E5BJdeK-XG%L+4+i=w&~xBCi%EN8U>?&=8~uQY#743||P zGqBGmv#dI{))vtfhGQDedb*wFhiS4WH}~^5Qk?=0siLlcUlum_WQzG){#dL+>CX-E6kDiA9WO>!H?domrcC0H7V1kAU|YBIa~R`3Y~=N) zPrr;odP5ADhtR25NAu#rV=YZ7(zBG&jILkwV>4)xb~?og$lg-I`Jv=1E^a};=hgse$L2j7(yazarkorPZ_ zmHoZ#5#1gfb8T4WMlUKKJ$VMx&}a-i%ituQ)$lv+QjxN`xGjM*96U_defX_@xFuq% z%jrR~`mx17Rja0m@~mI_j|QKhY>v51N3ES%;3mL=lzR>5_?{iH6> zv1C54CK$CCt%U(;dvP{?5aT(|W3Tb7B-_Yv5ng*}nsp1^FPX@pH;#*JA1nu8BK>rA z@#r%m0dXM7G29Gw!6_x!G~r@dbJsZ89ovbB91zc(6tf|8*NPPw!JAc%IC7|%MRNg`<4U|#1^9lPw6$!+cGFlQ zLqa!P!FZE`Ns~V*oKCbNcp2WmArqp@;3+73>h@qLIebB)P0PfCO9Z$RO?H;s`V-kZ zQ6B+As2+}etwW*&R{A%R0$=77Qa>##8GO;W#CQrCE6NdPO2pCe_T|(xp>P+8BwN{O z7u_Z4U}oLrM~>j1r9MxF!Wdj9iiUYpa7U`^T|ySKrNg?=xXJxYBQXA17qCsFQTxO1 z>A3%%RYTT`5LD@9XQ&8|_IiO65qR!A_c(+mK5kNK$rj<{?1Ze2mwrlPX=A4`5a!w6 z=<8#7MJFSWD5p%V1VnHXEO8wYMi%C*Rn|LePD4RnPdx#8LU2`|c#LU$9bgUQT{&rBf14U!$WvagQ29hj(LR^ZyGv0Rho zOT_5Y^s_gSxj)^-;Jt#DS0OOj(HnXqzedK74C;wiY`sgd3@&DSO&4T&WMkkSP=+~5 z%xU_W4!qZAKx-VaPpe(F{9Suk6p-^XAQIww%~0Tqz(xPN6!#+1VG7E*UD#la+t#9K zi{~aq1{|xUD1;1+PJXluW5ww?)^E=XdhieI7tUdK^sIy~;3RHvAwP3-`q{AO`5$+t z=i4OLizY=rhH`3X9)0v?-Zr94@QxOig^apL1h0ns1!aMso=NqdPV7`@bK6Dmh}pRI z`X3lv$+(lPMd0a+ue+O3L_}`I#@H>gkA~~N7u$Yuj*YM$tjA-(OP^S@;Jr3qx<-k5 zsE9sZaE0QxHQGoKx&T9uizW8+^Qkt>tosdUCQi;>%#5SQUPS0*v;nMAhE{3W+J2*| zveSt9rle&_5>7=k87pvVGgUE6d+zGa0uwVXt#gp?a-!*zQpx7;!2nQrp(>^1vaJ|{ zMf^Y}Jye^J2LYic_5G`EudAxS{)9f|y^Hy%Hfypq#d4-~Vvl2cXy*G+i3(qVQd`O5 z0jdCd@>(w_vJB)J!qqf5{mc%P{zK1xQxKu$oLd4t1Nx+kBB}*X|22R*{}#Y4YUKbYxB*F+z*C(wot!VjVnUBdxO3Di~SnBF?3&T9`%r9pY7K3fON@ z4S@osbkOBIdKv5f82eHsq916HC}=OAfRqeRpu3>m=4mi75`wpC(^biQsc;?aMa0b+RAhO6U8*bAMqA>#v zYvie{L>dR5W0$2I=mC;EUxZz|AK@s`n%JY{CN1AvB8AfFl?g@13=cDcxsU5D6Rz$= zf%#VH(`KghmN*rk0F~+=;n3bKk)&lJSgKRte0eh%;ppZM^PE*3n_n~v|Le*pK)i1a z^VEJZv4;4$MeAQX$>m=&D1maVGrfE)c8Ag{?26;DR`IpU;fKUnq&wD1^1ewH@yzM% z6@fcZE7TuwJqFBQPEt0wJDqf@N1aw?ljP&xop)irkN^9*iLub%9aXb+5MO&9SZpbn zst+n~(W!a&+0Wibgg*29>+!`S=_6$j*_}r+dF+Yl0M($>Ci0!%JYH^3rqc(;)`PRQn zd1sX!F0ms?wR-#saLj+92*tEmu7YPv7I|b!hfyKE)Qy-%gl`3Z3)^tpYSK1BKGE2B zhI0jF9nLaf<*a~{)8{FCC&ViA;M1|4hq9x5_L$j0K2g&T|K>eDaT1I8nrfTId2T|> z?Bh6O2YfZZ5K8RS4#IT>oUkV${_a{kyqkhbGLZu4A+_f95;FVz zmn&qAl!}qxLc52UAqvMWO6HEdx`8b)UP(ihk=~N?p{{DRiTmMuM1#u*5JSoYo54lV zG^W;|?U)k;2%!B6p5b=}9GM@Z{isq!p*`FLn?Q#H_GP?MWQjFBf!7ZcnIk`g1AWkj z@$t#HjaN2em4#N%Biptd$HNkg8kSU{Y0mHm*>Gf9Y0|v_msFM;G#(AU1#4o=@#vJf z>H^3<&6U9;`F53i2(d*XNi=N%oSsLZ%5sFAj&=o5gv4F>JajP2$}Ka+VZCOIrO zdr2pt&`_iq)<|3&q?XL*ap9~@#0%laeSJDN;a|KJE)*%|)T)h{S29h}a>5p?uaITB zL6L-qE4Q>1ij8TF-z=-DPRRU6VC2~TM4dVBZCECGqCy6-yDGfcvz!b1`+WQ5Q4$mcS4rJ{M3Aj$Q>(5WIcR% zIqt91E~Ky(1?DTL`M!nD4hp0v4-OUm*GR5bFY{;Vs{79ws{0+SImihzzGmKpdR
qkII*Z0es{vMp+AbocHj^v@`ILQhv0lL(q$2m&AGOe4$<7z2;*2|e08<4>nSx=c)6hDSXHI*!b&lLVz;wBD3yV69>Pb85N-h$?}JjlVpX;zKg zfkP^c!^V8f5Y@&<&k4N{dx00+K0Rez$&U^okEeMrAgwp0NQ>cUIwD69@XnI1w}Ie@ zr$`KKlX_%FC2XOA{A(xTRR_+cCQ3_ACuF0pSR7vm^y^;6@8;Gie+{N7iY9sa4o+BO zC{0GroF(8wF4gCNV`Z@fD<+*<-~HHLD1fjzzO}Kp|8E**Pv(u;;7LnS!^a2Xt+JA5 zR?&$U2N8lEQVOYcQ_cXoC?H+LJ!o2<1jOfpkhbSt7UgAYzL#|GI*}EZo|Vo@ojXNJ zN~>91^!UZA)Yzln(iE^!Bx~KhXxVD6W`x$r1knPT;7mg5t4gGnx3yKdXa}h$V|Q%* z`dnqwktnDs^XwG@nh26TXzf(-E)_&Axcm;-?LNHyR(1!tPs32Mr>BO!+@pw<&=4J9 zp2YOp86~h8>%h;KD2TJ0S=CQzqzW%B6NyX)R*Iw7sofUeuNynrb1}H7wCPaLp<24W z(?`iQYFDJxA-&Ik*NjvQ$LA=LcP@s^C6Gln}aDiVctCg-h=~DgG7xI9rssj-C|~sQv2!@LhYlH2U-SYPMo=co(@gF4B)9 zMCSG0%4k1FUG+lQt8u!{?mfqqCJ9#fNb#-ZIxxc+YMq9g-Ih$36RRzu%;`$fXtrZ( z2gWM`Nu8lNolga6Ow9A&zl24!*LY(c)hr^S9+W!YPryvadqv}5$_S;nME%4!S%sz` zz^@4f%h$pWpot{&Eylb@jnL-mYWW&>N(v%tX)GtSiYO7%75%@af)<-9qN6}{h%@yI zVKR>gg2&P4klYv3BiBQge(OjqUCPYwW{QrYjdr{CmM^u;-(!7|S3j-SXA;8d*O z3>@~@lYirgo_2i-6+?u`4g(Q%to8-yBeE#+a%;M=g?_5bpy#V;xG(F9{aG_3p~1gf zxGp57jrS7oqxYi6R%af%uizyN7spH)Ez}4q^cU;Ebie3Xw&z+idyY1wVa5%{OdvOC zu5adz7PLikY)eF{4N@f;=8Pi+b9kpe5?Ik=OTLvk4afZrw7{)xY*vTeSfd0VQlzJa zxA(w~V7eEE+vlxn$rb1k1m{VnUIG?z=|YC+-}H#NW%;XDys8E|ac<$9UQJZ#D*p4Te`GSHt0*x{)d zjOdW}Gg^1(VVeXK{*3RE>5T1FLJZ3Y68k;gEpCe_>%LO0Ts~WDTl<98yL`BtlqsN1 zMo4!**m&YWyO0b|=HMPqH#=#)t@V*t$N;;rNo;6$3b8nWa<3c*n*PwaIbT3!yA6t% zGEe0&%rTa%QN@PJGiM1ZN{^^TE=;+Am!xf~N)UVL zI`P66;%S8TK4hItzyRWdB5FCc%I8a2$Okw8!_a~8NAPm@7V@(6cv>Qb4Qe~dG`|{= zz~(`9jh2Fiv3{@j;tZoOr|;DXBOf@3-VUnrC%|MRQcm3Q28l?nMnK6*f^IGMO-Xz$ z9w;=Dm^VI||BuGFPcZkk_1^O^L%NnituiDdA-ar0(Y2>Rrk-~CyIG$3*0gUfFpBPxA7VJJBgTJ$B6i-009C$q%-WAE#$ICF@5D1JE`A? z{o?A{bm5KA6SnE20tF~cY5aTT-;TTHOQA@ANkAd(Y+j(?vu*ybG@Is}5N@I$c2G3D zzSEOtNIfCY_7*eEEPHUkWkWOy!{P?6J~u!$N^(EbJ7ZsD&oL8HYgjHMhE7?;g)#N` zRPY-v?fyEYs)%s}MPf@IU3op@8od{f*SBLB3va41KiLY(KOvcSdnOJPnAZ)7dCQAD z?#A||(-njqnXb2oQ6{5)sI7Pk8~UzBB&9KMJ^Mj?q{%&M&eekZWK0;KOA_77%XI;r zrilc?*b^S!SP}3t=bm0#0+YrJB96>+gbev9CUkTYXc$5*Abz2K9!6N~&jYbHoMS%tt=9hoAZ zTjV+%^a7+na^;<}6M7+^wv;qo3PA%vDZ!?q%ORzZZja(&4iuA`L@JRD=ny;){hEbq zX{@oVAhmT=$>6aFu{eVAK%#cKfA8M4x9Ec#wX#vEb;_L^M6_Q(pBIJT*Pt6-utux| z-)yQvN~*xWjI?mTQ`#i7#5usAV38Fl4VOZb#s&8~jJi_lQ%ZN2N)i`>Iet zTTh%6-VzFB7I_jtN zUz;|Jx2hXYKl*fkU+l;#Ws5Y^#C5=&An&I0 z9y{XUMYbeWpY6Q8?LIEH3o>E1O01&x9(3wj(bU6@5-ZAqhd~x-4~qN_uzJ6E?*9Z3 z(wLLE? zng4|?`Ddl)(C`K7@KPQy#UWJo(pm@p?FSvd&=SAa(GK+p{>d-iKNENqm8WKV^0SXoOjV@-h(;^gW4zI=p;m&0U=GWqn15bKv z9ML007WE9-2?&u^g7mPKEyjN|b*}k&8^o`*+=SNAsu14??**XJ_wFy$n&Say_$LVmk*UZYNpwr1hfANvVY>=I}EvT z5E0P$ia5&S`_FuEuWX&6S+xUn2?yM`=O)M3+M3P=?IC9{zIlm2~Yi z8Qt{-a_3KurG}|$tMxf*P|TomlyW8ZC$Oqb!f$6NezBG$Y`6OL@-d=$7)~w@f>pT zL57_lRF+ap180pp(a9{DQ+?phFRr|?^?@u3n^_yQ@HO^g6JnFmx?CbnTK+t>?-qxC zN`Ae5<`(E+!fE)pr3;hIGH42+!Ez+BEBxG+B2Y#3lNHaIUDQ-kX$?ZsXXea%! zAmXO;<&8QK@UY*k%%6ZmaoTcX*e=cz-0vbPx@U{$*b6%N>p@F$EiD9X#D^eawDbc! zh7TzUDm^BHvdFek^M#AJvM9*9bif))QH=_KdD@U&oEbeP`pDJHP!?CIM1+qL1m1GR zUFx$~8H!>K!aXx~>FgT$1l&!Y|5QeM0~X!lu;VI0W$Mft@=Vj5;Ni=W#Y)r3&JZLTOzqUhcAaBZOQ=ZpLE!r3Bso8sW zVho3zrx7PoW(#O)R`l^FD=NN4Va`dp&ib8RX@P|2C70NY6h2%7w(1IK5&_UdnK_EO zecPRZj6wRzTRkmr*@H=I^@q|c6V$7U_Q7&(#;UCus{bcVtolF?q5}wTN+76Qu zei!w`Wcv5vd3mamcPacc1mql*mTjp`)25rbU|UA2WHEfkH4Le~)<8eIqQx~UAsc#w zS6wv{ETAi138qM60I6?cU^)jiRd2Fb?4)Ge_siJ(2~edQ3c3t9j|WV1cgXw1MxSR~ z%+TFV8oVGW{8+Is;h5Cwl%IoB!Mjf_N}H}e9#+eb4>@RJ1b-*^4k*>A-}CjI!E-JM zYi5fC^t;XX6%ym4>V&iU-v(JJO@=|*j>}QG5l|!}T?2Wf#uK9ElBZyz4F8(Ahj_#} zF&c;CjY2B90K+SWEG(hRJXbX`w1G?7S;U0Uz(u5`eNisjT=@=dd$6j>KFoWTfqM$L zz+owv_2ifhKrCYC``vzveCTv${d}!sdZI~bR9Nq{vl8qU4dyPfi7e|Ve0Wcsb5R1Z zbOdcAh6O28vW&}aTKLcR5m*j$5ouK~=UiKgg|Aw@B^EYiNq)#Z>F|4t#^zg|Pe9+# z$}#yI^z@#Ql4fg*00_73re`t$-ib`09>1lYWCe1{0F zyG28AFNO{Ily`W1!=c_Lkxv`QIKIg9o@pMrd`Oyvz;}%#9flKRZ~{L#t4>veOB~ek z3BW~VHF+|*$U5y%XO#M?J%+{oKsb1s641->TlazbAW>qGvWng|op`i!PP1VAZaP~5 z?%dgo9CW68U)wY5#Uk0GLze?1z=#)gKOfT>&JBE)46DZq4#AyD56;ztB08tKBEd zbXtusgg%Nz#PjLkOk*f=Ff1PM6deB^Ag>st`Ovcf+Y?LmJexuSsFKV>LGQQvSB@U< z){&a-eB$Vs(VgQmqI88NI*X>muVX<}hS13hIWRN4UNR=LW0USol;oz2gs%%F)lY9cIoGF&U@2=TwoEb957d+fQ+6jF3N(7S%Sj!2!p)D;W z;rUunF;RShzF>F#hI*^8QpV6V(A`d3aTigf0A<@<9PJ3d4_6cjD3F3j4?s11Q)i1? z43_{&&)%LgZiP^axd2N4-aa$~_Dx_eZBjOKh9_>oF4FN>mQ3y~N0M_{mOvd`p%>~9 zE(uhgB%3d#87YV&LO@c8MCmE0v}xQB_Yz0!Dho`4&T55{hmohD(|yY@5`}aq!bR>- zzC911^F1;#|f&TEl-u3ezttL$-p3L1sGkZPA-dE>MLvqj4>=^bMvUFQiLMa`N zx_gKZ=}>bzL8j_dMqYDo_WAIFIEA<ebYi$vELhsrvz+tWS(PbkI^1_AMau@C5y^Za_lpA0E3jYMGuHrZba;UHnDJy6b)0pe(AP`L! zT)vt~5AK*{5=+B@*1s{V7bs9ny=%~)1;lAS4t=rp1kS{;Kr5p|2(=Fbm-CsHY*A2# zL49|hANyXiTrgqtc+2>P#Wa0m94W&H=h>DcIO!VplKsV3g$S`%`8s0nI|(fzsQ1cL z8-?k2Uvss2YS3u0_WayDjhg2sR>gPy<=h13%!y<0tQIBAF0}=TZcR@2bsTln(N=7q ztyse?g7~9X&=HzEu_tS06a*tpBHb0|dAFLZi$`CPgK5*Fc<)J)n;+OCZA)G-`rdoN zs8_DK1qBMWGEih;7BaxvAVA;vYQux6K05RqbEBW`=4VEyy8tu$^VYiGd)WM_o~j5Z zY#K#LQ@f=le%9m4fHPS7ZBNul)?4y;6EkXFS><42p93DQpiW%S@}gV!Bk8OgPB_Vp zAqBP-$5hr-nvE*Ng=H$Lzo%{`NUzCCQwLX0izf~UHdAPMI-`I>?AK@OnId4g|0;y{ z2|&o8nW05&V2H*Xzr#n~2&ymM=+}eugFuU=J5+8~qM3G*taV*Qm|w_D@!f z`BO)~)R`_`W4qaMf>;;pq*6^~3}gy}am)IS>P1EmUWLq@W>y_?bRKtKKhU?B^T(j3 zCWb7+;Z{78B|SVSBtoH3Dxn**MIb?%nEeFceq}S>z{%WvGb+D1)-n(#^n7`PB5@%! zwoqv!q^aJBq3^z*lnguC@e1yKhD1`O%EFj#eVFbAg^4fyCISS~>rhUoKvyPp0zD8d z&V~Y_{E5Sz9%Hu(Ji_uvwwY5f(yVgky7c0GcF1i{s3@_Tte5@YyZUO%*v3ZBEohv! zM!~UD5%%A2ndst~!QF{_|G0)LqTeYPvOv4cw7G`2jeLpdHrEZ`wmt!@--TN`(9r~v z1=AKVSEoM#?Hn3^h{B>Y7kt;WVW_=4fa{qDia$iNA}rLH?*(C2-pm~O<;WYJMQ}@c z5os1P#9(PWpSDaN@)dl3wm5U9Zj2tCE?S%#&MTcx<9ef12OV^U^J}3^4BZXqF02nK zx%42nI9Nn5_SDP$u^3a#!WHA7MqtwS>>r2y)9r%U>78!RiU^Hi|EnLtgG&6N%G>BP zeDRi0S~iDA7XS7prg}g71@6r%9-ViZHOV;&1{y7tf>kSGr|HF#72`Sg8$0ftjjn^O zj$(Y<0f%Dzf}gZpdg<-apu(ESOuRzppA0zVeS2Q!roV(z;!IexmbAEUv>!Sl*`@HW zF^4DuCxHk0GSWn2h>ta@I4`Oxn+_8see~}c@t~Aa1~)xa9Qlb0;5>MDvVWt&%)6$p zAv5JW=)aH3@e$!<`%>=G?-b z=mu_^Y8|;@rDN}{u%^aN71L+OPyreDnL$VZr7Sq`@jo3tBS}xDx5Av<=u@L7{!H~t zckGPdB}HaGlA@R~d+pwK=_}m`EKB`bk%}}uc`FYy!$^?Gd$5RL6l+j~Cfh{9fc(o9lq-x)bq=GLXTiBkrT9r^x%y;}*iO*7hLYzA|xt*i`B_^b%>+COTHA%W3A=y-0()`iwEtYIFn zKdWfyBNc9qxYW`7qkeCqUK@=V8PPFGegBy4{stCpnVf1iL*vLeWARtTMs(-Cq&+2v zP!aP}PUdkiz4`Oy|7)nHnL~cLw;cXMe+JqVm-&g7)LL9?*+!j7DA9wiga7CgMmd`- z%foaf+l{aiw$|X%cls{a-tq~6eo2PXDMj34%R#tts?&2U(`gbOmbBS|zbmk%Cju$S z`_DUlRlj-sekaEZo0ZX}rWh_;&_60dfbRzA)0(<3w%CyQ!`wo#UTj41kKP+0%tnM$ zCZl1iDw*~_+LDW4{wl$hB9pcw`ZkP%GB?l+caIEoc`j!pQ&Zzwn5lV@MJUuW$`7|nid%Pe8@A)27?{MglKmCfPq#aSzd6Z`3 z5eFga3I-Pc16=$JLT1s##X)(OXH0$GotRQ`UxS#^e3K`8yWi7wOs4K{T#v)rLoLeP zR@QjQTYWM4qtD6!AldGCDZVgBQgv(1drTAEMl%hwQA{|JmLpK9qT@L1MZr)X+9oja z9G)Avru*E(@6k*^Tr+e*6`%Vgj{U;ScQE$%h|Zyxp8P}uSa+SXf%j?Q8!5(^ZA{Xqm6AN{+amZAf9xK#y{ zK0iD50qm+^YXQZ@1W@+4lkNmFsN=7JMHV3P_Hd@(yS|z;GwcZf(Ge}srug!g+LeC> z6cWsZ9D7N_O-p7@kH4N1FdSHL{Izts{HDb(bClQ}Wey1Tm&n!)Hc4CS;@5NSM%pOh zZ7JA4Xs*aWKQ^6PsWXN7K}{p0;(|9xVina{fN1~zS_jQ?LfF!R zvk+2)E*1PJilvmzCCjH#FeHpOhWIw@a+Ju8Ih{os3B`vkLTexRu-pNm(g-+HB6O<& zTCr_O(;^;?q-W^0j>`pCuc(XIu3qT%VEGT=RC&6ifAI&&2eM(-%a8(fYa&jBo+e!seb)<@*~c3A~puV69>I{4P9_2B;0WA486XauRMxO3SN!W%1u$s4Kj;(Q%>y+Tm1U9FjdhfoJOif{#gNnQ0bgtg_iMkT1HAqMw3UDrH%`L z%8#Jk1=w9|mY*tJKeh|#1J2~wM~ETP@H{if*lnDv$Wt!19=W^pE_@v%_MKn^I7mDw zGi@!)O4`@#<_M`7k1UZDb98ACn?C`V-)NJcUV<*~HrF#kwvM#0wcGLEh(>o&UlWdF z(v4d1PLe&Y>|3qYuQMznxSsrpjg*EjzN{bLo4#ICT?qDcI2js}ApZIuw|krQHzD|7 zBG}SD?abR|12x$*OeT}(^Z1gf9b&@#7iat)GY#`mY-{OdcKbRLoPY|7JpkFd z|C!UZZ`{atuO{!PZC8Y<_N2O?%L~$6O+VEkECe#T664|d=RzOkO+*zD|HFe;HWYVv z*ql^$D^>=V9Q<5XKz17mmV4joO8eM+LX$ER)U@UCrLI}a>U_H}sRhqK0P2za37FhF z>(T@Ft6$TD@9%^}>pKT}KeZmtx8P~%k8KL*3X^2FJ>i6IrMd$ir=@?`8TzA`phk8S z?BU?kCfhSuO@&Ik)PW4R7E{{Jd<+43G}JT-e)aYJUMZ_03S_eb2n3G3y}BMbgoj=e zWQLsUh)ZbR*<&y+mg6AQRPlj4eUF6Aw7(rlix>4P7TeILAoHu z(m=av-OZ{(1ixo$?9+{@%*)79X@8earLT0K{KxqDZ)fJrquRma4b4y<;gOxp$Me3; z$L>+PNRhu8yd75^841yC6-dUASDNA(a~BrSVcj)GBBf!Lg8%rlNB-=O(Pg^f*J4_q z+Z(iJ)7W*z_85*{#IMcf5`;~=!FJ=t#=F;jga0kl4GD-Ha#_U3$>>$|%DEaP2TDG) zU6o4JLVp5Kxxj}H9746A-r{)G1A$5E#I-m= zQf4Tt6FSLK6q04M-DT?9^C&81zMKR zaHVHesncB)`SXcr>P$GW2EjSwK60;;SQUH%0MiX4W@z%y;Rr>h(K07kxW%}J3G_9g=c|o{M`63u9x`8G8D3K$X>YNQ@S>Gas6zS7;ruYw$c{R;t;8bh3KG(!IO6 z)%H-MsPu6m*@Pgyq?eoB#eLEuRJP$7v{08FHQ!T_T}9XoD2$7!_*Er?rQ7%b;0W3} z`S{_FqwmO8^P#b?Ds?AHU#Y2niKVdNkh&eDVcX;`)3+R%?|q6Qp~_&IEOtDv)nnx# z|65*Wt_y$<9{v)4C_o-Eh5%EhHqHI0iCU`j`eE)ADSa4Urui0P+0FaU=lPYm@Nk*N zwiI(emZ#4r?bOedUpWvmg2M%jW<^7CUInYBa1pG8tythaLbg)zkN*RmKw`f? z*Xu(QyyZACBt(X3Nzzy38LMxHWi@^1bvuS~)Dk4>ht?8_J!TL;e2%&-TEZXHS_cMrrPV8yg$<+K4m{ zxarddS)Mw%c#z1zALAPmea&|#+ONJV>RR$!qSj>H?G-9q%P z0XdQq)5{7+IyETyVo#Mm5(VtX`2+rVL5Kqj^3>pJA3x>?`ePAM{#grD-PGq1QMMxI zk$*fb#v~Ls*x)}ooTl%J*u~QTr+MtLAl4wVxn0R@FP!4F@BM$SJ&sVq@AAciE&}SW zykX!Wp$<1HYZ)NlQLag+I{DW#cxi5T2)N{hEgMMYlDYS{38 z31yB6p<)POXbg0_F!l?-V14myasET$aOJZRG^ahprI@rNo%I_bTTK9gN1Eh| zb0Z7%s1?81!uLl7G6PeB&En zCS)Z{gW+a!C1a#g9UhRQZdWR(#fTHZt!E70bc_Mu;k@1%o+zasf6;B+;*h zMR;%8dOU_>H9kuz7>hp05m+5XkQG)MK?S7LK}*?yyS>MAe3_n)J(ia^uNG4<=2;zW z2-Q#J0vpm@$)a}(4(dp%lEMXs2*|$Ys8eXq;Z_%dJf*|_;XHg&eR-R6spQe3HT18S zWNB5PN$kX(uV&s0=XicoFPV>sG{N%{kpsvXE)!xi)BvS)$VnZ^qv;rD<9{Y_$IkHS zA!l5xz0QnV@exM40gj~x#=U_cw%11zA80NW!*ltJlyPQ)M4+sODHQ37mv7<<%oS8K zSyf(}X)9?DJ>qQaoNa6E`O!}&YyMe6_;bRzTu|px^HMfycX;ySap(bzU@1{skj0+2 zuaLm^2IO3i{5*%u6P-q9SnQk9KmZT!novm~btR9aVtd4&4>D5cxOj&=qbf*AO0t(l zk4Yz0*d!{TE%5wWek*Uq{5i`ueWA%`1F4Mq%LW9CR!vvG((EX@qlcq*ZX9!y;cQt9 zp$=_>DVTyol~WYLHP;&*o8i4D-oZaH(-8g`a^TQJfQ2VWaj{1>RFVDD(BP+w;mFx+xafPc~Z#X~dM#VUw;Z!MeL4CFSF! z%VVhUY*iD|127_n#BbDf-ybY^zD%REZCc`zNL+zKTu#xw4{bY z$4(Bhab7176{j}s`OK-AC-s{N$s1ZO(PZ=wB#=tfA7Ot0qT(4jGYsWiyr@)v2#!XK zReyOvZxlFaH4BFO9dxSe!Xb zz|sgCs@m6*(hn=BlO`NTJ{v3t9SF$9xd5srk{559IOFdJ9Yr2LOYM)bviRQDUzS2} z=`#djvP5wDET#tX5gDlLyEaA|@o5=iayo!(5-A=%pC%bfo`-BLs>#5@Rte*t7H0E# z*UrgoAtb0h2pYg2O~-Dgw#KyZ{{WO#vU+72QMb}X5H9Hb1q>8sxfU`JcL92je*6A= z^}aqY!&hBc>@o z4}2(-Fq3ApDpJ{qH|nBJ+fV>d(nnE10YdKn72wOwm{*n9{6$Yt1&tz&?N$o(KTI3* z2PQ68Ch(vxeMjglw|}k?senk=2XhAr=p`*6j-h94j2Vd(K)Nz{X9bkl^Gg?Yq&o!E zRGK#07jx|a#3GJ&Os}d5yxARd>_};ZMSx^;O{v4vHmxOkY`_W3wpEz0VXsjt?<;9Y zWD23QtyOx-AwrX~mJCT*9x~FQ4VjJ^6Y!vlT2xh%QW2g&OA0Mo%Fv|%C?J#hc5ms( zM>XuxC?NKq6VB0D|oy)f-AQ1<;@dqv<<==;H4jQfKo{TRg$x zju|4-uNp|L8%G>!y(0+jRDz6KQt8B}G;l;uCxqr123$a1Ov0&M6i7ssg{lVCs7YYy zt-p&A@^J@~HnLAJIc%Mbtdb(CW<1#Bjn&^^@)V+sG?uFtbzfq2{{T6EB}u}{x8i&e zq6{S|tcH=w>~?0W9?S^=yVvVg@ciGOCzEyD-YKA2?Aq~la_Iuud^DFkW+bsmz&vRZ_ zjg98)O!iy(O=a_=)nftK%j zVS3n|-p6Btjt`Y|K(FzEzg@AB_P&@jM=L?ove^4GFZql+?xuA6ov`D8P7H;6P(RNM z=aRtrW1a-xiS_>gxB27C`pVMf(>9nHwMxWOk9$pW|E5Hli<)^SrAOoZ7q zFd(iy;oLSWHV(rI$DI^tRwG+5y9ETX>xKo&?USd?pwt5G)3!5ri@dqz%FR?8ag7_4 zV|$ENemL-kc)ZH^cKTxXgiYDZ2ULYrdYDv?*@wV}&*d981c4@oNmKx7fdH+N4UdR0 z9>5;>oNoc55pe~~XNpg+bPNf0B#EX>9=cZpOS(<6o6X%sOJ0h1BLDb4yP3S@?w#JgSM>a!uTWfVZ z)e~4Wf|h>~pqnF+;r{@2@}|k8Zqh7%VJ`@<%BUHFy8|IQPf)aPRwv<`it{E(+SM}o zc6CFb#$BWOlG^|^bf&r;gpOOpt{cNMS&8_32QsBcSH;Y_$|u(H_6Pdb=c-!XO0%!Qm?^7 zru#W3+-^Wog1;Ah2W-B7Ck$E3hcL&_a7MV%STi2G>K>K$_>QWqJ@JphC=8JO%Q9vK zm1Qaw#A-^ZkdZ*p+>73b>JB0%1-<86R(!2|E`m1%o`_S!4+bF2!=Eze5haFJSQO|0 zs>Zb;y4jjSr*Ntg?3UPnmHa;w=k1Hd`vdRA+9RonQ*BKv!GAr;NT2#g~dV z$jXem*UV=+nKjW4PhG0m*Jji3oHFL0vzz7f8GO7MnB_U$N*tX!TTZfADI$oiyD?R~ zH4NfGs`k0YISu|OP8LqMWUm~NE^{m#%|;2=djuBsUj4zox3(;N)=gwqQZ_0_PnWhU zZV)qEKQkUnHZ`PsB!!DAT>zk--9;Xqy@}Z3pcbMC+&x6mz?*_NtMgXm~%@S7boNepWA#3cs=5rTuUw{j1m3iU_&XoD!}w*I}z{F z#w9t91X`iPpUQw$8a_zSlj1j@n4YOIb}Rt zF0YR(AFmNS!OW4sy5wN0NC2Izo`Vju`LOC4O@b^O8pVC6Cv7GYY4Dji;NzjHCF{1E z9EtJ63~(0{4MHwX47m9T_SNA+E<2h4SDO}IcKMPX3;NUmewh03tB;u<{71MbPA8Un z-dwz^fBnWt$Or!b-;zH(3TF2TVx8aA9>2fYI{yISzsvnFr0&hRN*jj(ZEwJ zN@>SPxaKj3QOAL}*%hMe$dC@$5%F?oT&%4f2GVyv-Twe?{jhYgV!rXumrU6y6fqlb zMyw6%^Qyova*w2dvPUR!NV5s}sVvM*N~k{!9d7947FBx*r9rYy_;RSkPYD3 zIHMBru{Gg(jLHcmGN&mNYU`NNq{kvM`B5~= ze6lpR;pzv~r7V)5%;$L7jOH_tA~Z0>a{fcz`LR2aQ%NBW(trWhst(pEJoZ>em5+xR zVrV#vPF`@ns1ii;2-xWfB&(w20K{1Ot7S?8LC@j-2gN?|Wm$K@BsHGf_UmTw-172esFe;nEp9>E=g^B5FhrbTqf0x6 z>GU_Zp@02sbx5}pUj5KEhic5tfJk{-Hs0~tRvxjlY;u!lZ!Ac9QhUJUqX0TMi6 zl&E)CBv{x6B^7tF%--zmLljIdEPk)v_Ng)I0j-wYxz6xAyiW-`}O}gF(`KN>Vk+O9JAJhyR zq-sX^Z<$p5u_!7_UI<-t0U1NTWN!eYmMq?8L=NUgjw>A&KTUmn-Fy21hTOs7oP+OE z@hKiSloZOMF`=n|G%oc>KtP~$-1Px;_kX?6wN)Vp+E8Es4f>=RXFx zw-L?eMERwP45X_f$7f551h7Fv(O~L(8oDiY-N_-6&vCW}e{U6$E)8)5V0KUgo3ip` zvV1!)0)6u;n%ZV6RlJ=Q69a?B82(3 zkMWshj#e^7oX$FmZb+(?{Zwv_I#Ez6mq_-_F&$tEg7H=qkHu| zPUV?J3$Ztg+&Rqw*0r;1Z|J$3V{pCjgTrw@63=9tIu<2~p6Fg;+yGC$k*iPul1h%3 zhTNUuT(jnLnOL(ZjgxON9I*_=O#-IIof>^qeI!;M^Dlv3OtEETlU8ligqka2fSvG{xMw#0@5nr=gYxBz3O=hX?< z8|}Jxznk&7QImRVs^ceNYh+6H76q#W$4hwRav5;U$l6p!-w>|*_V zXP>g42{D|=?P>?pbN+bHt&d>f&C^K1=l=l6uRG#-sCVZiffz;37juM)V*DzClGBbn zoG*_5HavM~CBDE`=}e#RV3o*(5g)X+P^=lpO;@IU_O&KLmdzlYHO04R96y7<1=MI>)^O^9+;lk3pKPvLX=BSv=e#Qn$tI<3KA;&=&vC7n(tRhmIJo>Q;s#8d zEKQv?e)WQbn|XZHNy;c?#{qHdvjJ8>zMnjFF*U`~3}F8N{wBNv*C#b@va(;wNSTlC z68_lUYPidP{6RPlz8S8sM3En*xQw6DLxI07K1h*a;P|hfe`k&^(Q#~p4S|`JzvXCU z0DtSzkF@zI^&+2j2lW{EUCOz(i#3*TXr^l{PyC{I_5P`-W9`Qz=QZV_Z+O@C6@f#R zdi{UlF;j>7DXCix&J<&gaL7oUr0|`pbkkhDl^nt-7A0-wCXkSsMbik#BXK z{Bq^v1o1oza=9oO_W@*N*RkqA76;36I*R*+@kf(d6)@%s5{(%&?IIH7H7HUU0V};} z2K_){?E6Lt);Q#lF;#!zze#0e3~J7hSQem8*IQzR<6LZaC8Jg{M*aQh26J7uxihlyg*`GE~*r$)nj5Uj^pxBE9!gV z!{#iE%CSb?OpPnN@;V(SLrJL*Q%kNxxdea!H`f%WidnUeQ!(4jQ$TgL76;0StH$Sq z#vbAhdMv*h7l?VG-d^WfUof$|+UndNm$ChQ#tS*qn$ykDhJam%{Um;Ne=0Zu;hq;} z1P#PC@_=UC?rUSW&c%C!^A{V#a|TIO*pu)6L+Q|cF|>6fL%}1G#pw#hb5?F8e>p#n!A`nG{+kd8J*@ojT6{x}46M+@3IgWKpNhd3{{S(% zt;H_H9gZt#^C9x%N|NJdHDj&+0A2RO4TLq*3hJ^U^KKOynWZ8{x^2FQWehZR! zby&r@BS`s;{{TVM{IS>#oC^8Fx=NeNco!;!k_^O|d5tjvih_HOW4l#Zh<6wmO8TbmY6dVn!ZON^=e7`(VywAdN*_`fX zT=sRN$w!taE;Udxv+8C7i6vwB6j>}*_X-Zm7qqyK4xetTBO!k72s@Furc;CFGkLg- zd91i+hZU z;;@UGqK}3D0K&fRWSco7`7(NZ!yNKlWQyKJjH+M4blrh?m!B`iva{v58i!bC)#a-i z1~B@hQox@=YfybU%QYJSQ&L7snN&|?8C zZD(`T4!5C!G8e&cVd; zSp<R8w}if{gk^*{&*?R0Jv(;mCnzY&Urbgr*RUq0>ms!QhM!~-GeOx zeJF~ft*gk~;hk)bXEZZ4naj&8Wa*8i?Aq1d?wLc=v>nw-a2nIf&V0jz(q!@bxIvz!f@6g28D>sw1(-&TLO5 z-E+3Ua`jLyaKeY?zA>3F$eFY}Q=|I@TS_CpPb6`jv_~H zSGQT}U-11{Fh(G~=K#5>Z+gL9y+S9O3 zxwgvQ{H@~7XCjFQ5gMa-#b!XH+-xRa{H8mA;^AUC#7f3e`zP__Ild;o(UK7JleH1orU$$y=G@N-&w&grGE7Cr z2n+&}0ZSovy(L(vHom2;?W3$?-WuN@G_H(YZeO&AC;jh+W}^NkhCrvdVuUFlHELn= z7;W}qn++x4` z9PvFlS@OknecLmcfmhl%GZMuSA0~-oVe(;zfWCY_{{YH~wx#>+9i7fcK2k^}5yu;r zkw~#1)9@X|bu0+qTBlXit#fjx6f(gZo~)wrRQ<6ycCHnel|d~68HS&T`9Fz6{$Qnk zV3FSz4}}@P`6x)=(_h!+i3{fbA940j=Y(9cVww9~@c#fagyy!gkvoC&L9Bl2KRhFu z@zAdo`S!o1v^>C`eX{D>p`@jWZyCr(S^$2VmP zmndkJVyD@+qxl**`!(P<853~)yMuOs{zO>*c=+|qxpS~RMFaKkkEfquJ}zY7*-%Zg zMG`Up01}`^`g)Y@g&^6(%k$=}s=u`Ptq-Ffs$-?c?N~L^pe}@1=CG>1I0q2AEtl+e z1{IDxbYU2Ya+Q);1xZmFT|D8^A}38|DriBu0!X zUl3<)VCpbM*C-K3Mb#p4koU zWr~bhOD7PmZ*~YHN%`;Z*2drOgt%WiykH_K>SwpH*1!$)Bn_MRcO&LM_lxU^zEMMyxr?Fb7 z*IKpz0BlxLb1;`t)1= zlzGaF$1_J*b11Adn>mOOm}>9u6^TAk=k>}=lQP<^hWQ5X`Bn#+N#6?Pxopj8*n@^3 zA+HC;LT2k}9G05R=eTx$Fz(6f8xlY}@7Eb??K9x6Un=lsyz?YpbVah{sq-UO4$-gt z>I$C4PUf)3kvYwHR%xQn$L8earPWqpAewLwQN1$tp<|}xo-z1Q!)2c-n;Eilt!K+d z)G3yffGyQSY19B6hSz6T4280XJ8Nz^_$-W(7|wuAkPdUj7<0Mkb8w_^jujHhsLBh> z%%K^`ThryL4GJ|9(qHhAhnydpc%L0Jtrche-Hmy3C?P`4@h{SIz zbN)|*L7SEV9N&C)5qZBICZtf?5>NyZR@Bd=?oD$1!NfRLULliNmM$R6puAvel6xz& zQC4eqZGwd$fk5Gc+uTOq2A6Xizv@&#&B`j|9z)LYe4b-HJi#SKWQSSxr^FsT9)fsOei5 zVjbXg#^3?(H1q=Z3Rv8seo^ozF)l+fnli;|TgpRvw4flHzvDZAHUmi}*nmmEd^Bk| z9C@_OD(HDnldyCPGovo0I)>oCZJ8J9w~3sIn8|S*o>{Xfkxu9}0(`UvOLEC|U7hf4XqMl)l8cXS^}UV1y?QpO;Pe zw+zq0mu3&=K`LKedP54kCiebg+YtT-z#v&E!Pdo!UilcvMp1U=DrA%a|N z(2z6Zf^bO>Dk!@ooHmPK0?P|dbqgSsmoBIeP+zY2%b85F zDg0F@PSx#SBZ7!HsEwkMGrUqHiZ`oP)CbDykI0-B8M`Sg2KGRAyM^>m#LoeiYt3Zb zVNLo_*1CFE5$^I#yJ4Kprs0_A#An zWtEBDfYw&<>+Oj&(nXi@fYqgSYrfn600`;OZ-hKo@cS>C=|WA?yxXWV2$a z$KJtK2U3w^rHu>{2|LU@?bGwkEhVpjwD*XdEoaHhpAxzew)-$3{>K&n0NEFbyYRRc zL->e@vR6+rR3xAK)v!02<2mjXsw}{K##Dk9Pzy+cwh}1bMcTZ?8pVxh-nnp(*t?EK z@#fMu*B{>VPZ_)P%RA1Xe3fPjzu^N7Y5?iyZ`=7o86f&3{AyE|;@K7y6EM#iByX(| zq(Be+qEdc?8Xu2F3=e%YALWW;?P0@!OqOD-<@1m*KHh5_u|N7}H}bq)Faa)De~uyl z0NKaawtRnY*-fqQ5WYNkiNdtcn9@N{K47Q(K5OayfFG7N=l=i=-2VXn0!WFo9J6&j zHB9n>`7DYD%Ne)F&JhePBq+5cQx$Ehl=|28MpN^}ss8}O)0y8<>4H*crXm6!Q32n*^Dui93|@s1CC7iZB2OK3+FjL z4UUW9oFJS#Fr0ANND{aw9toIwheU^=s6mgSaEMJh$6eidY`m^+V|t(l~I*)4sGj>@=Oa40%IZ&H0y)c%IzZ9uCNgVH`fY2e{{Wa#>Chi+8u6_7R4k%ks*LU&DJ7iS zDgOWx83QM^8q=`Aw=Z&oW`hbWBc*d)$Ud?wa7wk;ztbCj1jjyRw0SJ=7q{u%bxm=e z9ID{HqNE)suX~}>rH-EC6@M1XmUcBl&3!TTgZ}`@=Pd9;sxZnEuAcfx0Q>dc?--ZI zUkFBA=44eWfr(Y3D^(iy*c$p!$E-_2V;X_F^LXV1*=IN5O3}0G3!}Glw{PW%uLv$o z)KM1-j=(uT+zcIn`H;S!W4`;03}y#xcf)drP%3Df0?v?6$&SOVZb09!Kv!u2STtN& zTs|UjT)tjKjU?1HBFN)*o!s{4ue=f2`AW$Sbhk2o0#&T$mj_KP9v4UQdUU=$p~by zz)4cf8+B#N%zuLw_0Ti)(zV>$PwbnZ)Y^#LIXEpLESq2Tv9H+ehJtPP#CuM|SZ%2_ z(Xc>QXy2%_>r1}+)QbdSM}TnSF5uAC>!^cP7}b2V0>`)tNLJV%6>klh#=tLCbyQ$p z=!=Gs6d!ShlSp?xaX-R%5cC4U)cRN^Z1mQ6sL!VLw^#i1z zSXHoXK-KH85|~{&vzPdA34P}f5I@9N4S+!D6E&3<0Z>oP>?p3Ju>dF?PTg?X@-Mff z?ll9}z#57AP$U!7Z;OX3lZqpwMWZW6OruMAj4mfoS(IuANmUfgO6Urz31o*X2o4AL zO^g2JDFMA!x>CoflERik>xO^9SVgauAMqD7?S(;Q^7n$CR{%fMV2d~5#6rwcSY@c~ zfdP#O^)}?}p-DcX%;kZgL_SZIr(ZHu>ldsB2XHssZS+4vHYz}&lZDR&fRSMwn~{8@A(nq75;q7z-K^NF><;=y_7wYLU-I{gJTp23 zd5|*G^ksd9uHjpu-vWL-_*27T%YEWmLqx2U0|H1cpsLZbSxHe{0llj5{Uq;-T_j+i z%LP#@+V<%p$Mc${b6MQBV=a*2$pDURcGI{8$%%}`SE+d>nG9OV)1;C$R+9Ue`Gbk^ zIn#%#>i+5vefSYnIiE7|L@DwHVMY%s}qsg;91c$QWaE0u#uM<6q^sr8|!(V!@e zKvGyp-wj!u;{5f*(In%s$K|eN6#^=$Un@`%fE(vUSo({T&>UJgkU+PVuk{_r!6qA( z8N#>+72^#nO3&uxNmMLVX@EME1yN?o>U!-%p{XAYd?3ze@|mpU@&ha~x*7pRa2Og=%9Qs@bW$*l+90^ z&ghyMOmQQ)X7og)F)nqvV$7jIBA03);fDz1%)GoU<@?APs3n%62XqL*6j#OTRT2h0 zoG2cWOy`Uq6O6WRD#CPFW|kzCqXN-&sdwsEP)%J%klH~9AGy23T&#RYERg0@Lz^Z! z8vQDCyVa5_SzQ3O`iMOU7%XzN#1QNpfo*`?Ar`2fRppXVa}Z{=G=MybtJK;FwO~z> zC=0Ex4e}l*sg)Lcjh4VDDM|kT7wC8P!EC+|K@9FAoD&aVM@9tp>_I2#zqTMbgOVec zdZwZQ7`BQ){0)*W0_N)N@K2QEn>v|;TH=h6r=x2%t8b7XeNHs{V;+5wGC7E|?GXi` z5R%MnfJ-icXWR!rAP-9yv2*FZ*!A87>(c;5j(t`Oii=E5@3KUkelLVhIA_L^%9m16 zq{B(u9XUAUCME@ti7vwPD+Zzl3-VJcfD~YRFP8%n5y~*E-Twe2M)ROMy#hD8L(Xz*$let^(gX^6iKGuM`p6 z05#RI_xg6a#bc521-@y1Jwoo7qfexv7oN46y9I5;?TTn#9< zl97agfgq_AL87MiIMG?W*^4v6GZIbaNb?w8$Qn{iypDY!4aCtxzcAlSWtn*6#-~A} z{ePwv%JUr4Xpz~i7!^10kU<1{5NP4bX-qN`)PyM+s9k-An}7spAKV5odT# zZD_}uh8B~_kZF-6j2R;gHxykxE~SyG)tGOK+rpgKlP3!UV6W}l`QQu39|ZCoMh9yN zAX8FCbh_UAFzjd+MzngSRvTiq`y_b!1PhbQsYZl|eBb=38GQh&b}gsjYP0Z`?B8k2 z^YbW;VQcfzQ2zjJu3qxEjD!jj!jJW3W>)_I+Np{=k#ZhmJbr6Mttgdpj7H;T1Q132 zciS4j{{VtGq9l8_mh1lj*=oix;JL3@I8pxbPBFJ(YWmUkdd<7==nJuC z(zeIB(l`A2;>Pl~tHnw7LeKe}D=+R50s4dh{@~zKo_s^j!^a_rSKV46VBMJuHsj=> zKq8I#mt#)v7u=-40f3dGQXnzfk%Ws90rwc;0ml(=Fr3B!>5lx|HwtjLhY&bK2}Y1% ztl5EuWseOe%HmD3fL0=Gq}!d-xL+9O z#hzMI#3pSQsK@a&C?LD5f&JxVRVKF#H`=kWP~$9pqBwGl(fl&S&<0}Es-=aWnOeI8 zZscPH?Q~_-T8AeOxcjYF0ez}0d4hv-Y;-#6jxs`mk&R6puWtVUK+%ux*Fn_c6NqFr zwy?5!e-qsx4y8P?TTs~9pa?zsV~n#%h)8r)A)0?I26tL38!dzCAk~ffj`)<~oE?`+ zly)t&y4(#=#tpq$8+oH~?Pney(m~yNNI0^sxzB~krDGyrHMGkq59!o_lmY=C+5idL zZH6olvDXU^nwl8_qYPvN^VCh9`!F9Wzz;KJnTDc_4D2k4E};ihZ`Z2Ysq6sx;>i0O zN&f(kb|HqS`+)8;FCvGo`Jhw1*f_E8}hy7My~?Pc>5 z#(%N|d^%_(P-KhDK`1BqeB?EKb_1ci76})4dz0jTAC{Lyh=(ju3k<{_W?p4&3!_*g z%gm(HMI>8-2DeK&e@310mzO^KF-9lL1qd35r$ToWLEIe&Zj3As!ypryd_00Yw97L_ z(9%;*pqSaD0aUp$v%}|UbtX+n#gH;-Q;hWWQJ0o5E68bH)`ye>&c9_ixPkK`a&rNZ z(WpC111bU%Lc%P_XuP$GRv<{=%FG61Ni*=jz&RszIgKVwEtj03V7ZLBK&j^!($Y$Z z6`-AxIb=#!a$(L*of+`soqp#;w2c6-C4tsbL2Eo)AX!n0w1UxF)blQgkCt?9L&WrB z6R-+152YV{FGI{&%5DKt@1xXrwA40Yr9g9d1pZ`ra>mJQdGY%=fsi4+d{;4 zv38}v6-19VJq^(63@p&P=59a#0G4|ogjwiSKrMa!N*Dp8T!pNVF_@Y+jB;1eygNMA zw-*hGBa+xyjLMf`nlg;q3l`o;*<=BfDLz@Cx+@V}#lch{bz-h%^oAjjQmbi^TUMky zi?^>x4g&_od&K$t+)E=GA){gq3uZ4BjgP>pWUB<0UwkkB0L{i%M7=v1m@us}I)xj9 z(^AMRQvn@x1&Rk)$xMOJr(8+A!EUFhJ2MA}14auzlEBu3D;8!!Ltb4%Gv&@TD%@#D2kcAbQv<9Uw0~2W*>S3rf;k;O=k>ybs^PUr4;Roh3Xgt19g zCoz(df`1U23UwJi1<=aU6R0#~4RVr?{hOqLh{RtG0Jr0M8BUudv@)$GkxW^_g>Nmv zEa(y`C0SL_u_7k@?U&NEw(hGd#NPyyG9=7J@fj0RgIavU1aG7p3{=LmH?F`blYdpt`t$>k?I>tZOk0ElHw?(!jS8zvYI57b#Oo>fhglp1dS|6Bd$K4g`fuyOC{M*ebbef zc|XJ)j#3=@jIeW>lAvbOT0qd6$u_>RB$NU4xc1t2lsL~2;$BAtKtwUdAJn>3M-AvD zfVTmHl^1NiP6~MUg&9aP`CPK)UR=t{9PZk0TB8Z14BacxVbV4VFZhP3&N0q;?8$y5 zk%cmm9DR8XNuVq58q{Yh(Fd#6!2F64FudD$JeBNjt^Gs3PRoX@`v z%uObYtRh4&A!Sl)%P6GoLWUL5Y<#f4BltHg`K<0=J(vJxG%Q-AB(Ws1AhGm?Uo9wE zC5RLT?xtB!4?JD7PBXI&URpkx32mc|ReeRsBVo1l8{RnM-W@CO<;+1mvhhh&7eHK= zkc~{5MvxU09=nLpyBY-H6DhWw4qI$-a*MWZz3}gmW0D>r7H2rWg(sI9nk0oo_<)-` zyE1|Brh;th*e8&ZraZsPS$QLlMa_OQC#?&c1qlU6yP-p`TPCq9lFg87ug?*RygSa3 zR62u3H!4EY1K;WgP%E|GA7(NdejV4b?Te_cNJ+SEwf_LwuZAee2>=@8W+e9@F$45a z1}vP);=S+<>mdjp$NvDdaC#cfJ<5r?VP}x~FEgrksU3c$H;`cxaOXC-Wv>>E%B+4D zb&fINlL<56nozabE@sCAIfm^f6Eb7&&3)RZr#p||e#0Bb;Mn`J3+~vbhd6Q1-i{k; z$A=y4!iQ>C!Mx&>kb~IZ)0qHh5$~}708B;mW?f74t@`}%Oxzyw%D%&I&lzn&T@o$| zALL1GnNRU4ALss9l;!Y5#8Er+{=T@3;Bk3!1MFzvh8`xdN?3u?{{XL^BJ+0PLB}S@ zrzw#V8g0Mr{P8F_7>!5<`}<&jgL4p&>Po+#Pi#p)9AHYauH$O{SXIq-pi-nAkuM62 z6a)|}WB&ktm}|u5DC)}8!0c4+FW8O7+ii%xAC_lQpx1rBua-QYftb`1ZMyI55A-nr z%`)Gr8CHfz#JuED74xMk%h0hscIwtgTKKl@TVn3}8Nrs#WoODvps;xc;1i|T$S(SU ztgRlxx?-B*P$ZdX+tMw1cl7{vAn#r8z6X3R@iKgb(ijSzb$W$>!?>b7=ikUf32C)HIBV4+nKR!jg)2 zBYh*>k8huD^^M!&UpO8k%k#X17ex{hF0Kg{Myu>X8u?=d@ShYU$vlyj5=7^r_>#uu z4Q@BxPR6dr_F9J#-eA4cl(ry+^q1@v#`R=cGIvLD{{XzO{{Y(SkABshA(K7AGbykc zvlP44$1YxAFW>Rj8oX9tV9tDqNj!SAi8RQd?O~K5q3jG|{fi6gF7^9gaPr(eWm8^8 zXDL3R{m64r%P0P*mDK!+!;Ci#K3^oehj5OH+2Z-~nR6nGA}~kkiihl}mB<)IVnvV7ltUh(2B{jzL4U+RG&-8W zE?!{j8FvI&KP-G5ebmV07lv$hl4J3M9td$Y1?k7CxbQAHIFL{!Ia7|o!)3yVF~S$X zr)qJV{?_~=&R#&S^G#ChEC0lR^HPD*W6!ylAQHr1K8^FBGus*GseiKz}2CM8q zJ$1hN;h(a&;g8c&!g~F1vrh+`%hH3(j3@vdCCf&IHAeN(!#h{C0n~1c z^9Yt%KX<|JSl5#Ls`*J}Y9z^XeN zIE{}aWiP0>Lv~%51`2%?0}ao%o}1sdfl|}HXIu;~Q*+wUlmPD*VnDIw>19)dyX*qx8UF0m=V_zI)iA#yH>rApfO zT_jbH;(p$n+Y>qdAtCgrNPzf)mgs4V63C@#MbPW9Jpjh$lm7rtTIOQPqHrb!uGp(9 zIpZiG=vgRTA4aCG%6%Xm`bfQNb9)SNFB7C{-ay3qOopxgYq2TA~4 zKm*kIg?(PfNpOIZy(zI(@e*&$;wV}U#IHbTQ9Zwgm~{hdVMBWc)_x~6hYqkwL&M@= z$WxOu7V|wNgIQ`yH@bpU_37HG#--WCbM}kpZ%By(InvU)`LU|`jqvMP3Kof?GL7{{ z9nwZz$Cc!<*8|T41)aCk>yd$EDmN@cwXKbv0q8Xs1Ey@^Jl6BRVrHIXMt5W62-zZ5 zdn%KLkgX7?LLDI0lr9N+jhDNMTBzEi6hyU1Y(8@>J@+iNL9az-m(1w27{ysWDR zXJH^zBB`7)kd^=+EfOgS)%~1~2c>+xUVQk9r3zDdl1}c-Yt^nfq5zUL+ftIC=++6X zhAE{NBr7%zHdRkKaex#W?idX}2w7sZ>sK6Ti}ho0_u<%KvVo`l1(O) zXLKTWpifCa+xTm~d|r&Ez>)N7BI&3xV_OH3FG=?dZowi`ko{g-?nrb^uV#&l0Npl~ zU)2jNzhy-gVmdy;!7^IR~9BKFg;J3C!$*P>sY6Vz3I6XR)ALseE$H5d?C(* z9c7pm7Jz!8(#1!XRDi8tgmk+`@zccO{8kV%Ie>*hlSH)Mom$GdS_(mxWbGG^hvjCRSZ)o@iw)rHCM%sfhDbk*CVRsxyEdBg{l0 zT3<6-*lPSrv{tgUB1#IPW)`GUV^#|6tXNIY<>fK7V2}ZguT0v>3KCG-Rn6Q5O==Gk zv!P)#fv~#w38zRbiTKYoMHZ!t*A|*5Nz}TDxZF~fMp+_}0<1RvV*sX&UE^?RS8Y_+8{!cVCIMuQQ5-DbFnbTBgr1Ra879%uBm+^U zC8Iua0JK0$zkfOi43Ws}SK^CKp&J)q-fb&Vsf?qqR#4!vL8XPvChW4Tf4y~~a{SgA z?lWc=kppgZ1tE!RzltYQL=LJ(v9>B+dEq&T0E|yJk)6#Gok;em%M+N9NZb!BDvc*^ zNFw&c)>A$eClQjqVG_gT*>SCWz%)?v3aZ8miqx_w0LaN0E=UWan_^g?77nmQv%+af zMIG+c$s~?%G+S3i>r}NDS7SHjqcPSZ_IRkux(#aLVzhTN+p> zumCg${{RsHUgV#V!wiCP7@3uGj?A4=&4c<^^T(6bR!IznmjH{m$8~NYG7jU1EEECE z67en_#3j$NM|qh1nG~pDMnL+2*a|8R!%x?0is0PEByi^uLP|3%U5OTBa+*$`)Q~QL zv%W5Px$I{{Wpij2a5g_a#2(<_SHo`$$eLE3O;IBq0CynuKJ+LrYb;N~4lN+4YniG+ zk#AdUhUOOInyIpxsV0a60jlwQBrm{b^7cvsHb~n0nznIUf0yO2Z6jzTO$sGU??A$ z>-*xsk2Qd=2f1-z$v}(!z)#GPWS^NNlk>r(NsoG_ZJ}s8*nJn1&g;tJI+VLHJ+P16 z&K-U42+1xA2?{P~IVr2<&Jgm>9K5*ToE$i%T+nt%n6Q~lI!KAf=E{-V5)bO2SW`_n z{1Xl|W6VoZj+Bp;j|yRW$LxKneWWjoJk`U&YN5X1_x`T%OOYf-F028zAh_(oMX)4~ zZu??8l(>_-Io^Qj*ndW`irlMHmfbkx*~A18zN_5-0AAg(4bPF2rH4+Rt_s^N89uTs zZ@=|t^2C2JL>exe1N&f*^ipB7ln;ljOFE9@`C;x;in7RTlSKFJe>{20Fcgx+OJU7*CQ{xas|Tw!suZM98LxPwV=uLh;TP@JK(!xc>k#{IMy?9M~aNQa8Wr^uvB{G2FJ{AxcOEySBXhd*ShWb6aJ$YcNWbT zjP1W%Q+_AIylw6)^&kHLt}SD6JEy6=<)yyP+^D(9a$#MgER7;M5~u$FB+y|*TFQP# zjr(Hw`$2QYb0@+;MDzCs+!80K8wq{`n0AhgqCGl-5ka7Gn z`A6gxW{dZDllI5b?s4ET<}&OZLt0p|eMT=MXe{`o8qAdN1AsrEy~$TK{5DDfI6FEY%m3c9e9F325CGLRZwn0)a)19Qzl%5VoF zj{eyh2u5BU)bX^D<)scnQDxK&MTM0VqC&@U=?51lBIm)%#=uhPusw%ifUmUoD}C#P z=b+pfGV>rKs4W{PBjyPNeDNXRHy0U>8JBQuJV1gynL`iffan|kb4&*vR{Z3rc{{|M zzHQ}5WL9WoXygFyuD=quu^;b(zb_d#z+PHhuO8wtErRAbi0pjNG?Vi>!vpdQ^2Vs+ zTyi*6G>jAy2IjXvTy=ccTxB~kzvuKD;w9ZwgH)LyZg#~}<3~Hea=uRIoA@r+Svn|dz#8arFDg*(kzmsl529(TWn9L&^vasgC13lnF65-yIzg#k^D-| zP?52)SbA&$s}Pb#v5?Yi$E0-EX1ePdj{SX9Xqp1GP2~8%BTE?zHsJ#*0yS6#n5&vK zU|1rC;I1(3gfY5UEEcRf=H(^C=3#drvC%9>SG}^Ms8F@IVmGU9ryTr7g=wZVT*d(F z@VHc9Njt8!BJGOqd*ey4j37>o!pMyqP%3nm3|W{00a_c4KoUz^4h9W1P)q_gr ztc`1==@mywvS>9>rCxv>e$B!Z5QY}@d`valYzbyjr*_!-V!8uckzst74R^4m62_vy zY)18G)J69y01MHaZhFSpWlhJL8s?5ViIADy!h#Yzl1(VSe2D-9N;vfwDwi7%Fok9S z0-2MX_*qodrbJ~@S5S<}71IVu5Hxbfs+E9|fMcf3qNdE^yr7~9VhyA?vdyykNOQc3 zK@_!Yp>%*NsWj&{{hrmM^G<2qinCGjohiS#RzekesnF~jW+2cw*3uE8%Qsc;-F(*K z6)#kqn zAj`CqW)<299YcgvCPMCrBO078qF<`J6acp1U|*RzkY;01UQ|%3E?yGel~~tD!Dmqj zf-Pc+(#*}PP{o#@P+G%lt7E|qVwW|WE`$lC3T9}~Y7wc0dMyBa1g1p;!a3SHyBNf@NO3u6Rf^hdL6qyR>;YSf7<6;{;> zlp|AoMT-KVHCkL!bkXLvrq-=3Ju5UKK`P2^iABv6m6b^}s~Q?eV~y%&IU!iaFc9B9 zaKg|DMQI3{MeNJx2m^}f8b*=8t7Ys4pa!{30!h6R>dQm_05C@7NIUG{my{YOZ~H?0!s=5OB8vM`ihTQwW*%OFT4LoTIOm88XB zWsX9A_X*5#zm~EiR+3UgB#^^N46Mn~mr62{p_x?3ppz_-G|h2X&YBGPo(9ahehbOMmAt~GWnrR2i9c_*7#4M}Yy4=OzchL(6{Uoe6Q^c9Tj z8flk$z3ps=8ZN6CqbTy7w1!fyPyj`SDx#zSme=LRSZi0*b7zOVZPUwUzsGffY@Hy? zBtT6NLdU6C?!J|Cs1UVp@V+j^!@~&$(9YVeLV6k)8b^#dg%SgoLfUWA2v8+L@stB_ zeG21Fm1S@aF_}7ig=bJ%UL-E1+Bsa8C8!h{Y#I#86k7#QBsu5AXf*!-X3$Q(LF;4K zjg9SI_wvBsEpf>*^PMQ-K}z0Yh``maaJI~+Om|Ybb$U-Rsx~AXe}h3i=d*06bh5^q zC}kF6Ww`a#};rTrKjM{&Lnn9546Af=R& zhqtQPZ}a`D2r`+31`b&PJol@=fafO6x0mKDYZaqv?@9=*z!pl2_6KYLaZWN;XNW4f zQhi;I&!@g3Q;9ZVm7-!5wwXbrs8I)Rdat3xuMXf^F3Bv$toQ6c*Ym~w3@?akIIU^lhviUvY zFDyZn%@~Z!EK~`5Q>MuWQBp_vhf5oFvR|3yJ`EO7v{~p(&bpNrXv=CM_Ce}y-jTle zrMPDSj7FdVr1b-C_=e5$n|Z**mH{0<+i!7%y2cFX+!v-`Qt za9o)Hq>4cs5JeIF9-WWR2ArqLYT{S39MSX}d#Iy*4*vi!N9TmOgNeB)C5n|;T#LTl z2)@_Xtr``+A|@9t8>koE4)EU&U5RLofOj1$<h`@^^*NtbCd zjav>g>|W#@zL>8$`yr|`{ubNyujFuP!}6?J$4&Rd4>V=}01id@ zY9FDm+*TiM9GQ;}2+Qor#1GvbwlHRqtp5OYCd=j|{ZdH<{LVhJa^${dS~Kq${#B2U z-({6D{{X}?y`+d~J@ro%@e}_5@Z+yzkEmV&_?H%AhrueaAfP`>>JPC7#e0li!D->2u*3H_W%Oy?HLmGq`<$OoYlMGjz)K5sLPyMx~F7xudF=kBs(_=ZJQb4i#* z>0#19>SJIil`KI5>^9(nPA54dn^rz!&%^}-m&(X~Y{xK-`YJ{J3=n;(ILgVARu&9% zMLqPPm7Xb^e`1Kh{E5L{2_7G^RkjWB37-b>{5%bo&!NlCVztzL5`v)IZ?Iwk^&!&h zM3vJ_X1>JPv^**(klI1`3V*{1Fpt_id(1qul;T+>NupLhSO(X)I!*lu*p0_rW?uk2 zX&k?t&v18&X)^hI!x*I(ArZ_MF{SR@=<`@BjYNeY?k%9~VKCme+vv1c5z2@;mNpwU zXDMGIGV=64{{S_y8;kBOPrq(HU-S4}jjf{)GRouFd)LM~{w^>?o1c0d4RL_6%F8(_7;u^UsMV zG8$Q@AS=;bXGlNYjZ~<5>2_$6zLRZ+9Lt!JchFhQvjR&Spw#=TqAKenP+|c-kr*I_ z72`S-f{rEAQLdY-PzeY?Y5a-&)C|S}PL)UumR3mhim(G}t z39<6xZHQGL2c!mer3N+Gm>scV@cs`AC~0JsJ!iy?re>}wSbYO#63 zARKlVlSZiIV1E%~DDCR13|TEfJ%aW-?`%)P__{=&!mMj(+{r2GpvYp;<(mWv3o#mW zi|LO!cZA2~kf@P`{{ZSCItkc%6{EM7N{FhOCxj| zN0vo_y*{zEL#2v`CEad6~;Zje%0zYKob3~y~MTGRbZNI}p zh}m3v^zJnf9aW3x(7q=zJpBVY=tEvnY~kRC51t!dO6>c~`JG}t=G z)fp5I!cAV52MJEg5T1$HP~20A#(?Vh{{V(DC?PsR&>wg^sIj_GjyR3j^Wx5Ct)jSbWD)$9;-I zqQKY@BNyF?*ekNz(h#aFmSbmBH@lETxr~yeV?u*}#5X2@-%%~9>{%68upKdFT2(JeQiZNgH=T02R<^xn?DwthneOh(SH(}J znWzsCqluqH$>ykF+DH|-1nHs%t-d7O)oUR@#iTFNd=+$&>dMW$>Bn;!hRwWLxK3Vg zJV}y$M($8*Dh#GoWK|B$suHw1QU&r~D86E7qtZ}?oPbKJ7t6-WBtugzF?s=Lol+*D zM3Hgld2%I^QUr%m2oWq27)2@uT?z?iW@m2FIZM4MSBHas3&sBc56b6bTmJy_hHp7+ z_vRGLWzrzWVdzYvMndUH1Z?bITN7d043LAQv;a_VgIuE=YYSUBl^8UI)e&JvbtOX6 zw4^JnRLGGBG8FR`B?KJQts_r01y+^9mL-T5#yHkCV8S&d&15L@141d5HkL<%^5f+a zWYq5(NpS{kfLDkkoxo)U$W?L{PbbU+)2$+-nnw|aBsQ4zo7@IgVIu|7BiE#W6jF%6 zEvtN0x!R4>;kHaN9G4_|tdGTHV%mujs;iV>RDi{uRzk*!Sx(A)z+{^R4J+|P?AF%AUu?8iaB?Wl_Y8?#0Q9iq%+^AOBw{WlSY$60ze_P2fWeB z@w+Z!4N-#iD2=3zL(ufPi}H)99~5FaAplIJsqoW+fhh9WD$Hty5Uju`Yob9ctER@3 z5-O8>)Wi(*bnZ0KL0Z80bZFE|qB~KH$cz?@Qbx*9_UnbWRQEgbM9K>W8W@tcGyr2d zjS6hNl^3H*fOQlyE54B~HI#ip;U!q8_2ubfkOyH}H;l3a<+3unk)?IXdd89BP&BBD znjcY8%HThjDw?GaqvG>JsL-*@k}vjmC02Y}A>|z!RTapL5~br=B`kcGnbtXi>yU`G zuBhE)i%KZdh&h4T3*h1~RWf-_bS6+dsY?+mDH>%U5Uf^El+q~G3oA1a0=vi-LmSNV zw9&Pd)f8d109Z;GAqgNL+|jzjEOIfn)ZhZFk^{WY3Ut-1ESnW;k}Zo88aV=p)m}yh zt~r}9;1H2ZxDz>@46MLS9L1t|naa63PMsv%+X)CzZTD)7$0n&h0G}|v)T8--H0wLW z7g9#3;d zBVXaYY?EjZqm(Ub$g!ic5RyAox@2KWu~mV>b5j;Xja6P!p$l1XT8m2}s2UWLYZ{+0 z{uEl`032pG(;n(#8G-P(=kT4SaP2azK6m0-9IOP0<80)BlS0m*1%lrZNcI|5t*=!v zAcFXV!7_Q6g2R~m$&3PfvYP5B0jj||K%h3o!2B14aM<-s$oYm)B${F*ov3cRTEu|a z@}db4YawpVDUKt~d^a&G9HA*FH|bgo2g~#M^~agSG{_50>^B=LNFS^m8)trMtmSS{ z@@8ogRVY`~q|qMWspv(MgKVx^6z%M<<+r}^T=STR`g9I(b&wXJGu z+-+_B924ezO3JP3nFV{W{{UQZ#@x|kg~w)}f+l#ez>gHV{iorUQ3hEWW@gf*ei|PW z-u{IhTcwZ&O&Y=}!W^{N8w4!+&+2TsO&#Wx)U(zr4<663G~KLv1k zcum%^nfagX!Ipx(C5acML1I3-K~ed&;3qlu;B3frh6YeNvz1yN{{RzE`H(lm(Z=~3 zt)k?`SHd4SXs)6GuF((QWzbjV(dUw zK3HNch#h14j2I3hn9C}`6tW8*qRmFg_>FFNH$eP_b;5O3AEqI%89bG@4B<}V8jNlh z9={;Ao^y=Gg4xso^T3JmyNCD8#0`-}#?`hmGc0+A@)~JgLJ2o4hxXKK>C?6--eu(7 z6G(*t($#hH75iZZaYi3}5VX^K8BhT1o*a3LTFJJ5l|*s%@3FtyzFlvoC3#zwU1KvR zs30uc9)sf>djo$>@gY85Sz*)^R;?sz_t<|37D)gC!|RDp4YRq4VwDjpA{jN+jcpq! z9jKZM^Zc<=^3W_+3^C5a7~z~)E@`zWD<}AGZ8g*0zxNCh^Rx)CGA`iR9-1J6Eq;4b z1NmaO%<)N8mB?K~W47BH_rI7o(-WK{Cq8DuDyRX}Xn}vP`wyo0UQou6@^BDz*j+^M zA)3oZC5>I+cF@Ec->BaD?fH%SVe0dpO|C6U+G&A1^{{roe%(JjNlD^M#gcrXQL}&> zNGE1B*SNip)2;xxhC)RliS;su27v4I0CliMb@#zzcCtgovVBKYCgfbBE-l0+Yu9_J z>Th~9Zmzmk=My}en=*i|eiN}fdv~s)w!8u5IlQ_ECoH9Y7X$!DU5Fcf4TIAYTmv!} zPy~=S1RA=cEMH%K-9{rb2WCK0BIe3VouZLsS2An$P~6y|U$6D@#IzjEAc@t@>lzQ_yX#Oj>D6zDBrjP{lBibjN)?!uoyKh8X)a=+ureHaQ^_ao?|zgmn0cdT)Cz+ zS$pXxa6-3Z+VK_QcYyQoqY0IRE?_!}6jhX2Q@IBBAdhaFj9ZCWaNFfF80|W&fPT$9 z!S{Y7LFmyN+Rmuuh_C3TO9Qo@*xf&5-YCKh)?F6F8HwWnbO1J>KSmYutbE|%IWzwN z5QPJ1FDU`9O%_HfFZ@hGeszzaKV&?}%V%@BW^J^?FeLn5WOmIrCgcFDcXwDnGvHLH+9tim0r6!0F*W0e-xH{lu|8Jh#VU`i4kQ7^9}i8rCtGC(V0m?l+9#v(~Usv@w}+|O+y?@wz6;03RT)sZc98l* zodl9VAyLg1P^GPDj`T^kp5;`Ma$Z&{hNx=lEn3tm8rt>_+5j#JkV-ZsIe&u+znwzrimekV2R~AwZ;avP{0il?4h)vEEu3sxda2HN>Lc+gD`TarBw}$ zOw}ZSkPV$OFx1DgI)DznT9q+e7*12gz#?85nK_9Ijc%#}w~`r@ZV1&NwA=<_u?Gu# zRR=d8BETcM)wofoTA(0y0BR)v0EB66tJvUUmH2dQV^L*LT7#=WZslm0fCLfLw@uf4 zTl@=yL|K84%wyNm%&07#%cuiu^sp&gin4IbV)#l((jPPs|C4_;8xhpGnN{s-s@r{Stbl_z2Fs4ErLz?iP6 z@1y`lQjlok3ptTQTD9DD0mh;_(g=e%JK25O>@MKq&8)CVjnfFYI}j|29<5So3V>L! zswuh(FRP-^C)8)6mN7<)wxe#qJGC1D2>@8=Ep$c*1Ih%h2aJwLpbq;T)rq6fXc~Pt zy?i&tp`_ zEOM#bf90awfkjCxI9E3GP9c?JN>S=^&%G_AYD+u&Dg~-yPV1;#fg}4(wp??p7rES& zP{tGcM}6y}vF^k%)ZVHr(pX}Bt5_c}_%$|Vx|9XIO+*^MhJjK*)Mz#8Z&hwFWO4@y zc^pja3%Jw)8cIji5l3Lv=P^BsU9m1~eEvZ~ZZ7%(u^`zXcO`-A{42St7-69%=V>-s zD>2~IQ3A1_Qvnjl%jOD}qB>JzM2gNoh1#?NqCn#1&v1zH*(mbS{26s+i5ab!hKpHq zKo(|^*_ix7Ne@wuGx^;NgpfpPu5)3(+UAa@7SBD_l7 z%K#EsTG(vKb4to}4x?FirVL6K6}=Z+$HYB)fb}b}WvXRkWSu*DS*(uCy0#t1xuc6| zU^f&49qeqRchRrn+UPLV(A6x$pKwvUZ;LTjj>@H^i+-iBiDs`t)oYL)m9G2j1wV($U`RIKinv*bA%YfN|uQ0<8_LkgUK`9WGZ>g!0)=qDrtv zpmjt>kuAqLqD3BNrW$RSxFTq(l*-EODQF!{G@+V+gs~;n;>qpeJ4OY^8;*bo*QlX> zU}a@>0)Qd*HbDd&H{yOK26T9#So8u_LO_5@Y#F@BqD;V%fhy%Cj1YDTaL4KIo1lqp zuv+qO5TRiqm?Rvgk(M$;uca8s~S84Kx54*KY509k*)Q?G?Kh=p(WX1l?>zy z1(h&Mk>t3gjc1A|-X=z3hC`Lh!yr()PGB?3G|xK->&}ZVs#rq|kwmgFTMdw3C@Sca ziYfcTGR#lip_0}!m_OK$2)xR7WB_I*g3}0C-^TUE&#t3bZXG6SzfbrJ7k(wDKc)^P$W6k)hKX zGO|v_CNAD-MwOe-%)(SzLitlG$l9ayp_VjB8$@!*zJUY?^BBx40}=QWERvY|B2X{* zBE0@$O;-9ss+h{Gra~E(b8JehH{vl?cL2Kjh*#AZGUR^{NLb1BIvH9RiK=;2$FA%Be=I#8P*X)TE#F9pVLv?ilbWT9e_gMWiNuVECV8KB)Y*b0`(DY2|X-P(P-3sba zEKX~>Y>5QAUJAFed>n#?&?=b0MJ0YbofT0+u?EY?~fLqifE)Q85^5t@GV;axPE<2xA0Jras6uHlS8Po zzRX7T^09xN;n#-qxy-~sw*`;>(l7VKS2D)pLopO300F)4XM5i6u`S3vK1P^%M5_Qz zjm=p1>y1Yb)g+NHVCo4vjW%Eo*jcqfWsg7rC)=sTamT!U^HTDBJ`jX@T4&P4b~;os z{EGwh$GPzS6H!rd7;z(VPT!sj2Uo>t_-oqvw3eKUY<^clv1I0bhnh#Xs4wZT!r5;M zb2)+(l!`O)F*=&N*c(^X$I#*ffq0*sm`o)xnbx zS>4IDt>jI* zq8@qVmtd{Zfh=!jSy(9Ez}3*=n3f_59l;>}NBwXv4HI1p=_PwySU@0O1ZO*QQ}&v- zwyPXM7~H`iBo|vZ7QoLhswVKejK>apD>UEP$1rC07ObI&_EuoumG;Jx@%x*bF_(ig zlSa_a=p1@=Dzhc~kV@`o0(+gYU3f|0%x|T$EF)4BlE#Oss1IRBbJYH08!L-&J`)aE zRkI=E04gq_qycmkC>7k*<1Ot)T@5{a51Q^EcbYcSpPkB=vy#@DS-(x)$P_MLnw}CK0~lOOL#!?U2W#hl1sV$P zX!|!8&KS_*+nW+i$u`DD`9yiY3*Rl3%@|n76$Fdw3}}Jyirk@9JN4~~&%@4onNa0` z20PYieP*8Mo9GEU)$K;b;_>88eU5CXh2$z^fTLL*2o+?l`b~|F(*`_; z%hob;#!V1XLDX&mTi?Fmb>98)n&NdN#KObR?R8aH>;lt!z0-1vpo0jnsE(giG-p8*)+ZE@DoMo3V@q|@Iqq5QW zuB3Z>_ZYvXm$qbbb!bO`Yl~WPyrFq?nU>YaQMW==xUb1u>0e9*X4#{y3f0!A*%$5K z2QYJ64jfbf#wie}H@B><*!EZHfeAU3$W@A{BFzgMl0QD8{-+K`($^cK=mN&-Q-~Cq zV`#}z1+tM=N3cB)?bGtVJR0XohDlK-kwh&(1~dTLtJ;NEOlO{F%QUdZA}B@;zKh<& zUHq&M&jh{`bJIZW3#l=`#NC$0t%;Bv4&G6yWCsoWxyd|6psFN-uhVVhe9lzN*{ zwz@l>{p$|C*1QFdRvFpZ{Jh0_i8QaU0{U*_5gsAs);#7?baiz)RFPeJ3$J^@{{W9t zq}(eoHY}keU#fuL{piA)%Vdq+ZTlzK(CFlELP+}km(MV(?ByT4}EHlUy-wmdVz8A zKnJ=V(2v;wQ_8Ne%ms9lz7Y8I}e9Wl@b!&x`AA3Sa`PKvcXUfAS zLU5{}n3RG#+JbC*kDqG!;zz-bZXa{Yn(3q;&td+UrE9=oJqb_R@;G$^<%L}MDa|jI z&gb-sI<-;QQ%4lmKRF!zbz`rzk3US)aO}en23t3&NEc>E*>|!wD$J*T_qpgflg~2o zS<<(43J19cSnKiuN9l#W&{+u&57uL0OMj8}7_em1Y8pw>mvbZ1b#ATi z6Z}p&jEvDtx}uU65&>sgQ4Fe}uEcj|Mip*Z{x9rnKS$wnjDGp*Q4=+Gf4Y(lN~$aSWt!tL6v>Mq*1A9$F7kU=%F^FIJTr zP%%NT&C`t0%$eCI%ob-TvKuF>h?2v(B+>@%*QkI1I!iV$PZYR{GN~7$HqxtPi6kd; zv7)GYiax(EkiwbsFFt0QTPj?VND3@M{IyY3A4%1xajG?I&=qxhn^tke$m^Zfo;HRw z0G{Xgyf3TE)8&OB1A3Q0+PYPB44`RuB{fPw7$W8F455748Lozu*+T-${i84N07W8} z1XB=6mM}HKd^<43t*NA|Eg+?4vIJ_YKq5W00u>Ph9VJvbf=sa}CI0}2Nj|Mh-hd*7 zlKx|AqyfCjRee))Lq&$=eRAV;b;}%cK@v%B>;R52Zb$`zYG%}nN7T#$Ml||?AzHwP zomzkl7WM#w>AvRL)B)H9(!r>QZIsrWv&@OR2tn3&It-;p_}XLTFsu>C_Szvrhj2Ct8lp08DGD z?u`Hjq1kEJDJ({<$S1KNF*V#B_7z80PRgVciBX?t+z&@;0EJ&*rBEBYZHj^18^aD` z;)@>S>T2|&9c{6zW2CSE{5AnBEC#Juam9TbQVSyz)u<2!ur*75s-P$rt8KMacfvD5 z0`&}7Y`p@ZQ(%w}P)d{lkg8D8VxlTOCFUp~F_Tjelt5m9AXXd6BRcAB*<%F*Sdb@@ zOGbl&#YkG*TPXEevvfm}NCQJNF;RdKgwQ!%WU~WbQUEL&sM6{KS*zVpLl{+Wg~6&0 z6;75g3k^Q7Tm&RHmjc72GZxsf5#>|MpaZ*>)D5+5z;f)d%`)!V7C24mRUz1{&dh~? zd7@c;N@9$pMH+n}uundJMqFvK`G9GFqOr(Tv_>CRqp3mE)S@OEDrw)m^B3-Zl0 z_E1HUfH~sCt>O`=j_0C?wXUSkN;X9+Jmv7^VDE=7)|HN-74bl$i+i6%A^! z%Ty4JPRgfH0I&qCTtYlx-Bp>){BB-GVh99k0i)@-48duY?{z8|2JfcGhaKWzu_f7s zkPnxAcLQ_fxa*{c45X8#YYw>A6%j3z0nb+SKdVnrL0|#sRIsYGHHzYHVj8tKmOvB~ zvifViMAm!ld*17UM$3d!?4k}iD~L;i)CWVD38!ifBGxqQE3T3@C}`rG;?JmSNg3Tq zB|^M}0n+3inqpc!l=syWsfUO~8ncXN6xG|o)DC>&fnR6a*soT@l z6x&wc!hoWvJ*kvzMV%)WF*3#?9dJkYyhIVK@>?A03QvC7>vx$KqFS}G*7jY^Ei)~ZHGWhYQk&6GR4`Jt*lVJ$vr<(d;YjH;-SdU#g1Dn z6+tBES)Ma9>}Vs~x<1#Y1|wdBL!2IM5+uu{NR~iqD%xzVf(RhUG&O=ezcEN4twr&9 z=8}WGi)Ukh9I_D7$lC(U#0AuMD;o4SgIeh`aE&}U;{IdGS&o*}LhtHhplWX9u=lgn z5^akY^IVZ8nY_kHP?D^q>MTLJAcC#fFg06r0AiKlnTA9D_?OS;nSqhXAW-+RdwXrU zKQEaV65OM)_HB_PbLG{$AyBJIbg|O9b!tTfvkTX$fGk;sY9LN<4kRQh5tOsAp{_P! zW0S+Ym~zLnUU02aol@Pop*;teD1 zJB0uNtU(y)fPg)1TIPBIq+>*=Rm2=X7f=G_Lvn`*Xygjwc$giulE$jfQf4Kus?pqw z9+EC@EzW@u2lHKxX^|MRIJyfMnot&p#002RlG;ct6wV(HR!Hd-GarTDb7s`)&>BJr zCu*e@2Ya93ehQ?D7IKytkyeak3#4kegFdQj1QiSpky29PIG40BKum5P3ih9eoIdM5 za~}a>JjjEx+1b^jez#{@g54}IC0pWFI)$mqxfn~NvW9jHDHR#DFe0^d7xN~&*4WXr z@l%kGN#V@Ps30gw)q5$^y9Xz_A6Q@=H5S0HH}igEG-#YR%aOhlVVkdTheg={x1qK@Ag&zppzfyLq+VHo8xs^$aKH5}Q%#wEf zu&)l}JmblyFvw)mS(Fv#J+PkY@iAn6aXv^{k`zpy|sIyFJxeP{V&Lg9prTqsdsYt>uW18eKkwkp0~ z@Y7Vv>_A^LS=2_JwgJAx(6hE9zxkqRGAD>f1GZfzX!QYhPylER&#k<$05-sm*tN4B z7jXd;O)Oqp<{%QXY8Mrtc1El9DLOivZ((F%*;`dk`6P(h(m0oS_Uw(GTxx@-X?lZh`5va)AKkq&MTF*P=w0fO6YwyPth z@4rE$P{iMd{{Upnx81c(HOnGdEc*tn=n@4k;B8x9d{9|zgK(}g%L^;Flxx*N*6xOz z7AtO`VxDRmnd34z65+MS!+uGIJ{U-2YysZOVDP^JRTY|i$n#tcPLz}fV4}bqlke_M z7@xC#L#fTZozJbhoyq+06~nooH6}uA^D;*FQ9?-|pubTzyRipgC@zzGnlR*kRWoIx zmVEjovM#nn^3hXKC03o8tyf55PpN@wP(q5<(D56cI5@xOL^sybwV*q1FWqOK3%L?T z%PwclWQ=-tAkZWLeMW%Z`U>~D#d*duNV$L=!!EOJw&Lubq#OF;$M8drnKJWbq=BT% zkBzj3RS$bIlWaY}>G|TJ@eh#GD?3LH@X2o(+>X|^Y=X8e7?xWb(OhbymsF2qhnJGT zX?YiTutyyFjpXXfIb;X6(mSN=uZCtQYdfr8{xM$XJKzG^cE&Dev(b77xf>@1U@!oqG_Zt zHl-@Bx5BMf!F`mQYQIjRI#we&TZ(!j{4H8Z1d7|RCi@%GZ*G`9B}L(9!4{pj8U>JV zCvgGzi=eW&KA)Gj{@;90bN>JZk50H$01+JDxjI-4y6y=bJ+BGyPcAIxm-MZW!0lVv z71+@~rV{ZJgrc&f4qRz%KsAG zTP$Bd3c62IXK{C9+W!DC`b+pn#-WclBvXZCm3r()!Zi*4TmDywK2AfJM$3YIK#rqe z*F=NV1MGGN@dJ_NIiQ{ubs>q=r{CxE*wL}m2aijMX*MWMvp{%7{w?rKzF5ciX-jE# zCOg|j>x|c9Q~@19@3tm$z9~nV$V?GQaS@A2t7F^f$_d}N{oeSe%1mG^VJh*3)W)_# z2^C*EA2r{g7^yP6q|FGD6mccdDh`veG}W;Oa<(t8&k}VjY&*c)V{}-mfN|0WM#`<{ ztheDPBXs~9>_vnASd{Slor`7DFQ@w_R}KFFAfft>zf5_|@id6a`!cap+i^z;@)rVl zb%$FFJuU{_LEqcIxcT9y07h3@@9B`P$oyMr;gTSmtZcQnaetxQVdsW^JVlerh$IHX zZ9?`2!M~MYd&7?nvkXpQV`CFgBnua-Z{|nN>lRnp$HSbKVY88DlT*axDwQ`t0;CY$Rt`l?-5o3Ug+KB`84ef z(v1zeV?pseqO$1n(@9-I%>k_xZ?Ph+g#b_PM|*goU;y|po!ULw8U5B=@QC`_0EhdJAKNgw*dQ}w^P}U5D=Z(WOfkF6I^#FRFsDRew9q0fOzr1W` zs-+G%S~@sO1>C9LdiBV0^A1K|utsK)lL#EPo}T(!#s)tK_wg028TXG(@7 zAl;9c&J@1Q_|0Un6kVR>BQKbw&}VDv;80PiTVlagefMG`Q@JdkH)edObVs(Eb?9$H zDy4uIaLlC9S}a0}3PL9ZT7ht@?qJN2LhB&LH#RitB#?o@2>!VBJ1a0f6@g+9OD)3M zc{MkjV-2Nrg^VmmBDmyH+$Ae1t3WoB{@5d>+#rjb=!)K&I3WGFH_Gm~JG_>Pf)qD58IYVHNb zH71BevU(UcVo5x&Ng+;|B4w3Ph*Z{+tIXh)B@B0Ja2SHFr}ehlQ2E++6kF7FM+o$U zRUzB)9GiN@gH94eDc`y13b&xPn$!xr9+m^W#+&V|+qt4nw7@*sr;Hvm?Z_`h#n#ETcx2I_~KVN}i&_aTp*t*QrA4^8+(ncF~AmN{|&* zm5HOJQ(=0HgnTAOc4u~?#5}i0X|kKmb3zp;D@h(>n?zvIHUSZ$DJ5bmptWouNfZDT)q!73M4d=kX<6781jeOx0tsd>_^h&8 zgA}MjyDLiz$a%7pOk$;`GD_5C2uN{#%1w#UqjY!@&g(Y<# zh{YrlIIk^+h5pT*;6@P3k$;685asgJYs_Y8Bw%*dZh$*j^r-{|y;^|v+5OB*sohT6 zeSI$cq9h7OzM@nO@3&Kl+|G0-@X!cOlR~TziWGl%wvaZWgWLiwoXr4}NbSAsNdLyFmAydF0_u(` z9(svohh$*dh3L{KpeQH+KtVtON=g1NRbgipV8+=~eCx%-C!tiR0D4=wuW(fK*jZ%N zRG?6$^O>cyu`TQx$LdfMw$wW9zT4Tn-5CQ(c^Dpu?4f`gk~I{%>PccY=q}AY#U*7s z76$qS($(*+DADOk#C+`5k7N3ca#nHvI(1FNEn z=Pii<-Pxc^6Ype_N%tpu>+gqI%*bzzs;De+r^>d}!Bi~^W-LhbC{JeB;4zymSIP73fAMGyQ=mifmBH}PTEDYnqQXR*Y#f8;Vi_} zTTmZ8&-#4p^}FHu{y^#+Ob zgSI&4^Ic=oULW#eLHLxOX27rj2-WXM4Y{@}Y`_HsDu8UUy}1uAScgy@K__Ac8id zgl2birkE~O?aSQN9BN-Q>ao|N)De5FfaJRkj{A9pSx~AKb zRVXv{^-yEKOnBtnHT9KYjBZIc<$=v?dv-+$bM~^elgBtS5df+{Rlm6sq;rB?WtmCR z7fzC&S#Q&o)tk5Skgt0(oWB?ZO)RaRM}7u%WQ$jo+>VDh&Ey4 zRRxEp$V&_y17LbufFg$5V`lh~_QK&SlrciyRD=zH(yQ;N*sJApq_LZTW5WyjC7Lv1g4-m!LgF z-Sszkvw4mP6Eu=Ms>?KN1FaN&fd_59N9q9I=YdXm<)xZXV?KZ=6a9X8$8g57DZ`C` zvGDL(x3SM|mJ6)2;=d4hkqQ#G*Y(9sH!{p_^1oyG zi1kjQ{i|!opvT4NpC z*azXd29F|e7vl#kO)N3T5;Mspfl(A$Vphd|nBN)S1M+JTWCNz9KfQinVkek+auE(+ zHymSJPP*t`l6GJ=-03x4Ny7~Oo0{{r)eD{_1)%3DmgHQnZ!N;($$!Jw(u-;&Rw~O) zsg8ndk!(R?PWF0<%)dABJkb#p4do!%GTMW&8cL|>3E!vZSi5+43jYA}lro6ijNV<9 zqalio)Rv>N>AlY1!aC5!0l+x}Kb7M=K{CFQ!_H=u6BKc&0Ceii#OOdnG5-LRk)d?2 z(&h_Z*V{Shc`UGvu9>gpdlCNt37%7(Gf5=*V)2hBF7i>32MfKC>?0eOBq;qcPxzV3 zGOFbBa*~eGD+ydNBGjG17txK5yZho#nR%Bt_qk;-4?3DBaH~wVq^ShBKBQAbnkqNi zuzAAHdpDPl&WIIFmr{;{r@}YJUHnPVmN?%SaKD9u(26!YP#pI7-9f>|plq_noI^1y zu@s9N1W}~eABLjn(A3N~6fyb^V}Sr)yk~4|Xv_uouUOjtKwzP{12|Xk;!(E1C2#d4 zVg?@vxPcaOnu#Qmbdqe7{{YqfK3H~(h`wUyHQ~Ny%CfqKH)#*mkMxWEZk6z_ix~4c z?jPtxnWY+vg36?nIz@m*QKMRSJ**gWzlD)~(oDQ{B$2|>0t(oc5JsW6C(S^0s*7SZ z6)Z7J^M4j>*A$a76Uj2Z(Y1{tf>T!hpaZcMH5=ar+!vlEf*BQy2MkpDSGJ`;5}IPW zt0^1ZcE@xO;Qg;+eAaln?b1!axCvfg@XjH@pEok=1xo>1hy&Cs*o}&j)c$y-IcJIo zkx8Js)OW9a_dR-iv8%ic%?!^7lPcSU1t|XjcXc1OGfx!cMaf3SGpUwZ&}^L|*K@eq zhLhKNBKl*!pW+UlddfNd_DbLf`$)S{ay9_Xp2$s~d|tM8`j z{e^bGKPvIog&ZhWplmYg*o`bJy^r?9{{R5u8Hv!YnmTo99mQ6letpKo8``nog&dXV z%bJO{WmYv*ZnbUm+ZJ3ovRX*wKI^VLl`YF&M~bPrgNwZKw2eT$R**oj003`&2XlCv zzQ*w9jPk6=jp-~GQ){LLL~Ull9eaW{BNz9CyuFwI0EWvX z?5isyh|aCaQ`mGM0;Gm=1Ui^2Mqn;NJq=w4^KpoUr2joVf*A#by+{UP~M@m9S zN|yMDS0zWafO`D!{{X`NDRC~VA(ufUg%m>z-($TB?|)jZd2ToJk>YLT z$WzQxj8{-3D+W+N1x;C^PS;@XjieHTN*+z@-=eaH&Iap!{gk=;ESx#A(=lnT|r6uWG`su zKa_SJ&@>aW(j44_GX!YbSgk&i{#BJ_ey*U_rM`mxU2yjxpeLxdy{MK)0DCBlN`MD* zWDT!T)rwD)dCF-M%Z*`D&FUUh;zPe*QmQV))Ymsb>07*6863+Z`o)u|(5~Q}^xCK! zHT5KJ00Pw@2HT>(ftroc>Smb~RV|Lk*zaf4(yM;^*2cwiyu_vSYb@=4l^X@WS!NUv z5q}dZg&Vp6_XWkczOpM=VnV8dPP5gO5m_XaQ~_jEeRVKW#}j!)D2=FS-_`2UO%Ymw zCqTMj39DPuDKsiZF0A&#XT)HnY9xF>uqMK(x{Y_;SS*E(rm?4SqN!ec=2ZZ~gVc3K z4%C(eXGl}Ks_R0+slk_H2rp`Lbj*>Lt7j-ppM@-t10BmUi`-tsl~P7C{{R@^?D7Ch zC@Z$jK}iXz$)m1P#f@scNnJ=~Ti$k4TLlM^nKhDwT%Ka;m4ItvHL9rHO05toCxt}vAdnp>2gFHsCqPwT6}3ApzJsZPyQ4Iy6BTk{%x*7bx5Ji0~;zok@zHc=0i*(vlFj51fGmrz?D z;jVv(7>df#qfW8~XKD(LMaZ$mWBW0;;+@%|iB&{$$_{Y5bj>&sgYhI{UO3AIaN1)s z08!BDgd4*#6tcZ29@7*1k#H2G_nAoI+Dv78b(^$P^##PlhbEq zl)Q>;k*vP0F`X(4FgB+`>WKu-z-rSnt3?`0MYuj&K@OoA^m-AKQ!=`5BSzwepyBk) z0P?j`StJbZimuNIxLNW*O4l}m%v7UC2pV9GQG+~dFPj{3!oVG2&P zwtxx`^G6sgfkEg3D%R@JtZ(VB;SsSv4(!pfqO?Hse9{_O)Hu+S47%A0bu6r9a%@d*6kRr0u-$^sj*{bv8K7^ZZKHs_S>eT&;zQ04QpjZSgQaEppZ&1ZIxxsFv+B- z0QCg!D}B_)%3Sa8)3sIB`mt5KGaHs3PH7!X)krW#6u z8Az{CI;(If5<^$M0QjQ(Uxvk3cV$&zqo8dyYBdloWC5&Lx>Iik0k+ffhq=j3-^#oH0AHpU%D=DlFm|P!nn_sV zvFRA~MhXy$nBcS1q4r^aPX7S5G}ECFM3wP5FMh>rFzi5WWZC-cO=iAVkMR_Hl^^?t zG+eQgkSc})$^`BO5j2XV0Bl%YSGQZUdU3~_1@kvV{0kwifh-1^7FLF;sss^T0Ud8q z^bVrq!S&GSh>FJgw1B-a^#Qgh>yPx!inXWrvWPi??9Vf3s!ZbLc{nB*?Rwz&bP7q*ht zXaE2Lf!v!X5Zo({Gv**$^t{oldKE=8g*_R?Z+Bo0zzlsb@sE)pb;~kN(q5_0P zkVru7qCJ<|fazG&ac(*|bc~F)`oZ+FsH5Tk0I3Dn*qz6b^%^p}7`1 zbg{S?nabpNo6s^!pqc}vpKgO2JD9nLAqzz&ULiDIrPm&xO99Z|#6aqL{@5Rz#||AH zlF?Ic%di!H72ivAuz36-`=wHdF=cdTiBouh#+}{!x)362}oN zSo)~$0^a0%FUuB<;c@W|PM#rl;iGHy+R20M5aG^bFtnX{alP$~UjG0KCOLi^o6M_% zs{$(m8SdDtZ=`KXDBCq(V0z*1Q^EZ7Wg>`zvoe-)D@S{)-_ij9o!W(W!2BO+_-1PI z80x=HlcelT?mk#sIrgQ@AHyP#@cdg2!u~^UyJGW)@OnmwBd(@lztMK~!6yypT&&Ot;|eWx=%%%A zi%V%x1s|QBzSzUOYwax~vPCRR{{V)xLiDjf>NnJHhmYg-VQJ!rMMhZcrsz=HZR@u9 zVylCQz-uJV`fMycR!)zNOC+JDF7Nv9&!v{A<31=%lSV?RWd%SBzLQ?(x&BzC_{6+x zJv`FGqbP)g4#0VmFROhjNKwD1__rsunty|Rrt>pAY|wh&qkJ`Bo$LKKIO+b>`Tqbh z4W#VvX1K6t#?46BPE1n_tnc`^}WmD>uB@V)YCbXTm ztT*AlH|ON@28B{ou{3u*>u$I~{5A5kvvB9XK$(`J7)T++++ckc_04k(Fh#8Cs=8IP z`Y0#%#4KJXz{67Oqtc^4?TjVFJX_)@@T%J@Mp>^>0Fty0-FLt8!}$DA&R4g~OKtqW zmMzD#R1pE%Irlz4!opVZUL_;+(Y2Nvmt8gScZW-sbey^t8&Cmg5nEN*`tCaRtVwWO z{$D$nGEJFCiQV-}7$o=IsEz(z@G;`2hwB;D8P!Q{3l?BH5`JAbAHKi`KPrf_sWfqi zCBQ!YdxPmaZZ3%cjm-i!*26R{m7@gNRBE_Z_k;c>rcW^}DXTFPotZsWC~gSY*SNbI ziJZF% zdZl2Yqk21aWwBpAzt9-QzN!j7FdEoEGq(Egl3R;g$y)YB^sScu>9HLU4~PCOec2LO zp=T`KQfWo&*K%uW?l0&u?40A7ekNK7yon!5GBpbuko$Dmx{I;<2XM@S$qKoU-dXjk zmh_S=5!a~~RuAQPBIBMV!%gK+oKbAZfAGSQK~K$G$dZajb?kbHCYFK0fZT zy0W>?glFT1MQGweS5C6!^#=5@9VqY9p}=p2+`xRFE?2dUis?T!D0*+b0N2#-i*t{0 zwa!Lh*X#1tm4#|nBEt^My)U@-tHyZcJ`~BAmMMp=S%VJ5*lQgPH(TpQy4_Yx_fHFh z{0d2qBoKE@d_>{8C4rZwoj3Gquh9JdxRl~-#uiE^QeNBF%;BGk*}IrVEK>pj`Hz3= zrY{apW8Z6S4mQm>f#-Cp5^7K2-@oWF#6VNY-d=(_#kW0e&j$GSiKh)2MG` z9ryWvFMKmdz}mSR$-tkER~iX9Onh(3s%Bti_#GLO{#GDY{U+D)zWAlMJib>||KQGIRbr34K?08Nw763j>d6S2@7%fMXJ1O@| zvY-{x3m0*oDWP7jFf^BA7$E^Po3nadkHP^mIp~k-y4RPpBqHha7jg!-RUqx_aGsFb zdLb<1Wwq4!Uyf&#`dNc^Mb$%5^s~3b)@W)WNjgI6ASDIwE;(C>8D`;gmgh6Af;OgO z@%oD{lB`K!<~{1vApuAoP2huqn1SwZK#xfiYyvv9b!C<3a2;YX;IAp!yAqM#jc z!Y?$3h#X;Aw8vO$&B z%_{5w{3xUTA?vbZMJlnmx;u@9ly+5%%%HGUqpMfpmI$F@^A4iQh(eMqFgnJqZD6a0 zaQ^@@MGVWPhzcEb5(!-bFvNoDOH9wFN-@BD0+Uq(w%NF!JTOi-@yj<5dDQ2v#Uvlrlj|n*B!3w3a=|49d+T zNF&e09=OfqUUUTsiWZ&Y-&Uan5JX;JOkijRSZ`pjA!pDGa-zfzYIj;6!j3d0(4|>r zcV=3mvBDybIOJQV0u-825DOV;6icC?W2wj551!fxCM!k3FQEm6vW)5llHOBjgsm%v zg|zh=hddtPjEgMWi)pgpYSX3#&5t>azoFyT7toa;&OR=_kP^VLwLU$`=DjWi zU^pwK%RZpA-~cRB{{RpUSO6H+s9?>~gdmBctS6@u+(r__p0;eCd$aBJuX@BbaxySb zP>XDccq3^c{{XrPA2XzZ{{XtNtGqMHYRl!_g_eYW2yj7>o1w2&Y3dkn*ooAfB9+<@1%>>7zI zXxX4mAZW2giZV_Vzbf+J05+8;qL83g)w+gaI(m8mXMWhS`3sfF^Ckeri1~_!yTMMQKt64NXim}yuSXC5StS?g;&b(L5(GpG+Ou-2&uSh?{!HsG6 zZ7rk=GzBg{iO+VHu=)8b>Szw`9!S%N@Ql>CX?Tmxr(OAKh^0(ubRl*8H(gy-5>=o9 z)@`pG{inF>`HZ-1!|P>YJw1StKqNIV`LPshsm8tIJftC?FAQ0SY^e3mN7EQ21tAkc zF1FM{XktKP)Dwym?bVgYc@Z{Im7e!xHad&8?swjeZgGp?)j|A5Fh%ZgZDKHRt!kNS zSNVLfvFC*mLfk_nGY3otbrQF->3To@df&fET=*?fHbi9WG;Aro$^Jzku9G0KavJ zi@Mf!Bv)Mndj9}BBM}pLL&X`4YnO~^v+*{OV1q|UJv#bvvv_AQPD_T%<|y4CEdK!C zrq+GVC1HX@R{RNpHAEpN^Y6Jk{Q&8YOKf^_M=S5wm!DOWe8KN%2N$s<4qZOW2rq#A zRRceh3aoSs7Gb+s0YFkoJN5toNh4rV;ob~!S&ghIxA8G*_UHj7`)$5AhG&7av1yVi zz`N3+#;(-VZmRnACjOqq5lF!DW?J7$#`JHuq_0q6#sf$rH|xy)WUrkwUog$1 zw>|7VEHBAo9LIq?STC5#2ns6ByC8M|b@_jmKX88oc+99+EJ`bFLF|s(1K+3z>UxXV z{KLf=glO>2PjwMJ6W4lOG)~piNUr#o`@sl<%9_-AhnDoj?r&-!o};(`ZZ^Q&E-$&g zx8L~jU5)Yfxi)35%J%8zuzwoxUyNnq^Net5S_%Mr{&Y68wf#32`-1od#&aKt5)=#p zwpO$3XcMvRX3tP7d~V)1mPu7S*s}*t`aHQbp7dx{6IMNd7h7TEa<7!C(N2VVhzR2U z05u-KD5L37w)@~^jbJ3Ut>G7f|A+#0(lrP({NCdtRgOGB>Pk-zcPLRnoTtTKVO4z3#(y1H zu7*v(_8P2FAXu~4fAr_ZKNvja%Pj^9%1s(0Z-iHUUHgx&Cwx@OT$0|Nrnj;_RomFt zY*W0k%VK#Bwxx-+Zht{Xw^9Ck<6hcmY8een$9b2r>2%~(4s1?f*zfj7ygwl9B$GkX z;1e2yX3bDux7byXNUO!i;dj|vHJ*-1n0?u)e6b>uUqJr=%%Y`3k4QAc3M6(UpJm=V zll$8jU_u}ZbU^HO`j687nA$mC5wc$jy8RVf{Dv}oUyZ(+Llk1i1fR8*mV<|vL#^PD z?nB|a!aUR9=5He;Ge;AGH3uTf3f)bMtK3-%Hs7`ZPHpDm3nYWfW~FNz8okABU=e>z zeJbWG$d(q+x3!BEztiS6IK_T1@)YWP$nwr~%CB**svCy%k5Sn43T)M1cv5k<&hE+0 zwxMhEJhw>H@K-UWNX{&9cljV+81qm(K9u}as%rKFzvYMy3(Ql`2s**?g;6)@7qA~c zmf-xh#J4qPQG|evnmb^2U{1%adX=c@G{e3Eyfo!GGaA(!NK?N4-N)zO7Z8$ZFE&dZ zznaTv(mUINq&W+OJlVMwK-Bgw$RIlQuKNNkb;q79cy8Hc4Dy0@Itd2P@oDc>NBuG1 zG6}L-h|E_T71E>|)km$6I%=xGM~IxOob%$FFz()9WnEWQM*d5E59NI^pnyI}*(e}1 zw0Wt3TyM!q_?^Ns?ZTs;IuawsrnmT-jqOAAJ@&;X&PsHnfIbjx^xO6)rY!Fha&oT@ zR)uamVJ%;!bt@^ZhL2O*tzxj^a2Ly|+Q!?k09Uy*HXh^;<%{YJF~axnA4O>a78X|D zDZ7lUQWp|E0su7d zLF-_uY-t*+ZS2TE3aSC!i6ql1P$akDnPwIQP^RPiJ=6ho3k^cHy$7j&o0+ zEwEy?MxRR-DM2Dd*a57gJ1`*^SHwU@jm=K77AkcBzZUCi0b&z-0f;5!^wR7I4eDo- zK$-w;Zp_L`dKDHiW;p7NmM8|22qY3$%G`$n=$6Wn&gBVsLmSe%i50VGWsHGB)-cRt zCbhE`vQF|m;8GzRV@Om2Nfc?bE`>3nHK?p8RhocuOk`P+?R@4+k(q!kpeiS)cV^tO zw^lkzfm&nHzox8ItY&zXrt_yHfdWG#DUpa~l3AuGrP5g>2@Eh!jS{4PdVy3iU84N< zQ*j8ZmNG>X%y|ei0@PtdmHA*uzjuvw!yAZ) zq^N0g3^N&ti6~^%Bj#2)$$1($Mrp5JS>TL%QH!h)k2y~y@-en!qf3@ra`uZXl40 zD&tVl@TOd03=v66)F@aQ$dEUtDRzvUuiI0U*}oWy-CS8)-Ar_j!PPvqKYBsQkpDtk?+V zek5a-Si_fP8hvaS9c^s%6cq#w0@_1lsJ^W+;r9q3GNMgxs*J9V+MZWsMFr)tugq7> zYto`w6EGCCN)Z{SGPoqUrhhCvj8-)OvLjeZHDC!6KB|E3?z$w4o0M4HWIYUzr)mIM zEkq9EO*(_I1Qw#VLuxvQIi1R_vS|M3)1}Y+%B&ed9}R^JD1ySVD)M@mv$3Y!7O+2r zxwq3{RDph`05Gc3!`x1CZ$bp?O++~xjoj%5y0a>wX)Nef^es98#-HNUBA2uRy`S6mWXCjeUkRN8_ZBX zk+9eu_X)yc z!(zhlzwFPO&CI4)+6Wia%_8Y`D$D3t*oQi)_aH4U>;c5Jft3QntxvJPh7Q6>oHC*! z#Fb&5QcSbyC1|W)hZtZ=vlor4LyT$OBf~^dlC{#vW?+SlSz)M!v#~va2KL{MS>Tam z)iUa;k*=VWs(nsDkg=~+l1r)F?9RoSjS?O6Mt4Y5+fH2 zghcYk9FiF68c7v`TCuPd8Gr++7IkV}XQ|bV3Cq3|LlUSFDwDV>JM}lv*!;HoUMl|p z95_B+5htBXBdAzMRVJ(u4%*3W+O-9`IKj)sT|BN}#rcfi(Od>Z01Y=GC*hASK)HP5 z$($u5Vq;RleKb{MTTuS5P>iY4z}lA@SkOOcuMN^Hm~T+Mz+zZ{NGc0i-nVMHCvr|L z&LP6G)BVB>p?u3m6{JANQpFn6ysTIP2Gy@o?7N&qeXM*!%1xE~v?xT871jv;&WN$T zFmIMT?c+*Pz^!=6r@YxgZ*-tdoetLO?bqh!S-%H2RzYe)) zA(D9{%#v1E;}D*xqjx>@mLRCJdTVO4kD1&9D|x6?Z*w5_yKU(GFMoV}M$dCTPl-S> zkVIg3;nYTux^#emDHaC){{T#A{j(>uh+No%e*tw$~`zF5xu7mKeHN6N|N!S{6m zsDRQH^r=-l05qNK*J|s5?j-(KrB9fnZC7LV#YY3;hKbG=$+f}o+dfIE`bN~)99e2? z$J$SasUD^x>cnkdmcsu4E5f{Y?JL6IkItJgk;mPfFazth-<~o{al6~o@&b?b`eBDK z^1A0Dcwc3ZQDgGxK<$mJUeGwZyal~~bw>UW0Kqr(TMvYNr8s2J49rujz(t7_SgQhR z!3(%P{V_Y2@pmS`5X6K22mb)@ae(*}#gfX79k0-of}e+oubHSCH8CP{NqF-ga%m6QT7$LvF+D?Y)N?c#IcMe7Yiyoq=i=Vv@Pe!tPvp>S7WDRX2-GbiX%6f zbj&h_AU4Og*wo%May+m}6y?0dkb3md(W`KF0O>dF={2zJP31fxlRrBMm?=$s51@Cj+fBUs{rHk^Xl;c18KOFgcK=u+@H(#?TvHdN7+*^oQKL*F`rEpBT&+8 zR-nuBHoZ36V~Wo$_F0c6r(*3y5(P2&=?00avMT+s>Ekf=OeCFPk?FsZ%1y#5BNw&D zhJIW8l@r0T9%m2EK-P2z6d6X~I@kqV9^S^c?MFFqNSDfQ?FA4)(5K=9q1L*7qtrVQ zjWNoBBcD48wW+$Yb~G;jVCoz1{PCY(g?Uct*5V95CS^Un$5HMvwc&CO+E$H@mk&^s zrv_3C_w-#6O8uBQ&&{4}uay`J5dxt?@-bE#n6~AHo~6RA+N@S??(l_UNTmu7;l7kE zi4f{+d~d&YYu&ZE6ha!#CS1cMhRz4#tOJ_In{J)tvwl%<%38mG4ap!_&^ocLDl}}Y zK?+zX5aV2A$sig)><|^&y;Ms2OMCC-t@?*-5%Vt{CQ^YdsFz`6vt1S4sBKCXt*i!p zC9p7@5Z!d8x1)8}VeV|QiW@SJenyYo4(hdZrtJzK*O2o3Riu;}Uwn*4Pk_KGU7P%K;0(@ID3Xb2@`H2sj%ru}y z^8Eh9!v5<}rfB4eqZXxm)Lmx@weNU5|z63Hi$Q6qQrX=6{kqR(G=HQ{VoQ}>L+>Xb-K1%ojg zgCxry*@Q>h`LiKtyo|Jq5-*m?=SYH@LnO9g%0z{ftGV+; zx{Rt45t)02G&c=ZP9&STImybVH+cz+Y6{^B7D&Qs@>WJ+1a8{D7?+kf3mA0Fk)fIe zd_{vyt{`L)1w}6VElR5E7PIP}RIEM^9pAG}7A=4M>1w+e+qW-~CD<&HE9 zk}6EOR1%Z;iq1jJ>Q)}It>%U*Vsk8VW=C`R1Ae1Jk zrIscJIV~0BY`2{$N`OqbpA2|n4Js+lN~MfZIDX zCR@);x+O%d1c7oe#R}?Z;mc>6FDyU@b!5!}Z7!w?%V${@Ow6u=S@?u#tzNK#MuGDh zsA&*1G0Kj_mu$Nj*sNkk`Mp%!2FZT};X|6t=M2i)6^MH~gETE1>**?6%=@WV+d-3I zY${)Y@o^N3m}Cj0%^9^iVloKh%d_wJk>#?J<;t)mYlc(lbkYTRUIxpib~m(C`-;d& z8+lKdlQR)tgu5t>lnVw(#;uebO!HAJ&jdy`GGtao(Rvx zLx>eMJ>v8wGSGs~R0zDCL>6J8<6|HVwCy%fyw(cqW>89q$)p{WD_dw$ZtekQ5e7s} zD|?%NNHuKeOKAX$CyMd2K2Soy`OszZ?yN|VN}gU<%pz1~M`+#I8bC_vidSb}GD2$; z#wjQg12kTp#-)TaX<@M%Si)3nHJ~J1Vu%9e@nwGabIz*{VO^Fo6hdQw@AqRGLFLA+ zVl{V(CCx}$F(ZumDA5>-O$bSOYZg=u0LZ!myaOt7N_T-r200pkMXazRivJ1x;H!9gUGsTc+W zqUk#hhY}*{0|=ij#*VE7-kn!lmLu@&k!rEVXee7(re-b(MNf!GruWonTW+d7uGrb3 z)tQX~3l7cZ%lRb`*O;H$dPt^^_!Ju?2WMw$)I7vPS{a%%iRQtjx(ry~QWDrH0_bX5 zAauLj-QqfWA_pX8$%@$B{{UNmU&{<7;UA~-KiBjyzH^MIv$Ck6w&tkpJ*FkL<+&TDRGr^x{`F*2rj^I2tn7(2hb3L zb!h_iDZwUxn4_qf9RXl8NUWZsSAB`mByOP(#2Iz8o2s}cWMu*Js|&;aREZH$1u6{z zY}f&d5G)csZ0~2QGaCho!S-8+B1nl4Z3$kdVm;9s1DviYZqg_0q-)NaP(e!7QH4cB!oq#D}dx&Hu+6i|07t#5Nx zv8Esz0{1m*@dBg<7-NckqWHY*h4W!_pH7UnPyy}nz~uMpce8hbE;IIv;$BczXe0!a zZvY1Sk>xZ?-+xa|`)`f^00^vciO+LMZSP`5{VijS)|}VV2FlRW-rC@F^Xk5X@$P++ zJgIan&ZrqcalXXYQ(ZT!t2K%r?LWtwVV84zslQeHK?wkk*SKz>fY|iLP~u-|t|;mw z{{X_MB|s5nNpjW#gw-U6Q8W-J)n{XP)d|5`ZePo0bCICTvg+3WI!YN2mi<_I(1&JI zq5dTb8%oh04f+EU<+i6urv#sPV|#SUldD@Hd5jwdUtzf2SDq1gvo8&KUL;p}lp|~D zK-k}?0Fz_BCOl8%ociSC&Sqc=xrQ?9BnSLQQl*ossRPgn)CW}^F#|s**fKA|fld4D z4k!Fl`$pz-`9YMCQBe>QRBrkTg}-1f=y$`EP`ms>IU#8Gtn=g-D&+qF(wp;b)=cHR z;EOqym4>B`WOBardVaO~W9la{IpNWa#93ggn2t4ddJ+wZT|{cOwkKkDHF)`rz&JX| zo64k*t-P=OtNaN~#J3`dF~|`qrh?D4O|Ve>0ZKkrb;2oRc5z<2p%NKCK{X0JhCW^zL>z zlHjqUH{j-DT%MBJKm^}PQct6$wSqS3zC7uZ*n^xNe?_4qby4!yAZFWg5+J}52IXJm zPj7$I2AOXU^wE*oxB3sB-75$3!cgug9)$k@U*(Qw-xsW?4mVZKaoGy-K2FQZx@3u1 ze849haL*3%af*rv(2-pL{zC-|l*cEB9BV0fxdTZ!RdQ0b*;U6H)> zNzz8(?m!hnk9<=c+2dlfK?^i;9U)oOfDG+G9f_gtJACo&<{n ZywZV*@X6nw&e64snBiP!g2wul=TCj)7x^0>mMq>G;@<*9Ro8Snkk)>< zpkU-!*+C_NAb(wOnefst@1NfYc9EvbB|O!U;6`>lvgaM95tMeJ+JhR}^xbYO4ZfHi z@k^fk_%+yKju0Y0?lGD;?-Y=rYd*h==6dgj92Mjsp{&wp*F zBkj7SE}`tKLDysIc`a4RA8D+y0dr#27O0R(KKDoW!#-Q_<1LrY%bHZv1B#)x^g-x0 zy}Ne#<0@er;|3fhtYyHnPu(xuM}X0gR?*@9Cqp(~T+gJ2y(@Nh z?rd#C`ZyQm>8FuhVu|DmY&tE0d-m<`{{Vbs{s8%>1~}cc_0xNN`(tc}k>P0QZnnSF99I7T{y7yjeCrV_;{KI2+>>8kzw3dCYatTgX{6`Bb@4PnwIjn4lZ^76Yfh!}P^mBg8k%OrnB{VN?y< z7Xa8c6tSRo7t;5}^5?#6M~&mV45shev4!%-E%$~W1IHsMBWnST1K5h&+p!rhrznCudD1yE#EvPa?S&}LDyHP^w<~JgTGbV z;S>fvAON)#_5pdvxvaS$P$7!YyWMnAW4Y3yN%(1<+dw#49ElOwwrL|PL;nD#G+P2K zg^(jaElanW$s=W>3jxFUX15@l4rsYM1=M{?Ebd0LL*YO26N#zj49vp(kf^C1tv949 zs~>+yRTpd)^n&5I{3e2F#T>F2qpTo3RJnnGJy zA{EvYh1Fqwas#9ljy{RyM6E39BS|GbnG>E9pZmm&V*)7BJnI5K%V? z;HE?~&iYxH4KArGp^&MHCCjX_r<|aqYLYZytHQA)gakuA4azN>U?h2#A_Hi4G6{;7 zZCRHLs3M9pti_j2QcnB3{njtM#&TCD=4Oo?38GR6*1A0DW71rvP_u**PbGQoiJ3ZO zk~fYqB7NG@;T%6Lm5*Z)oz_H`T!LvI%X^L;M8Dxg+KkU5B+^Y0)WRrp1#DT}Rj)T( zb3YJ=CzWPGL8TEnQu50yJ&eYxarcRxgT_)hSt>FjCJ=F*c=WLp3zxQWsQHM>6_+bC z(P@mXwkonkJbs-)e(5~1n)Tj71wk9$ZJOS&uAYjWibmJ#CrZN!e8u+Ic}xnw9(FQCigU z4k;W96PI%9nT|}rSJIb|%FX3R7*cP{&*!6gfj&!&x{+ow&tB#4*S#L%nqTS)31h z0owhnVt2xovX63kfj{0eY@KrR&pR%g^K?XHK+UKs<|c?(Ae4qgBHkcCj$c0_JaQw` z#26tb`!-_s$!4icn$#1kR>VCL&bT)bD5NQ%LTdzCkgla{9g6Gt!1#gO5C%Ut3Z^cN4u*d2l z{_hnTMcYsKh%8#Nq-`?AmxvW$BeGSbe8UK?Cl^cXPTk4tK+&(85JL5)G^M6{G#QwH z0`yfEN`kb3shIDiGn+c3Ea%k3;Nc)boz3whBRs^1&>6`j=`3495=kYC$~7=8t<*^5 zu?%oI#kl1b7D)msumu2&Tc^Url=M|8*VJid{g9TGQ=+LSe(Y}9zr00|fY?Q53q|B- z7|roft%(QJ&1d9nZSw&7djbY07|JZovjdZq%$6X33eo=nPwV7FtT09WmA#zk!1egH4tods*C9 z!(zS>O0NyV`E(1h@4xzw&u`FUq3e!`74Tf!CXlU86g+3QQYf8>B$B77+>cN?M_wjC z9w9O&q-g=3H5Z~yO2%~2>RLDE=n^WkGc+=wlG^36h2A2+3h6bm05J8V1Jzqe-+G`2 z#&FYv^Bct4N%Xd$M)rdL01T2Ti2x_7-%u)OhH0fD=Ae!*lg#EkxKcUhNPQqj8_dPm z(9TvhccfMU7Kn{mh@&eEG|lpa(J3zk$fK(~GNOa*OQMdQy|JJN6127(t7QH(XF>mr$AU0toIh?$?%-Aa5cg@cvNgtT3qg*h`grG(Z z-pFjw2O3*D_E^Zm5`7ZoXeOwC8b$L!lGUWlGwO^T0iH9Z*;{Z6s8=Hy$|;K{8-|e` zJmy#PA6Av&I%dX+Qej|6AVh8 zhEm^$G1SIL%f`+Z%u}mp=0P_F^20Bi_h1=XwFlCwK^=~^8+7U08IjgVb2AszC9VGe z5lX~Md@6j#0hPWXL4zpNI<6h$`Hi#*0a8Z6g#dLUY6_;TE~_N506lto;NrSE%%@mA>(yy#& z>%CuFpgl`VSE+|jQnxZ@BRN5!T#)(+*x98$iTZv=d;*4=YB{XoaAdvw2qWr=St}=I zrW}avZxBMv{8NLlog)C2X%SR(B(s2^UrQuzez%SN%REmr9$P8GGwB2o81#MNT(fCK z_y{T#NZmE>bauL|DaW|ceCjuF%2vI{sZZ(w#>C_pq0i-Iej}J;nT?fl@#`u=l^R)9 zN|_{gBpcPyR4tNNKG($OGC4oAblelQygH@$6wJ9}z2jm!c%$r>E#@M4@-Rv#$$H;J zj2fd;ZUce3v0$NhHgt`!Rm)t*5R~O2%8283*CVJ=<>>%#q*q`N9s2BO;<4l?VwWuh zIay46M7tvgc2I=5A&A_0VJl@)$A99yTlqwq7{2E5*xlsJ!Z!tvsIVfny&Anop&;## zC8!0YgZo_dTPzTz`K$@n;gO4=9aX7`zKlYt0=FXZNOKp7Gv!z;sLBt*+tDANO}7`+ zV@2fnuigrXbeIwU01?O`g#ohP#&2r_NC5B9VpELp9KLZ+nvt|YiksZIU9_H_klR;t zfm76o&7LroeTL)nlFYm#?F5&(7$?eoprQUOSUf}Qy~CP7Oluz0vBm-m_aSRl4&5(v zhVuQDazS4}ZA8*)VnF#;!+z)aV(qiiyLXZ3mM>yu$(}X9&M6BH^e+cIYsZ8Fk|sZv zA@~P?vE{;~ZE_maNZ1qBfb2g~JK%zE4u+ru`arg77x@l?&r8402Q<`D<<$n>T@*{A zHYK_9O)`EijX&8otVZ#EGd^WNcUaK%3-MRA{{XfnlaRh9X(@Uj+jHEKdk>X&!hAQ8 z09F#n%_9?_FQrg_4#KRRiPTT9t?|ZF@VHK=O6OaZzb>V!#_VXnUDF9NaRmS5mWe8WeBw+R*4iI+X`j=&zWWcgk%G_ z3~2sh&3|1wV)5V{g_DhmNE6I*Ng+l4WC4GlO|VXQ5dPu)u#~>lu>Sy(-;eN(`gV*w zuZessB(frdy&m6RmL~E%sOvBZ42%y#BVsS>Rga}yBkZ@Q;K>Ca;_}e{0PfOoVUYVd zOR>`ef6T>?{>5SYG|fs(a=0c#BX*UMP^AkI9;PF7N+ z^&wPO=EDYu?9rOibfPAqxMm;8Z~5ctzE|w(M(r{${IK8t;_Lje#&^Ts7Y6x^@}&*+ z<}&b)(AoX)o4BP@;4lc92O@VI`ljRH+!`qalGMG&m}sy+5%*X(!>_VZ#G&St`JKYu zeJk7Rhdg`3(ZlA7X!SA~DTNY6j`GbpT zoB#mx!k~?ZNpR)+s8-?5=!}S2N;c!z0cODsK(|`}79HykV&zBsu_~pNbr~29B7jG0 z+oF?X-u~7IQpmFTo)?xQNSiGz1yzk15HMC{18qTyf(_~vpo471&xTyt9%F|-SR}rw z513lle7+URZ%6-nlB+Bu! z%!r383G)TWXLjX+Vte| z%*e6SjjPVht9f%;oUuAxs2)P%aKt>uMpA$TTOgVxK(YcMYXVh>&6;&%ACsnlZ~p+z z^2;IsKJvt>M+rZ3jv!UsY6%kNo#aM$Sb%~hi~sj%i6cH7f^D&sE z*2^)R8FMX{dXUEB$@4Ut{{ZxO#!4d@jwQ~p(~4({c+mb?iz14EXw>YL9BQmes9Aiu zXO&8W9C8Sw4#@6j^2v}Jg!3fM%*t9*BBIGU1&J6E78tVgFp^UYV@N-Vm&xUF%OsJ% zl#!#OnC01J1Smq9fNc>nWjw|el-MB#hRzLsYuhiU1$PD%}9m zNKmS(ifzmsvaq^BSTP_oEM-Xr*Gw=bkPy}93^xj9G?#ZZntay0IOhrwm}~; zhf-xbex!9juf7Iy9K_;|fnX^XO%gf|-71evdz<5OfLLiz0Cn5qQK%8Q=shKl)oP=8 z1#;`_DLyR-+kHC+4OUQ+NNVbIt(_x%Ca95E3*vc#Pr??BP&-u(D`Th%7LNL;HJiLl z=WdsBO*!fa3dS(0-*jl@)O(Tu8xmmI4OdaQ7t|BE0QCb?6Q=YMOKH?^5gVfGAXscv zHg#_iIjo-l0Inx9W4zdAKmG3K-Wo8n{2Due? zK1?jiTEJ!!N@9o0MwiSfO^qSp=OkqKYCA>-vAo%-J#XkcA`k!pn39N_`XjVy97+mj z($oPRBe%t)_AmL=KpNr^TlJuCz3DZS}dBjGv%>TV>Nh}P7IcOldgn^CiEMFO>H5`fAY*+M!;W%$&Tye)lS%q&c2D;tZX;Oye%OGkhqy?fFg$OliIG37bwP(^zt!b$hcN8kv>L3xi zuA!*apkO1r%`~RAy7CN97AnfbD??f>b1FwUCcA@>X4x1qz_* z{{ZRgVm6E7VsxD{sdgqtm?$K)l1GY=1(jhy^VQ#&wpSp6y_ z^HnU)C(URahIt=eh{l^$APGGwrYC67fI#wrg%-4S9xwpe+=3$A=jR<;dz*9%7aSZ*jS= z)MGivNf0*|?*#VO!+N~Db|sGYq;n__XH zd2fs6V^~)$B4*6zTZUj)NZswpCX@{}eNDHf{W#1182GCsISKMQ5U-mt40bE6iEokI zn!cYrJ@BhHnrHt2!LW_@)JdfQd!3K+IPH;2(^{f$#8>)Rx?hj{g8U!QTw#EYL_IOt6NRPF_G%YbRk~A4qFv zW*}Izxa)(dq>?9Tc(L^JR!j|hF5R>F*TAcPA;j2|BSPL`r6g%!icd?_2EvO~@9#qn zrQ%AB60;KQPJlHKEE8shY&)74KyEPNtJw2!Z=RZfTETJmGW-8OJ%Nf$o>)h6& zC=IQM7$lw}B#Co<-6H|xU0#_;9RW1IGN6|2?_WKRWv|VB*@2E;-o;D+AmYj60mf>LZSHVH&G-kphAm zjZzkBwbV%l+hK*i`5?=r$vi6~QO!iF<^#C%6e%K2L2W@S1qPBHoF(;Bos!D0M#KaLdenv`|K$H05ghbm^t4i9D-$@ zM}P+NqKva5u^U>^(pUSUjeb_J@W(ee6GNSmB9JS_f=Rt7z!e^u0O{7d`}M~JWDb8n zvJ{bnl5OS{C(J)?jwaWpY^-1X%PnMnb3CK=-xaSL`(SYf{@00SX!b`;xc>n7D2MmP zLw0l3nEhCh`dPwh!?F8Gyb___^q&B{>D+IJv)re@{mvy{oWb^h5?`5^ns1b|%Qx)o z*c)FJlS?#l>^+bRv-7M~Mk*-b^+XqHhDJx090w8@uFYh zLO!Gq<#-;O#Hpo`Ly@MyBXhU6Z{>*i_+vL>Z{>*yxf)HHtM$dSe$BO`TUtp9vZLDi ziGQT|0BOa5YkoEe(}(i|AsR{Br(67wTuSjCV^XD!tKR3PubJ(CJU#FZP&4Sy5tSCh zYzPLnBp#yZoNRo#$T<$#R3r|*THo@#d1tizUK&%_K0A^xb*|tPGe|#B$KV@TVTk$4 zAzr&a+J+v-#3Yc?M%(<4^l|hfgK_R0pZ@^FF8=`fN&a=VB{Lo_<Q^v-yf zHe~6B1*7vUPv=8?MsZG9&9L27N$t|Vo+lR*l2?PG$JC_VOa@+a(NO$X!tA7)8d%UW zHlS;8PjGtuYj2h(B;rqz%I1Vn>xyLsSq14jO0Z?CUyM+Rx1N z#1m%a%Bq>P>0%d1AdzGFA8-3()A)IkNQ|?B8;t(|3ZJq4RN@Fl-fv;d*lav}7tih> z5wb=;=IN{G5Si#eI>nhs8Ci1 zQvzK`VJ49qGp&Iw%Yos_ei<8q7+^QO^r{Uc@6}hLrLocu)fXMfRE*37lo;+`PNG;* z1LE&iw#1S^4NFY-T-YlJW75!0=r4_N#+K1FX=crbcqWDYCliY(u{< zrlkc>`C~j)}9Rd5Xu=eyXVrA28iLfgdQl)RGTQn)HAb-(#UD9-EW7W*`N!9|HWt zk#qyfK#HX5X53xvq?S}hQfZmX$*q7w&!r53c;bbn+?}iB(iQc}ZPNYCzyHH$ZT{O0BbA3eK?^bLHcWQ+O5s03JsrLa6dJFlP@K z^BAmgYDbM?)g;06{ApGxrOg#ajaXzdQ3BCKw`mKiNf?YXyRFFT?(#pNiDPJ2=3Xed zGG0kWZcQyBK;c?8JCgDVOKE{M0!)l1BT;o`(U;8U{{RyFNt$g!Ni|8Ce>z&q8Mbbnl`+HJLzEMQIWhkB1(gsica{{{UNU{$H*L^4uWkSqh)UEfi$z8$!l&5)G)T ztcei4nWti`S_e@J!iT@h=eGX<>v$(j;eE||RZ##G6%?}-A69pjj`t(WA-9obu>_;DETQWYi_JU>bfOwe|IQ7BpvrB=WSKLzog$# zLmL(W9*nht-)`MSUs3J9#nPx>YztB)lx+DjeP)G=3M<>pdL4ABJDS+K1Mv%v;u%UU z^tJXXNMF*o`PK9z1YBn})~jPmCWQb{9|(|<{pKr7ejorL6_7)ICaJH)PH~V!~PS?94fVsOr@3!o8lrg=+* zoolw+pL(Er{{TaO%N7sa=zpNHeSiaUPe2CbnjLVZXL-(!Z(NB4~MlERlumy1fV z0B`0qT2AQ|hRx54Rx#NB!q*Bva)Hb9pSo(n>6cPs1PV{~D zAt%XoV-zUAIhsdOg-v;3w2x*wI+`SP0D#(cS>BEza|%{4P@^L*pls6aNnJtJuSk&D ze^Jym3qy|M&%CRyFpm0ld2v0=R2cOYN7KAokh%?BR5Jo&EUYn!qb4wch*Hqe2Ekx< z^sUX9tU+W_42n|a6J zm2Eo^5dhYp6TE0&*bUT`{`G?yKN6BnPkNd#QCV9`sL&JA8&ZcJYZY1BPsxm|*ot33&WL(338w2qJqy)+2VAVpI1c|e|X-bVe&qR`_ zTGj-!$mLT+Sm!h4n3YhgmSXIP+Jh^oM$;^jG)FJ#SQA>x&Hc8B+DUFhhQU-8ozt#; z<@S@ynq5At@?wmpifa6Aoalx%7KTFaDGf8UlE`GtUY-YdUYVSmm17*i*auq|N7H|m z4e>y8hGs`xt;QAQo;I3IOJ*SH5=Kc1Da?NeA0v6W>+iFKB@%W1?5Ft*WBsbA zd^Gor*n$tuU2s@gB;#1Pu^%O!xqBnzW@XDx=hE!fe~aaN3+we5Bjm?$(wL;tPDR~p zK)SE!V2_FN!poe^t1bG)dyTq`1aP@z%S$q>W=O&Hdyk*bVZJJhnNr6zVa2T``xNNc z1LG$x1;ATzx+CzDjOJEM&`C6a=UewXU+1^JE)H4oM?08Ti_BGAxYZFmx#|UW>DT@7 zo;)<;rKil5v{r9~{{UXtBIU`6l4UKhKkK*be>_&#QL;x$WN5f+Y<>WzAeqe(1|I2i zu0AV8@m!m@%f4c5U@&>5ij@1RUj4uudbEQ;bq+W;H>48?U|mg|b!eqgL=cBa40@`7 z%tt_3jS_1a?}BkczIu7jPg;OlBgg`9w=$vAw_}e!}nc zt?`?iguRDIHx|D^oR6BfhOv@1Y2XX{n6hM9#xy7x4kU zA8^iNY>sMZ6~Tirz)sz04T)Olz#7`e*08Vmevm;O?feMm@Z_1)50~YZfP!8Z7-OK% zl5oyhfstdKz|>^(80u{UbUu_O^Z``XUSv6_-yrB9NFr9tu+yqkvJo^<0IG$tR@#7| zZu91MpG-04GLR6^JDCA*cB=|ak*bE?_lS-$&bf)aseH3&*>yqt@PV;qB@L5=X_MkL7Edr3nc1d zI;?=K;1(T*nl>v@17WbmdBwayOx9hSHBFgP6;8kvuDz+Q$s=?0+ZF!+7UQu^0U4_N% zy&Iylcwl9zGMqu-%Qy`TQEKwh7y=bQodEf$P!!nhc*z5wTgm&F7a47OkHihx7uJtb zJ@>?T?wbh9DcWdwcI){^5ZAY!Fc>$!SPxD0L+j9 z;0ab5aD19?vHE{5pX-L%o->pi4L_(KruY}i_;DkyqQ`BitljOgC$D<0p7@i=cv!#0 z*YY3jig|sy7qoFSeAbSa{+G`nyX0~CA|6+UJ3DM`mv703w%Z&?@gfPY<%axIhKNLE zFVjczAOrn;u;hSu_x`UQ(Lpn6DcbI>2jUm!o-IoHI>{n8?!1qIvM2j6^MqM!-03oc z90O*%?Tu5C@h^tLL2|S|m$v5^A10({02^vTKb{+LEZb@!uEDL(w%Gjl#<&$NWlP&o z+mJ{Zwimi^-YGmZ%`m#bz+HU|A$?F}-MUbv zj?dR|N4*X4iVkwHw0uhyPw5Xd<3Z9X-)Suj+mMGaciKFe0a+$naH-gf}+eaHg8yjchggAt*ey6yU zbHu!B;w79mVUD4s)m3{A_4;6cGtHUI%PealckB-LT`v)NhAc@tS;W7GIgc+p0S2wK z-&(%^08v<#RX6lp19NS?6Erip)HC{po#Ec*-0$hSp5;FjUKs*r6vXOim)LFd=zpK- zwfVt#+U7vgBb9nJRGmilqUo3lE^PJ+-EfzP9v8)?}e$jCnlzmP8{hJ4&_$8NK9piLU!5EYAT^iFwOku!CXxylAv8sxfGQn(yC9E zK|Y?UpaK~7dsD;TwEJe2qA47G0f8G^N6`B9O3n)HpfOO?6Qg;GGve0_M3q7UtEEo8 z^bKr7TB{XuIx1*TNTcZmL%M-CSlOX5fNl#;#1MXpy&RxffR`{3vDM@v$s~-Xfo6;$ z%fk>93%{J8EmpTI9?g+~g4U@*=w)&~Fr5T3C{Y@zAS1G%eWNyI3{;yM_D+JhG^=BDvC~rIr5xuyVin*WKZ0a)dLv( zeq2&9R#GzR?p9qYvii=b>+zr0<){w~%|R1+UV26rw?XImj;7t3MR6mB~8W%hq)lZ8c;Or4`Qdu3k7|jpdU;1r7cRmgbHRA}i_) z?S5?1N_ELJuT2a{6?DayjjBAnD5}!4D*!|iwkV!gjs{sZQVl&?q1;y68W} zw!-m$BxiUah)6Q&A(CJtXw05mV9DgY4>gq)qK;!}7nqrH&o`Q_j(khuPa55q<(xj-|_Y%~fKEFd@Nc`w_+yV2)TqBBXMgIUA8ttSc zia$#Le2>cmUwsU_b@uQ2_paT~w_1C`GIr`2#9;1A5wHTy??BKr9IpLnZ;6)Sa2u6l z@NOZs6YcZ&{l9D(aBQ2&Vld2ofc_!U?Co?#nj3?Q6TzRd<7Py1NST=8BXgK&VD2=4 ziyW@ZX~9*QJiA>(bbM*S7dHB(jpKNwtvE%P_s0 zlB}aPsC7NViR)KYSAe=SY2rRq{ajS2XevguY)XZtbunpVpyO2RuW%L^zuaG(+8ogoye zQkPX!3}X*VQIHH#NlXT^Y)xyOx|^KPvTq#l{9mD^ac0kvC_=j*F(x6Alyoidkwg-2 z5R>v_jQ|kL!~mj!qAtk2tGL|Jy{eAU<2-<|vqBUVrIE-`0;$_(H>ulDlxi$#R?ej? z!@Og|AXWV;&UBEcVqzHvk1i?doQr z#8oI_SqG=fMY|rR8iSs({Z|~`i$Bc{qxpXS0M_^^;G8ai-1a8HtM=-9_ZNnda>R;A zaiH!9Cqq~yH3>YjM%EYttSNyatoxNXAp{Uq1?YA9$nRh{-=_L*x3Q!Q70*Iz6z>S( z+EfaoTlWP1d;WL(ZH)!?YVgGvobFBG-MxE&xst`?=Yf1||=n-W^+*anm;U{jg+>~R+?KqE|v+tC?TU^Nv7%LU0b&>x^MZ_=kT1Yw%asGZRb}(kmFr9uwjcdA17k{Ppe{BjbI#T@k<=h{X$ z%l+<8{4fn(MqOGta`|vTN#8T(vQLUy_)hrpWEy}o)(;LNL zYHKjf7PDa_P$Z2lj7%OQ=lKD#owIp~rk^V$M0pd(BS=9S7t2K`fR!ME7zG-NfqnHW zKk#lg&O$%|)+UcF0aJR6M>4^69a`QvN=Tz^DXsbvaP*P1w&U}Xs;8OSh;qMtL*}&f zIf+q&fc0RtM4$@*T7+t1!G@3kEUc#yJn78Ha{5G?ocU+X3J;Z4Yu6%0dcaAa89oN%90vU$=J`y$yWHFGIV&$<$agPzDb~fx&vI0iJ&XRhYVr-ql z`c$Cl*dVynp;b6((x5rlI9cD}162yxP&-f^Jv0UO->JaPUUEJA^uKSHf9j7+KjIIp z-BzR9r?$iSb^?YM%4NdrY*E~=P`&+L^|1FeEOo`mz_QOfB$R!dm(PuI%FM{IyoNYq zI;4GN6$^zc{sCobuknah|MHuH&6}PW!WhyJ!=LVW;wA! z1WbgN#d_ePD#=8#)C^Iy|pmQa9L`1A<$?sm2S6F>{B za^wsy^U5{Nd1@hP)n$=pia1J3DU8U@fHsv?R?r5ZUOm0J`7)u0y;dfriiLM{%In)V2b-b0hx%jm;q)2>$>)T%4)ET=8ts3U`Vl zxUGvf^f1$dcxq1>&8Z=3C|vyY7aMf;-x)ZaE8O-qq~11L&m7g;j9@g9F!z7`EaJSS zA>tX2Bs2-7bN+->NBv=%Rn84 z>gYXy1K$bM@VB_$D_8-AZ$ZT3wW>+ECnMEn&M%q(fNY+?0!jG|x5GS~&QGEV*z^QO zz<#E&s5o~ZGG--AyCbw-mn3wtbz^aVJQ?th4kcyPB#5TS2#5hBZbqWl`T_L7Sxgz- znWT7(g<~;m+p3;cp964KQ^taVN@PCHQ#dAE(y6_YH0V2$ zSew4XNUpYeV?gD(iZ~dg@JOH^k3k@wgWv~mYsA#N!IzK$5dn|)ffZih_x&*I9ZJtX z93&*8uXaOjKiiS=S^FdQUxk0R%uLs?rqVz51^w>uZIk;gWVDaNbGKHJ?EQUsv^c{r z;Wnk9>wAvfe{2`8i5WH9%}u^z7C^P$=nFkLk~dk`1ztW%oX49yv)(Qw{94N?Y`ZxX~Q}1h2_8yWQYARMx*}# zdB58V{)B&gqth$h54112{XHYd<*Lm6)(zl39A_r}&ncO6&;tvl{XEcv^Sb%gEZzb3 zK;tu2Wkg{2ne-pX1z>;m53V(&pJ-VI(B)M}_?*}Nltmx=u@{{2+lVls)*mb39nvypkVwEa&xN zPBqQ;x~d4<3@#u6VYTh&lDyh)`f|$Zht$=%rOrBywGnf^JE05sx%NlMeqxpA-3AWa1J4ffU8{{V%k_pX>7e31jDH%y)+xduKd z0?z~NCq>kndJ7ws0996{?m)mNJ^h#C%*W^Osg>4?2uTO>M*V>OfX2F-c;y30%JV6s zd}N0VES|A@VRep%CHT)O&6Z}4c-kiiqYA&6JqY@B!yySH>4!9+hjdEm0YL|Q>GJ-) z`r#xDr5pjXDeK%3*W2q@Yng=h!>$Sdb=FBs++k_QblEom4tND@{sH1tPnTCl^{Ob5 z4G{Ya)V(!-hxY~-D;8qj2aYBiG;boR8|=EsVSO}!%i6J#Q#~vVXvD8!^6Wj0 zZC@-I@|TJ$V^PxG!M!Jbok1V;k=$6vk-nrCx+;e10Gl7~3-k>o363!@43|kGFuyEz zGh(jJFX2RMBFm~ca}~2DkWGW|x-6THq#a}dbT&BW#k_%%B_%|U9>(;9WIxBQ0ZB2+JlPzo0A(VFp|>aBLwqBB^wJ8UY<@uZL8|Kfx|5sftZetHz+PoZehACW9*UspplomVFXP ziAt`^7NR3cE?|RT`r1im&8*L=sN_E?1`-OLmtT^q%+bbJX!B%{B0TOa?UYJ-j3at= zDX%E-tj={z1)FPbap)=X3 z3&_f>--I9xSyT@%$|RLr(>(daGK%pgN#ilVs|z|hvN4!Qlfus#A+r+dB2a~h%N=Z1 zF%(%?zF^>SW>bX%VFpPf8HhsHm6ew*CQmGmxBaYRiRfi*lVa4Nh;@%jxwZ=%_&ebg zyh3pdNh7g!O)8c&vVzX;(`C{)(Ox#aZ6t<7SmJwTPoo|h{fqJ$T5%LFI;duy;}o?_ z8B~@elT8S+S4zsywXz>$9}WKP7H~+KWQQ_xLraN&4h3B&P(iaq=*k$6Dl)6-GD|GC z-!pSi6vTixuD*p+Paw0oC!-&Vl0!pLY8u+Y=^VzK40)AjPY#d`vT~eca_b!%wz7)S z1=u2;MnE)$2mxn~Iy#1u2KZNqOq2r}FQC^)ScwWG^ll}hTxilYJ$g!zw8tCqH5OG( zL@F_jYL4j}IT5in_@Q+sqC+YXsI?#p;a)JbjAfAQV`&K&T4VFd9XhN4Bch<`WY)!r zV5K-*edKq?e61D4IO-t;3)QDc0BQ2oy+=~+74;Gi!iv%GrAhz+S|g4-I~!8oniwYIo+J^N z1Sxfz2U!=UstF4nsHkG)RDeSmNU&6)wASIaZW3jHtG6l4ORiQsJBb`A+=1mPmI@0q zF1|nnR0o1WP8VJ_eIhC4yH>S!GoLN%UCKPe5fYr~pf6zG&u&9v6{4QYZ?E z*_CI@EFkD0wV+lcP#$mv?@M8qBKVuCWb+9kL2VncDIio(?GqbKaIy1IPnH>jsSr&P zBe=)hWdyd%MCG__d5C%_VLoG66=8;XlTL;ON0}LT$zifJ*81=X{{Z6tL{~Zo_Kql$ zKxB;h38dLN;!Qm^*K|b;G2wjYrM9MG%CZf;Pv$J)#efP;LM7a&QW)5gOIbWKoa2^B z*@mriWziHHwvsrZSG~6K;*wHpq5VZ+e4+;sFtN9ayci17s}qJ&>!U`cewuVgL4%M9 zwJWS%-d>2lSm4C<=x^&(YkD?ETRlm}p7GPeKwvJb>l&VrSamHdq%a4slEMA;M#Di4 zG}3%Ogz4CI9YqQU+QU)$&>q+cC8os|_ZJDSW&#uqT>ux~r?%Jm;oP1oKc0v~{xmC= zEw4~z3<4b|QUV%BN(a@ittizM#Pcjk8&`X~@3+r=sO@8aY%2!}4W*b_L0|#UsV)Ty zwXi)TQ979J#>rqtS7MdXxR9nepEh}F0$5~Q0-TnjShBf;%2z@f!pvJSsrC7ai-^w! z`I&g6ndXup(dA)@T33{*5=_TLF}~q!M=QzP0i^8WfODT|jK)_nG*e{qd5{YQl6X&; z@VEA5$@uJgU5eQ%Ll$7zW8xRu{#?{>BzbJS3kJq0L&T1tFpqI+0%~JG3oPE1)J`}d za5xI>wG4A^=K=Nf2*1QWe}y0J@v)LPiqpdw*}x`CmPPf1Z78g$h1Crxp^B3;%2}N2 z86bw`k6THXI#pzh)5!3&geBQ5a_yr~c4+-yhCq0iiutBiK+75%_z^={B-v`}3lb?* z3qGCBBeMMSm<(7*)>~GP&7^K<4NS~Jf>djFYO7{8#=(#li}O_SRW;_^lk?$@Vkw&< z@yaD~1aJoxIDhP<9<@nP><=)NRUv~EWN^EC!!Asdhboz3g-m&GCnXe92?7QY$0X9S z#R|)rot12&>4Fkt)V^a3XFm)6+MHJ=1ZyNx#+r#qVbw~^p@?-TcV^Pe#F8IP(EwUp zSM8^n%%ijA^5#}#ra-_gY9&Z=TFW1np{shMh2}C|+J9us0vmT+KI)b6pNaX6qV+NL zGccwAtW2X#4Fr;P0r6=KzM@SeWUiD5yoQ(^2!jMxFBNfaN5*GvEn2(tzrA1dtk)8dz{e7^mm}3_t40$IbWYU{76W-(r-FF5=uN#-B)s<8FJ%64V zWHRM=9zC9=WUp6yp zA(7&bLX+`nXVph*p-1R`cq8R5B@|psh%?BcPm)IVO@}9Mm->A1nE8X8Va#V5S@Q&W zYxq(|%(VXYzN58l4k9^s#VF#;$n!s)=2E1RSsN4m>!5FI!Cm@?vh4sHbIp(4D;T1N zOGsSyhno>*+*t3uv+`PJl==P$^O@Z`n6n`o6q^JeQ7Rjy*VBA?lks0MDRCMABbCa$ zjumN!-9RMnZq)p-ne*N+eb#y0zMnMz00kY#&WPxC9WXnV@nlE485p_Lv`(MQ3K#Vl z5u}z-b-eQ0!_74p5~qhqh8FJY>btMgpCzlfmoduc@v|{UTj@pC!|2VY{{XXyDfqTU z9-c>$&tTuDqCLmwjP1s`rnH#Ie=?1~sM+}uLw@HSaSmaRQX@$t-kS?_0U!_n*mnbL zK=9a3U!Odu8|?*2ZK5HA{h$w`(^(FA%akawA_H_}Y9r^T)WZ8(<2~~paeV12Lt2eh zSLNy$t9(G?6`PS^^AbHH&`I~}SHIT{{U<*F5d)sEU-T6@;Ln7MO*&>_IMznhJ)u;JzCoizKl`uefDSLssW0}~ zyvAd>63rw305*J2vZZI`{-rJcE5<4a48``a1LzOcZj9Fp;29(M`MkJc+FZ&O{{YYq zuh3!-HTII_$O#bvGk^M=q_1b>@*=0^8oqeK4a|@z{5V0O+<&Pb%M2UD7Qaq}{{Yit z{PFkd>Dlgq`Q#B#2IF-<<`>(<+v@NWqqwf@!7z*pBX~jAyc_F`JeLN_Qhx7cZm%o z4+x#ykz`n^8_}-E_r##B?PU#U$C6&N*{Nx1olz`}$1Otl@&QY7huThTn+T=+XjpEo z>f7jcKbHR3zPT5hve}=;;rWld%y(#uej6WR4@2^Cb^K!(4?mjL7u;=!92=Mm`L4(NxX>OS^UQ5h zWHUMoZee3;Pt#%gX>2_ee34>dan&gLHc6O`k5A3kbK_jUBoY{I#074RQ@LYkz`P1;qEgLkzzI@SRH9! zWj??@pZ@4^0KPy$zk3wX%-gW4x3Td$&kDcr%;j?sm?dDct9nT^Nd2(GoIE6Wy+C=1 z{pIWXb^Ngnk?`7`qnlha1+X_qQ}0HscD>Zsae@vP;vRD#bk3tO2TgiI8uws9v-JHj z;@n?_R7X`L)Xvp&a>Ku(_r4FrTIv|TRLeHj5)HAA_fIt2k~5RTdR3%dsdUc0{{X(B zk*|9R1d{uajS0wn?G(VKgRU_}M9VwI%r`!t#DLMSR7l_vxhkQ-czR_sRVH2Hf@A&B z5;-SoW{&_nJK(REayf)$iJlz7{{Rk{#BURBNmO-zFfD2#-^|{^CZa2;Dw^Y^ z*K4_hg!d7R&yMc1*~!-?-oLu7`J*7GH0DPtB4N9QT}FUaQ&LKAVYFHr$)d)LXE8)G z%!lzFB+^8VQYfQ{E!5BS|sIU}- z-87Rf$WV8#^Bl~mqEI7d4QlHf>QU5zn3R%;t3%Pb8Y7d_sh3L`UUegKrnr9xa2eJa zGVm;xv56g3#;~!+8ahuTts)^s%nZAaEJ~1OGGB-uZ;9~MOseIxFD_s4BSDpj%NLm} zg+YOe%{KA@o%c4hNR z$s@bT77{{YfhTC|FyR1(KrbSO3<+5wNmQPNPMFp{+FVfqkdsVvS2WwP zRu%Zgjf1c&WY*f7%Z@;6b9*MBF`YE!(vo_swzvqwG|F=kl16qv1IAEoRibStu`Ir4 zmu8Oap=g7M(Zvc_+u_g^YzrWb2d3UxG%Qlnq|0tUNv8WVFr+3cq<{ zRyA15xfj=a>UW@obW@ADaz#;6=mDA}bUwB*1QZ9VGGi#<+XPYw1mmFC*$Kl6ujSq_ zk>(6JfKn%rE>kkU5;UNqL8j~gH7N{)ILwVECPN}R@qfbo5&h)@EKL`gG;y5;Bv7R@ z5WIaQh3%G90gOIWY7A14#Ity6*|;I|kCT_S0#}1rB!cTSYC#HF%c|K#WOi8#sD)e3 z8{$Nn0Qn5^Hf3WVrg2umjz?#KoS;7ofE{$By;}&i_DjBUl3FWbg)&W<(8*sMC>XK}q z-ldiFq?ur4B>~eIDBV&QvNurJf=w4v<_NWpH7g-_4h+Uu?Ceq9BBJ@D&>Wi1Jb>9V)u#Zwg|~t*)UW>nSeh zb+TjA(@CvIQPi)n7D2{pAlas-Eq2eIgqI2d?l2NzvB{a1&dZ7 z8?)#&wT(4LIAwIm>qBDSFs8+yZ;e%f+-f9sygv-&D7k^lBmKdb;`!~4xM=l%(mz|X z{$HjF{8;5_eD5+fCN@w#CbTfSQnn2cZAic32DFO-`>Um1-%=0qCl>7Q3##{bO_zC0 zq4L&>mmum=dJizG1HF<8hAUkux>I6Ph4F`h_9IVp>a<0HR!Gxeirf#8BM=Rm7t?=n z^2Gi}mlcpZunJH1dyPO@a`IY;lzK&g?%}rb{W1~}ag;W?#^HX?<_4`xu)RaQ4T}AC z{YL%q7sOvfGc7VY?l&G=F}hL++)yaqq$t`LF$Sghj$CUt$KoN08}+6oNAV5p4X zCvB*cVOc;FbrmLR@~Mf5BJizPw3JVnSXU`CPYb=mZdnxl>} zy#m2f7|;?1$_KU!2MdNA%zjhr8`+QKpa3*)xJ4ujB#W|l!EYJxz~n0% zWu!>ee*%WHbzvx*Bo^CYTV5h~@7a2JV`WJY*c*kiPs)uRo%H@jfvkDIgl6(fQllY_ zX%3wv^c#1+*S`HR+Nx&NSQ19#2G_sl5NN9(LL7TGl3daYfOV{Ik3MSZ=KgV!b5Of5 zU&Ce9)6&#ydIO-qe=zfkeeyXPU{zyfW>&7cXcD#U@A=}H{{V|-Cq^|DybLOt#s5|^*^Qor13m7=4G-1=>U_z{{UoG-3H%WSL4Y6wA(LxJ%2zp z`e9B1#FJI5Vwd<%_gmTDd|9%VIK`RH8k2=Iiw-kkK4QcfJ zx^?=T3-Q&=6lRo{miwQ4OYsjHB+W0IA_ekPAXVF702_XoW5YSACk5|F8+yIR<&T40 zIV4`-Z=dc^c}#?oL=^1Bs=i;>Y!Pse42+}6z1MrMoqX_B%1}kENGkq~My%6+sQOmj z@NLWRC*NE~KomZGYujuFNeH^@03?6hf;blE;=_<_js-pzF5D0&s+_gA(?P(N=O&-JN5bEGPs8Nh1fgJ1E4Co@i&vAcZPEk z#wAb)P!7iaKz8fDTw6XG_-6>pLjtPt9E!pB7Z~7wxs} z`R$H!yyGN@?uobpuV8oiUMf6}Zm;Q!2g6=qj4aW}+I1soCvY0J`U6#X5#elzkO3$% z`%_g?039(v#bB{&*D4^K7kl$MVOVYmJ#(Q5^*gL>1iP=8uLk zqVHf?>fdN9CdX{!>buPGHa%GWxbufk>fZ>>9y!a6@nmV5-*j0~8kryOIFz=WdIl8J zj|yV4$g7a}mEfGwBrF;?Wgy*y{Qds`u9%5s$2xKH?UtbM2P+>^fh+`;G7p&c{?|N%n0^H=hkzh0m!+%e{RpcB~F}GF(&(JClO9 z;k-hofZm+yj`?G@{PI9~&kR!alV-cim%`piKb!)~0ew%P zC!tr&X5PZG47|%C+1MXEYf=e9NCtsSf`ZB#otTpT&HC2u}Cq}^P=%G;Q3U6yIs)B%fVx8g;PK*OSUrm}tg(PsReqqq90<-A4 zc-mJQNLa~ptSso&l&po(Jco?_Y>;j!P;1fzf$H65n@6MtIvFO^7`K)h85R)W2Q#rM z$?|S-;swhuohSrNB6+iUAnTJcnI3#>6iAw*5y_N_Sww86A0Z%-j}YV2fq7|7Nh_s00mGeXe*u|IgL3pnjmAsK0 zK~ZK9Ll&YgWe*ZZ6n5TZ&6;T9lQ5CAP{f(U4rDqbv6F-q5X|5hhnW^yG3Z~rNP%-X zi5BS&Vi*v>hBR36u|%Y`ed2kc*-@T8hD_XU8Ke@&BFfAFK4VDvmR7B`l%CuTgDEt& zK&^85?M1e&Nj9Nd7B@oALKeZV{-E~8f$@)%BYd7>e^WkN?-{Q&0Lvj!oktq#?@m;> z=~t&v)Je@8E?YDWY(W12nY3GeVJrTuLChZVW;rpXPUnuWHntWQ=6t~9{EZtG^xR;t zlq1)zcGB9wv(OO{>N<3F9lb#Jz&vvQ01&(Moj?B1OIOUW2EJr(SBp!-9#AbZuT^l! zO<1ubCd6&MfzpSTPUUL=klCq*i{um4AEY@2-=kAlFIn`B*Ns=V6kh0Gdt9p*8 z1Vuqck`WitpsN|ec%i!TmjMwf32$_hKj61_(Y5@UY zk=b{rjo4@rmUp&vMm(wG9Qioivsrl~h!d%Ynz@*qfIPXQ%vle5hNw~2L2`MgQVD4e zc;fNqCu!r@NI`O;7VOwA6e5?~V@m#!sfh&R=Qmd+^JFa8t818f>KxR7>e2XsvXQAK z0SG}ht8n&4i31}>+cVNF9{4rl3p~?h$kK&b#BW;B<8^0YOh~u}rd#z0k@b{NF_3TK0uGu3pacz6q&XW<36pE zb^&l17z=ciR>)jdvEzU~pOFNOj>lpwborjESQA*tzT^;oVus(DyX#$Rwh6P`JgiaY zBm7LX;K>}Kh*PZ8GSMbEP*S$4Qktol765Vcs>4vd!vkAyTLK4K+ey2!r{{_z-Q6DC z*X3Y!kQ-1cSyysMH4>uNs51!Aj8)q6=);&|V#Ki%ecX!AGPiY76F=oDQZ0*6CCidu%=HOD5-U2ov9P3!NN!Liet~d^Iq$`Yqy$8=9^+6|U*hTN zEH^*h>58*C#NF)g^c?`ignSAA0Et-*^l(&)-*D8!^B6*W>Zg183}|CuXs}fhJi8@F z0B-jmxApw+Rm<@)ZDiRD7!HaGok$1(E|qFJIvwn8Y-O{go2(Xca3)j|0u@b^*+H%C z@1hNh6TY8^)J2lWDnxKH!n8T}dpd7>wl)}_%-`pRQt-iNOKPq9ezZo%+n^Yk=WaW= zjW+}aW*u8t1#Y&WnnI`>3$twYk1S<7`*-itr_&rDE`mapF*u|;K{Ojo!F8UCEF}B~n{^a$>XW~3j=9%O@ zTJ{5FP&QRxWl^x~F=q0-uc1lmbf_QV-2T=1ZHTqJ)Pd407Ng|sp`B%Dr-~U-11ObQ z)>k%FDOfV20qSr`QDj*FGhQ*y87#_(v#!u)a{5SIwrQ1?QV1kSw39=nhzjxPlrYx2 z6cpO<;QD0}O&N##Lje}?4_hflR}u;gQ~~C0!9ms)td{3~Uziaj8GOX7fm&?bQOL!p zG26?HA`JVGk*=B*ifXB3YltqeyBXXzTmtG}EhUCjnbfLE^0H;xij`UjGvWs`$C;i+ zR7H)vwvB-erCK=18mL~4)RFNJSo(_qF@I@1ejkeB5kAkG-<+VZwT|)`v zvMU>GHm~P`-eBQcN{~a1&fT#Dkm_tY9BisJ1@8^Y0+I^&x{Q01IEJkA+i&C1u zIv?wSHe0sN^HR2P{-l=!@uCKqvrloW(63@W@AtuXB6Fr)r_4&lO1|l;=$-eWtLut) zj(B_ys3RZ+Hb1Y=5m~NTkzxoele39a8?Usar z9eH4Som86I^pHr^RslCeSRDp39IG-Ym@6!D3N2!sdl7qpMT1>OY##8x7;`Y9#pD;z zD^OP;j-rS)ur;?ztU^ZOz#&B|LGK<*E%_MyA;aG%AB-6E&crI7(K8U^p$;CVUki>H z-o$-HU()*Eq7MZFB8*t^*^3X)3O%q-!#p>43c-aMBinze>-ksD4M9aD#?iLvX{zId zUFOIY;=)OnH;ZQ{;fi%;y|&%A{THqtCn&^&Wd*kDq*q?G?fxpo!#$jt`E`I)@8i%n z4a%t5cR$x*j{;(IoU#QbB7*ux!mGGFHtpLHnOJO$dEHzF8J`j4NLx5162{pFF?7|U z!bUtyXEcOCRwK4||)%h2f`;E|j{%pdHCCUsI?5089Rd7}F!-7O%r`qoKX1 zpKxx7{{UPT<#Q5*5=2U?9i$2m!fb_dq>h__YWsD@ev-OqU7|pI`FgF-7xr3)qBB*) zWgFp~$vXy}@8E8?j5&;^X$!(avG|cv9m%k41r4b0L^^}-?Vo&P$j2L*9zAYU8r7J4 z>MWvpvB-)&NCalsZQE2oBY3zMN@gX@3nHXhQ2-wGC|zf2rB@@a{V`#4G*RZ0RAFT# z`dOF-)wMK^JhF7`Z&JA`wIb=fJo#gnzR!}w%}-ZM?#ieejj#Lj3oUWS^4IS(0SE+> zG|JK`PfwTSN9IPUP$b2!{Q$Le7VrJ1XX+(PCSkM%nNWh&EMbJL& z5)9mtd0BTLtVTw#xXeYFnxafo`Rws!XanY_hAAR%#+{a*7fb8LW)CZ!94TmuSM$ zs;nQ2{1tOa7IG&tB#iGFqr6WHB}FjVnmJZ?^(~lRPz(T*uC zlaP4K=1lHU;?b&_471GxqiJ*a=S*m3(6r4iVHw7qG3Wr}_b2nNdP$;-JD1OSqtOHD zEtWMzA*h`*4BZB)jAZ%4wYorKl4V7PNt!RiH_RdJ*;Kq1n}V(H5j;j& z(V@*j7v4cJPZVjqalntPnTkZf){>fF2(f|#v9~l-&_JzlNXkV9Evb{It4S)(LY=i0 zI>EglCAr@m@yjopLb;5tT1WE|MP)=I(g@JGa?2EKK_8C7X*8cCf{~zMoYY)D%t8d} zfCZ7QQRR}!ca9Z_qlYYjgpbdg?=mW@J8F4;?q;^a$S!rtbIbY1jGYdShgPWdfEXWi zG!6CG07dS-28!iY$vhP#1~&N2i1Emx!xefG$o)lWv`Vne)S5?va=cC%<8*=$WVxDW zG!Pdn5r0EXq*zA|g{w}KWnt$tXW>eY31yfOshy-y6^`~Gfux2h0~=)$K9(k=Yo%IN zYS}dmnpTbAekehcLj(-@pK}-ph+NXf?jKr)k<>Ch6=?+uZ5a1~_?MTObcN#5&vf85 zd4!1R)P?k$Pi2u(n;gvER5fZaGJ)rGh@XC{c~3*9!_i2x|r=}jS5 zP_Tt-UHU;Anz!wB;tINyiK@LVx!-fp8lYGZ3EsD(hg_S@(9e}gq-$ZRC2&~j3cw=} z515h39hH=lM)u_rIhug`N^o{bn83%#uZ4}v6Y12-7zQlbePp^LC=5v??6o!Z=~Iw^ z+l7<4*NBrLP(cFifi}!{JDS*ZA3|*2!%$?w-voSTm0e!R)Ct+P-`Lff+p*|&`g_HA zIE*R1iM!a?0N$S4nj7@J?;5r^0JDz-o24=wJy8QS0(TSz>Dbu+0GJU(eDJrFbZR?l zt-bd&Ps?qG(DoQc8*{yYKo!$q017`%@TV8fS^9MPbpHS>OuLfcYUE9+to0jypVrux zkBWo<0tN5>*c>^)Fs4&2=CC{0`eUI*7;voPI|}{3uj{4@n}?FLO?bmUt{wodxk~BfAJYyxyS{F@Qfl=CCFqdj0k52nWSTSy<4@MWOefL@lKfo zGsyY3nlkGoW-OaL$0_9H-#`%gC4UuHMrKI%kf4oLwAC43gN~n`OG6D1f}bGj2j1jJ z&t6weIg6SV&!zQTIkfDILK2X%U(H01;Z!sja-eW4tsvJ~;CG%6d z$+;@jNDiu190F<8f!_zdCQX>kX7d@Af-JUOXALRKYfy6VvKXOJt52Jnk=(SvB`IfZ z{WDgvan_lok*idW&cQ$m3o$xW04YV#`2lyiHjD+JEMFCsuDA&fACv$yb@v^;_WI+D zlOx`dN2Ou@UoZ$@I_W>@hI|@;V0I&NM&G~724k)=v*(|p<0-*e7~bU7e)WU=zYNgu zS9WRL^ZY(v)IZTf$owhj1@ANY1c7y`bb*RT8by29=xkVe^GYTwa3@fXtY zu-S@%{Kxace7z%2nnfG2jE|Pp`u%U`eN$)JwRJ6I00xZqfxDaTk3xxKD%O#mDr=H z8?|4(i`(goGr}%ocrtmNAT^yKK(lOw6Zv%;eR0{ET+4Wc*q@|>{{V6q=3M-qLMgI} zNE8VJa(Awkf6EcMUTu|B3+OASwjFSP8zG7*@^eM1M>Pv|3$N6~_XM4`plDth;C|3PVoN#3Yh#(2heZ(-Yw_$rc>_eZ;)QAOYAVX)E@6VAlBkF(0=%8 z`GR`%`C{M6cvA@Yy+`IvAItrQA3@1bFaB8l1}G->hIin+{T8;L*l5^qp`*_#y72!1 zA*jj@;GwUt`;1KTmT-XZle#qy{*&!yzbeIzkjo+iqy?}+Aa*#W_}4H5n^GGh)xC{- z%Q zXi)yP#0F!LfhR`TDM@}<^)obomKHIzjHUy~Reu%b*Nf!-m? zfwvnU&c8e+_${(wvh`K`nS4FvX%!>|ls1wYhy)rv!PxuJ*x}C*;VivDuYTA^fyMZg zdxQOc*rl#zqAKHuyZX%v=HQ$n4EAP~RuMCgN(EFwLs?a-3}}!AC}KWm23|68GnQy_ znRN*UT}4>*y-=V-0yGV`BL4t!>-N=BmS z)HVhkUCf$&Cb6*T%x2yTZ7&#(sfIe%0&Ry4{4Zr^^LHqRa_bsPA+igrFjcCp8i8V< z)fJJ#kHbPqRshKJGs5Ntkv{P;Bp*67m?BvPipZ|3uU3?0c$Jk7sLE+uJO2QgAkD;M zSs930P)jPG@YzVwhj-ITF4}<7L^x**mn_axDFBS@BbIka2c?(H(ySwtBC%DDPdN!H z+capx;x}?D&8ni(<876HoaOoRat(Qe2m|^PN!EQod4I$;qfeq-?q*2x>d727p=eyW z&QOa#9!>{%9xSu8lgx_mACnTiiiCJ%e-+UGD8wZC7xIkNow4$CCTPHW_+cM z^hgn&NMV$RR)`5yk3JOAr&Mhee}**xH7Z!V;m6d8BFQ?)6D=2;#3n$w9Hf3?&zoh+ zzm};RoU|}U_>S3zk)+oOM1n>@xx%k+^c3;^TU27n+Le}oYQ=QSu)4H`+OUeVoNCD(6+@6)bLKH9 zia^=4{{V)_2j2qO$Qv^1mFQPuK{gp;Q5pxk3=EOzHfW7zy?9rO&c&5Q0cuMQwp)2c zleB47tN#EpF=KE`Nprd=(A!hJ6IxM406r@pTRx#(OD@2Qdx*i5 z)qr&t^qoKwBMKFC+h?vj!E`Cu>2j?Y>(ocB-)`)Ou&Y{U*kxB#GW#l&aO>C${{V6a z^B`d*%CehMN7MYrLOw)REGfAy)trT}q1+3tm0ZWa_@q@*1(9R5)KK8yNXyg$;2P=^ zRo~>N^u>^HDzLPL8^QRZ6YBwA`{6(znY$legZU7oZ`3Iufk#!?{{W`7eqb$KgS8=? z#NgmL#getf;%#qpYd5VpJ$?OB#SGP>=Jk zuTS>xU#mxx+po{J``-O<6O?c)t_0!- z2YeToXdc^F<$^QA0NSznGkS0H{W^c!9~UkN(|nH%RZ?8^q87I#n)lcar2bXD(ELj} z&0v2Qr*6RfL+Pf*9Omez*(YW|EnPEpH7)yp7>J8HjTQ z)kK0OF`5>x6qG$I3tB%b9S$LU6XaPl+32!@Nj!&;`M?&Xf)!~q0>K0a3*;#W(((gF zsNZ~(;Kq7#v7)X_XX%bv0QX`rdYGj1jpzEtC+3xE7mRqOmhPm6Xk8B#$zSDzMTZDhk(P zpzW@i7rZ=X4FFBs2J&F1K-VL=jBP!xl(8oaSv0D)DB zEJ+{#tiz4q|yj$3-Ff5pW}=3W^NXHdwZd65u-b9sqlSzj<6TUVSl zg)vJSpwatGj8lbXwK9o^sanrUR@W=Az zc+XZPzb?4a7T5s1&R6Smo#3Q+Gt)oM4R{WBOR)f0I)&~IlkMCSz9r#?o3;gbG5mKO zslUq*Wr|tZzml{wyym%;MUpi_C>KG#n;VXYVS9J&c%J4?6%u(*m5@kDT8gXG2-?8_ zYVHX9@lNvIY?5!akL7}SxRteoL~mnaH|_1d1+|BoA7nr}Peo?)_c1h)#Ii!s%9f=_ z8V=@yu=Ja=xIbO*0lYJZvdyK(#kB+P(1Ja_M+_$86HEd#mZ}SVx7+^xaVL@DlCPE~ z51~y0qS)2h0<4gB)Gu$Q@C_Pq;*8fkTsU(Hw-@2$Nu&+Q>HPlymKq2d8|FXH2APf) zuRMNB>({^dhos&1#P1K`5H@zRRFQdh-?eo5^uKNJsglOu?M&`*{-sm4Vu=U8f9v}F z@oIQK!Wp4b@3&ayyN%*GcQpez<#^pGoaQrr`eoFZ<#$YN3ioBMdAMd z^%{8UBZblb0BJe%x9pQqjwg?7DOloTwmm`3!tjQiu6b`Urc^&Mi(YN9Pe9bb{KhUd z-9h&N{-YFgw#8U5u2w^iyuW;N6Jco%Ak;#WZusUdnEY|zQ;rq&*RtgU3O(>O#5r(A z>f(i(Dn5H{+v-0&6$p+Z`HLuYt9MogzFqNGT^^`jJQalmnkHfK2+P8ZpmuRqz!qEg z6o1POd4rORPR1qlxbswdDfqx0%A4B*Jg>$Y83V6x(2n@9G92_ZM(1Wp4&Agr#5!ME zKdwAZq)Qy7?8qT|l{ty=20w6AKQnkuENUG`n1w@nej#IhyVuL~#eX(L1G=@1zA&}= zq5U^MENmRKitB1YQZB~(b^O1s*bL^63!C$9qyucZXu|J0QCVQ2>@ET?c28L%N%SiMe z;`4l;fB|amo@TXG7TQ-87clsK`Fps$#6QIP7;SrkB3V=B7E%t@4GOY6Fy^E47+IO?w7D2oNfM&A}uM8P@qSVl< zva>03x%1_eq-LTh+8p_g&aUytTe)+Tw8_l*5%X3e_k`k@GPr66KzG8!;kzurro(B#$O$7G?@vegatK`O?bO?e8 zW@w{H7GXQAkjl|UocNQ(oz;A$bk~=JLQ0A(e->GgG7}%2Hg8-(OC(urmQf=pE-Xln zXkhHJ_m3P!SSE!a#Un;{B-fM*SZgV&H(hpYvc|m?6>Yx;v&7|f0tHwd$rqp+6i8AI zk=?>k2UZ4rO5g$F%@xuM$dPDq<+hl_2@}XQP>|*XW-!cH1|ZlvaO3R!fAHekh0=pU z$Zd%vd`nL$W3q)0wd@L<#uK!@`Bpp`Fcp~A$4{u*g;PUuv9O-5qD>^~K5B*r;yhB$ zl{Wf=v)ur7Ehz8m1^mEH+C>BF!Iu@W2m@ku0jq7pfQP$jj1U<0W=~|SSKMtvK9#S+ zZs+*CwT%NGaLXAx?B1KR>P{hYnv)7XFy2f+wV0uWL+otmrzhu9NJ6&iMwS|th9bCL zU_64t(2TuxtxZ}%?juABgRn{zNjR941%XBUWf)mDLZYX8*_nW|y^fe4$wRo)vZ2x6 zv&|;`je;+=3a!Q+Z-Q0oXP9LgUCVCk8qpO%MJli^^%M5+{`DZImm)zq1+_`)1&fkMq2Vl=Ng8BL zE!W|A&+xSaLWMO8BJpGA7x1IK*~DdsW-!SaC5`%(Oa?okNnXP3uN~n6HgRF}tZ9Ad z<<;D^{{Rt@yuy2Vi-HGSWe-h?0_Q$UF}0xlH`2*o%%Q-qb<`A(aj_s?0drYG39x_9 z=XlcmzrlR`x~pzOF%~R}Xyb0xq?L_>d|ITKQ2+on%>0uZ5XtefmvOaLsSs{2a?wZ+ zKnaa#0to|OnnH(Q$COYjwo8}FPoaBE}0J})&i?RM9 zpnS#HHFvlenc`j)ye60rP)cF9sHtirrPBlR+;+h=h4*Am%9#HEh8A!SY!KzL^hFF# zOUt@TUr>LR8XU2`v1t))m3)!9`zZ{Hfz)DuDFQGz){nj-a+wl9#NJDW9jtZh^auL= zuu@uA8pn;o6XW@?4FmGNm>QQe?S;A8awH&jC->j^9lltI;d5ED*c19!_4UG!35K4k zK&S76uLOBLrp*l~?H!Fwt)_zSyLAAY8*hL`7H`|H(;I{Ay~>FeN+$pbrA^pB&XBf+zq-@4zm6#10 z&6XgluI|7U(89`~)gp~e)O0jd@rj*cv{FbZ+Fqu450&NOxv#utShQ%=5YY1u)VHuB zpzIHGSB5`mPI~h*OO}m?lmtvwejQ-RW9Fd>X$PUyb~Sc}OFWqjg3+Vo>RZ{_tqXOd zXH`%D?g!U4aefH$&*v==UdX>!TfO@IF{h3&%mskrHD*F}9nc10H0OnPYW+w0Vf@*^ zb80(*N9J^&^8Q$9#-l5qPTsZoS8wOGKWAkB0LQP?5B;Qn-wuAtgdBP+4@B%PwLwq_UPO0qAc~HSz?USRBL3ikVoRz$QR1+VuYb zE5J7u&O9@qVQV_?wtwfPr85nE^ibef!lBivIwJ z@D+9~eQUSK3?1{f3mffRI_O+SD-_Ts5s+caM~)qF>@IE>fwbjCNW0?P;BxAQKly9)+ZBYFY+5{> zly4~qee3!-rm2M6Rg=-kE&}yq-yOV15BQG{(OSCU2^$rmJetP|!oo5!%1MQfTug-< zufQI-X~jt>*nIKfwqiRBH{2X6D*^!Co|ftI{=YMdURbb!Clu4r#Xl<^ zs@59GA@K`LJ1h1DPtb$@UrxW4D^5KOS$xQ306EfCSr$aL1cwo_JWZ&TE3I`34x|wq zVb6wHe8Qm2I1dS8u|!*GT}TC*fgVg`g7o2>jT8a08O_=GQQku6$5ygkM1oCjMxv~( zs1Sg+r+Wu+Rc*nzOW-C)yBvqy{goFQ;>?+%CJggEJO|xfyi0|dp<`0DGKm>v5UDH> zX=V7`Nj_06<%TPyqp2oP+4+!U*c8l0jI3i= zcD@qx-`=h`;>_kVD2&(Ts>T$8gsdFmECFE{m=cVxL1ot@Y3H!G{P&GS zX(9ws6l};e`C^GlWQ~KQRmpO(`%?%SK%%%t#Im!dQhDK2SHC3r4nGl1D@`Cb5>3p;}jS zO_$WQY{i18I8hYHlwCkT2bZ|k}$Fv3XrO;P57gfE0_|-$nwUPTk8*{BeY6qYwummQwKV|J)qcDA`#6WWlLVklbxwd!Gt=kCW@1e2pla(2|k z_at;GUCE}S_+`Ej2s4j}8M5kDwMgLs?iEQkK-d%OHM)ufkg6~<#WLYr%3JlQR_S4M zlfCq6X3|NuGB&Vwaa|F$!nK*Z6$8X(IvA7DMFZJHp-AjC235VqY99I{q3OoH~FjWGWGBZmznT_bWqs#5Q*@KMW3v63_RD8j0U~ z1i#dF^sPR?tKOH*x$@%&s8TGh_9QWeJuGTqYi{&+*q4=-)m)2Hx~=p=zzaKB&_!26 zwgeHxg%w3?5`owcF-WiZO7-qop5Oys7n}nt8?VHC!Y_I*R3+ZqBxDgp4MnSLMf(7{ z(VM9m_^qf1e@>P+&@lx-s;x}=zq&nVxx}9kEXx@t+DKz#T~w;9Og}RnpYW8xWb^7E z4w|S|)U8_&Y5`8&6c5OshDuEX)B<{Tu*CjkA8xps8Ask9@v|IK(&}p+tO(hbMzj11 zuNWoAc{jeQ-;Q$5xnp?Grb5uQixkoR9_#>pI@u`DB}efFD-XlC=aoW<*0eycgpiG3 z{{R+~YC!}wdY5u94qM^Nn1CSCjPpS2>I@LcK3*0;4KkJmNYtQtt{gKdP*!=iQP|LM zjWS*XAj?59P&~0FU?1Wwt6KH;5vbhv9_I7cikxVHw%6)EUqza-1%Q5fNnY69emHR? z%4CFKfk~iI_7p!;ztXYuXT*+Twj+J-+--fnTUYH{Zn-y;NLO^R{{Tx_@v;xkaT3Im zaqkL*p4a_eATtvi`C>yM%mEk{AbfzcM{a|E=;GOqwiR5=)NqONu%G+jizf!x@6?}` zA!N@6VppZ@+xEv?OPI8;mV;EUK|$y}*}ar?JKyui9P~!Vg%1;mu~2yv7J<1OrnM@imuFpaHQrN2)ONl3xvSrL!(TAwzl;kjko& zs-lBlphz;4M$Dp)-l9=c=o*Ue5vkSvHmFavpE3Y)kb=gi_?Fg!k!a(44XQa6O%bG3 zfJQ66X3Vs5D=8~=b=+T1rWp9!#u@m8p@S+c#iI@g+tYP>cDe^)UkR`t^LYi==%?k2 zhu{T^orJhuC&qI5cA0#_RzOs%3*V&=Z^(>6b0-dE&c?w`ok}%PuANW&;kSt(tmuE` zKcT!%XZfw8ofp$jUl8Bxc#uej+hnY~NAbe{0Ek!Vi2UHwnp5t7m_O@@Y~L;F%n23L z5J4(y<_8L0KGptRa4b@akIMeIv18-eN6X5uW^xx#aI^?D>`vSF7*^-zjM01fD4sFTa!VS< zN1z+NzQfztVdUmLFf|q)Z>SU0Y8zq`JCvvug6uaZ=hwa&!_1}3XcT%_Pym}HhQhkz z<_vsMAJ51mK74qRNZK_difIQ<=D@Ckw!A=b?qOD&SQIq&`gHuhx76X^HEIm3;He}A z3@G*a@9*@$&ol8|a+9an1z!750*6v-rUV8@kzJ7F`Gm(Wo3|$Ge&1j7#Qy*+K<_vx z?2NWU0rEQ$+!1%S5ao^-QbH#}q@krEh-M>CzTJOH_rWhOb28+HVcH=f4695!2Cxt0 zPWWZU;@$`j`;;-nvzmvOD-{9QtD7N>*&R1)?f(E{7^(8V$l#Oz0G59Dmw5prL$rZH zY5;ej4zu|KUf7Gs@}6OoGAk83f(W8NQT(u0iv?8Y4yY(a^T+KNC5M%VbAOorzidlo zxISG)yM_66{{T<-#nmse_4C`gq;)v@uTZS^zlKu-XX-r$P+Tf|1NwhM4`AR@H$h+5 z%WvNWi^cNso~6E2zy1*?>4tgEeY5F62Wzk2`r8_)eS|perUSrbW9I(=sSVFz0X`(z zN5RO>-AMhvefIiejv>gSvv1t>{kHypt{+dwrGBQbpdZT~2))S_`TqbT{=*v%AMA|{ zGlZFQhcl0o!tqaSxP@a(WCpbI8Tp~1%BK2{sN4CCzMsB1&T_>qgJgC!M1NoQ!AxXT z-|e^c`r$t-ZO>EK{{W~JjbWjN*cn*1*Gqitm~h?=9Row9*%#aLqwQP!V3#YHP@_pC zt#us_)7#{4>xhX84S^=QT@&^BpP(3jk)2AgU*tc_d?Cf@+xk~&09iM~Djey?tFHLA zbhlm4^210_(7Z0Oe6Z>W30tn8*X{cZd2n8Yn6F$ZGW&JMW-*SZJ9NQRR|+=UuY4lW z94wPz7JLka&{SUo*s`9?a8ndpU`r<@O5^B%uh$nJBXaz6LfVK{2V;Bp?f%E|81YUv zMLh2(YyDTX@Jr7FExo>o0%WWb_;<-eF(GLI@*@SDjaI-NG`eWkq7|D^qp&3E(x(>W z*H+VD?Xer4^|xC)*7xiU5sN>Tc~POtTM%S&#)#8N7+F0>VyP3bT?#3mQrfV!pAUn! z0z>ZoP`I5>Vf``heH3?y98K7wB_v3(Lj*A@ffP!&>##JD+` zSe~eKd2ARV(x?IlqCq`UijsG>+~C)UaZfUM!ksd1HMO#{2DU6wSR+ZP!V-NoOteVE zerJX-5-Tce7v_bMBFb4$1%2A~HikTHC zk6|iAy0_klB88E$y7+U9$d24h=9z8bRu({Z4Vc_2fD{JQmT00e)-&HWW@L&&0g{xO zM!G;w^jq%?kHi0<4^FdXI`Dn!diB=@16zWE5h4CN59u%(HL zFa_7}mS&h4rg0cCxK+IZOv7NukIR^LiL-M+?2-jo-@FUx<7<71pmJk1$BN zT*Qy9RpD?Ea}KeyZn=bpLdqpvvYBk89(1=C9`OxQB6CX`J4F^#%ZQmHkM9|OIhvi4 zO)#S8kh{5&;zyXBMvh|VUMDtewrJ22u?l@FOaRn{NYu@w76lCznXPpVhz3^%1(k{6 zHgE7WvePF>&5|?`Mf%%X@3>Pzx)W*fIOw@;tN_QU^1;-9H!JJ@^D$5ISORw&ABMpjRrSFP1Rc5p zYw|7_eqb-|aW{@=Y&?{=1G3StQ)Cm?$r?fV(eLxYp9ye4?f7V*YzFaJ`JGRy$=r6> z00Ve)uwcV&nF$}=F=>;p_k+6q4`W0Za0c{&+^DAZWoXL%ZA`02>(l{=6(lF(A268S zH3II|yPZPL-{TYoQMEL8#-sZ!b0cS#MK&dea!DGz#45td(J?zJ$W0nR$~?_1(C6mm zNG8oj^E)hLDwR+*dUa3)hWNEAup5D zAd#t5fK29w*rLSO5?l~(+B3!l%tnA%)w#O0BF2-{4JN_uVE%ZwoH(AQCt>ej*5sY~ z;@$gIa}052$X$|h@*vuk%bzwjpa3#A<0brMgs=go&ik}2IXjEs9}sX zyt#BzQ0DNyPa-uI#AHpjC317^`QpUkRt%LkoI;mwxN*SK)u{YOsri3H5m{*O4)gro z2312!eMjJpXvrb}01EbBcVZBG?g1IOz)TG+8k}&CGyHKf@U+q$g#};YkrH;9gO=lL@;E4yh_p&Bmh74@qFQysNA z9;yPBKu}xCeAyUcvIThBl0;w%>vaY;8{UQl_1!`p`f9YNgGjr#S(@6zE3Dsie9=dT zS)?wEcc+cU`<*Nt4%P`(vEKxKCUe(cigEqZi@=kMg;iZE%_)j zc~E}tYg%g6AMp@cm!`y90bNDbkVzGRR8yC@DN$DPyDnTC9P!0K%Bxxy9+q8G#Lw!K7j}Rcy?l^tuB@HrGX{biiK& z%BPYNC1s}6Rjj_$ZHe}~t%Y_SFk9iw3YwM8+gQJ9pgZ&+{RkgSQ*l!IT2m-jd>^x_ ztP#7{?|Tk`Soi+` z3z7c-F~zaW9vp+~faz|v*#7`uEJW~+5U0y9NrBQfrrZ(oHAnNtCX0=|roi334&i!j zZ?m*@&F=Rk>HVk3{{U~1!pXiR@f(l=u8x2PR}{dC>JI(3J;wNL;wlj=BzFpS`ijQQ z=AR9?e-}%oNr3c_bh4lDu-$j+JyDw7YZ;HkZwPa_In>A+c#!PCdejTulvrRk^~P9U zA~@fh+$+~=4pn23T!>FHy*AJ=1pw$Fe-D|?DP z!Li@>#c$#t5?7KYL#t%>_ZxiiVdrxr)3*Cz$y@_LStl~vsRP5=y+fdGm$>@>0AD;j z;QTd;$|zVNiSI;hWS)S6MGp9dc3|gnyNjc@?SgIt%}E%LIJc2i0qslB*S}+ci)PD= zHs7N6H_gSN5drC~{&Xw)tWI)PSr#Z}&^7((H`>TnO_Y0nT}}trBA!Szby?rG{{TOF z!;Tr7(G+YhmPKRK4@&|-{RTKT^+qRuAgsR}W)tQi1}2M90EXLQcdudZ^~W%B&RZ>? z$~$Ngm1Za6^=UL~-1fho_#E*c#1+moWk;w1Ky|aQA5-~#Fd@V^+p8&S1~EVrtFa$H zd@rP%c@;OfYCd5e_}|CjZ1TJ$5utD9MH~n4{v6q-nk5!xL+*D_Tl}!jelYoom89wx zg?j)8e}8Nr_zf6dA_?fBjeDM-=YtcXKM)ih-go{>Jn_CinaWBcR%KSCb?kSe^gl62 z1$kc=niUcK-Cs!fwGpcKy8QnD&kj6b;euS$+D7X!Wdna}{JZ?IU-3M&y3JK>+t?45 zA8EJ>waj7N6}EMc*ZS@L`19^Mw_$%@``P|jUnVGS4g2B2h##l6)&76ykJ7SlmF4j? z1sN9(^;!I{`rr1$;h7_9pgWKB2L5%9A%lN!+xdxxB213GTGb@nd|)j0M`t+PWQ@H!0l`%W((L*-*bPZ-*4*iA;IT5s(>9V zbU(`oXG-@Y@*~^ZxANb7J>~qVk%FZI*2(rB!*9){r_H4G6AdT*h8$1LbY3A0KBmPX5dpme2o z2TQ(7?^S*)lM4M1xw#X=K7hMGQ_UODTDl+g+b)+2UHKB)E1 zR5tAFjpaC+R|!bIMZ(n!Hgd2zjJ0YP;kNh48tlH|cI zYG!a)ERn!HL*rOuxJ^(1Jgn=d(8fdXhET?hqBe$&g0GrGE6dL13dPCo2IMdesl9@M&wW zDrHHMwCXa+8CJWKh2ajxXAcrGN@Y3;R@^eMB&h{}16_tftEoVxWT!yL^rb!&^Gu}* zDzm?&SyeHV+?_}yZ%&a&s2Bw-!XbLu=Qwit(gG_x9ZbHWLN1u7okElXTp9qmV#)@c zB_9eo9#Futypg`5&icHf7iQCAL83qaw0gayuSlOOPj{L;akzK6E3cDyc4ILlNdl}- zDUBXtYE*)r62FO~c@iPmh9c1b|I*PK}p z4MoFc-MsRl8!*EsJVP*5KyNS1&*v;Cpx%I}K|m?TCDROOr$vu3*zT%yq;SW73LQn} zFLrGhl>*w+&&3NY(IjkT)f_Rf14=xS6x2eelomC0kf`*QmO!z+YD)hACa|zakxRim zv9V!57~wI-i|?vJ%xcSN3IW)FOWj2QaEJ<9QPr-1JC8xrNBBquj;^DpYsIy}c-NkU zj;Pv2?m*VsOB?Q_I2(`w7RM3XKa$nNp)OA)y=6cwpne+3TPQQBoP`G>IP>=pqpwW6(kb&}_?B%b()&wy;H+!7S~YtkEjgX1Kg#>k_N zGjvyrgfGl%=>oS>m-!=u`|a(CjI5WadwE9wYD%5Hdeonx>yCKBhI2z=7ykg_4Uhh_ ze&-*!e$4moSN{M>P)PdNt*@>OP7>f)spzKkr_2CTof^;nWYwtNnjtWAwy_9pWmG$k$$x`hX7KJ(v%cAO-NG<6wokgPn>v3V(zT*n`{} zBd+}k_rYH$c;v_#S;(|XocLcFLEN(ghz1Arl=43c8+0UdL+f`R#^` z0mCU_Q0AN?KNq>R^D(o#SrkXhexEBBxZ7|sRq*a@k#=ui)YE=vzv+JG1o@d7NZEGv zDP8(*HX^-^AM(JiQ-F|tU!ea0JTV+#H%eYfovTnNt0B*^?W7!$i#Unm909uizc1_d#Pqg2AUatk_l?w4Gg-6T;(vqj4J;d|1Z;Zkup{ON zPTepwHe9Y0k2!(^(E{C7_2K^jh~IZ&q!*4wWs)tlvIUYxI*!4EP(m)l<59 z1G5iLQSMakeT}gq&$&)s%4SP_+fH9GD!-Pxw81;6^~UM~tQnMcIvql!)?9~X1V(VK zC#BxOLt$i&w~eR*!(&^KdO-l2t$-?4(uv|RJDPTat<=*tR`NzXGnix)zGjd=d``*5 zQdQGBF5NI#vR?KFWB&N^3Ak8eoW~6= z3thy0r=F6fl0tRF=7L&&cIZ^s5#%`{K4w|$4ZsBc#?SAEoG-*cJRy0{{S51-FBIn9tQ@kkbn=#{vW5Sf0)Hc zne^|mH}M<#uR`Ixatgv^aoM_Ns57EtfpkO~{>uBvY8`r`J^^3H1}*9+@UVt-2;gY}?rLBhG3 z9L0IaBaj-^>C*Msw&8~T8tbuAF{`EG&(Wp%9aT>f_F|r_>!&t6#k27K?=h91IL<&_ zY8FV_y$|)nOx93o2fE5P`4M;e{{UO!-0@e#!z1~zqNx>R0ulcJ=Bv^2Y9RF0(-hps zVLz17x{^WCRGo)#MS?%Xq>rC$YALE)D7#mfaUKyh45g0Cas!3ze`DZsr?PR9o|fA8 zQ@{1Ux7QO1l`}$#7$LfPfzy5HfO>v-o#ihLmRKc~r3%UkSiszy{vv(-uo2>)GD*Yy zm5N1Yc$VmV8=DqMyVzX~tYMmYTPw>4BB)r~pG>CQV=MQ4i)xWht59nD0loTrpL|1Q zIN+DkLGuaQ=0Df#iS7%}aAzW^qzfvI%K!lb=so+AP9t);e3D9k9^a}~$fxIG*!?#C zc)2k%qGL!ZQ-@8Wc-iSi{{R-labNbq=OgAn@x|4asGh$Sp5FfeKdAS{i~AI3GI^OZ zOc5oUE+X}58lzc})`r%hjBiy}M0Pm5`K#>J!{=t4WuuOAOL@_O8ePZlxfTUFCG_4-%mi1}1sTv;D# zUkhI=nJP}T^K~nKgwUh&8{(wmJVKa?_WnZ}`gvP4O?wT&3W8`$9$M@WjM)k4h2fV$ zys*%gZgK-Kn%}SlI4t zl0DDP=z9~_=Z6wP8MLUj1A1Zp!uRRF(+s%B6qS{PWFYJ)0DnSHPK59B#XH0}^r+_9 zg91VC{@0%@`YD zx<1=&b}==~{H;64=C<_y|6+>m&u69GqS4nzhY<&$)WB25se|?2Ozp2G!g#*w2{W`piv(U*(4C4 zT~@%_oCo+Z$ok}2n#6mOq%(yREAYakO&8Ir+w z-0NuqC!|`en+3hGTtZg9YmX(XqhpyBj>jpN2IXsAY5~Gw$BUmw^B$n0`j-%2;kdQ&CO6>#>f8H)-GCYLY{?MCR z+KB^otXEM>qbaD`ln{op00S=!#z3kITB@A{4L~t8%xvPA$%3BIl?mcvFgdn_7m^t= z^q#Foc@~icuE?pHMYQ!O5rREFuQ^+V2Zd0Ja&-zD9Uvp=Q*5C^CXi28C^a^c9p$-s zrje|W(#+FJR+n=eaFR%%2bCORCnZQkKy;5#GRgAXNL7#~rCkc(Q6a3j3^x(b=}YEm zR`p)lz9GdM0MN|8>E^F#D0TOr?)_DN4~7*PQWlEJ)-1lXI;7jwk3bb)P!Z`>-sl1N zZm>m$gJmbwu;#F`9b>m=IfE-N7?%8bzfUYZQ6S_vRf)Zh@c z&;V^|0s?cl1QD`B97{y1$W65(hAcGhWqSicC_M?VJy@>cQ#jOUlP-3Ahw_}RU=ZqV z0LPj~i)M8RB86j?M_Q5CM#!Y7MF@$XNNFU^dSZ|r8XhTMc~?bO<3A2CpC&r|HZnAdq#@HK zBLRMZtt4tzmuTbE18O+YdEQuxJ$X$UQ!fop2|ih7V%fzb;V9_&sHKP(Z6q;rKMd2} zBJpy-kf6&Ua_^#nZ4QJ{Vl5eEDh&qATU&^tVb#>r!tN;WOrdHv-AJ;kv8->T0%=G{ z(Ts$KA%G!R#)A`F)xzK|<`O{CUSy+6!HWJL_fnJL)J~-U*%m?g$2BAwk4ql20zk6i zyw^*%1eZi8Y}jc!>MepUP+>H3P8~`}OIEK~cM60yDFbd4uw3O5a z&H+I2P8`$%-=ohpv9}|wbS9H)6-7sKFf%07Ht0`F??ejjY#Sc*zg%01krg9d5U~^& z)roM{K5ZDI-mmyhD&8{yM!NLs8b?mdYvrXuUAk$nd+B zC9jlQ!yzMA%hU+@X`^1mH|#Js;xxoU$Sf8OUnB63(^dR{C$1EtK5BeHCU7X0Ocs{LWKv~x)0ph`3|34K9MM6fM<0O2VVaG zQT=zpK7WB;mkLFSV_RCOI=0(WP#-oR_c$8P<-D1!Sh_Yxf6wK&>+``^FL5;p`smmd zSE-yZWMkXXz!FK_btx3g4+iRBU8|g*4)mXK+ppz{$3MiivoX5qN3X<4znJOyV$;b^ zKs^f8QPW*3e@SEX)G=K14=%h?8wQ-}2mWtXpRFIC*JFbh0G6>&xHfNL?hS$RDpZ00 z0JM*4Z+jLG0pW_u2~c$s8iDH07RVh#7f?tBi+xC}xN}u)i>s03A~=u)B!|6Zk5fEl zQ0W^ajC~A!b`YidlvmZiyM)XQ&QNz4<$aL z#b3*uNKKWSD!^)J%#mK-ilK-k_8$mtl(v6_VSfO*%kJ*cu(+muGbtXeBn4AjdZT4D z4@+yq7rF+@{{Z3H;G_faK9@)C7=UYg8i5qnX_~AXH=z6mZ*(HO+7?S?sc9;@kwTZF2CpeUhReK!~1=Y)9AEX3&E zVyjr_K>q-~GuH?4;DuTw?S07pScT?)w497Iixw-TU+eSfkIyWosS+03H(NtND@b;+ zZoUXB#eNbpv$94*ME6Kf!~Xy&>Ib)B(-rn>mLkj`tByc69Sz?903*HPH^k0mmT3bu zf;x4;=LhC8o|d{Nul4-KHw>YFWD8wqbMZ-!YTZRrV~}={jfnS$TONOzL=f?u&QH@I zGR8aau>Sz>f0idQGi9?YsxlS#xSMXh#csF1sWv;>l5r=%c$&J?I=%D;uD-w5*8_av z$nhXD#^NU5U_rB9-}c8S>SkoZEb6}{>A$O{uciG%vcfNOgZ}`P@O@Q%#C$@_$25p6 zA2&(pf9kz~0{;M=fkzOzpR>OZ;uD#YC;PK2d2-0EmW#Hv)}v5j2|_k;OaU9n|#XhAjoC7J6~a7zCL)B8pi%g#Icx;gG1Cx?PS%P_di@P@b8hO&dBJDv8-{K z9!3Q1VO14D1)a(C?f?Lj&;z(W+rANS(D6+km6Wm7qJeOGFdLos7xSw1U47u6hw(e< z)FD#6g%_@yov0rF06)(Rn$qY?+6}<^Av1?Hl8({c+xcIhR{sFlrX#^1%=zxJNt<}2 zBq;lEC^i^wIe)is^i^RfD9_P1cUqI6UVrnElV^+au1@?aEc0; z988-L>ad3f`z!Ix&e7#m4M9(tJTb7p;-+oD??(Pu(-HlWie(Z8MM=;GCsEQ*<-DIh zihg~sRN`}9#aG{;>4Z3jY0GMPy}v)tZ_r}|$>wXd18*(X$hD2pE$de%e(iec$@#vG4|LeoZsRh32`6`hC$zF=9! zf_a@Io@o5IhzCx%#+5q`!+RYKpY*^4+)#oDBF|bq5A(wumSl-9vAeIH`{J@{hfN-t zGJ2e!Af03liT95KpvS7Vd@tnuq}j!JBUUO)E!p)Fc02C7-krfUw%F5=ZcgNG8v{#9FK^e@t+(s#2W&~LbH45%4P1SGHA1?EmkUi_Q z)sfokYpS~1@u#AnRDMf3_F2i!V=>%y#P=ldGc>3xP}ZsiUZ-z_Z$o41U{2}|1z=*s zY-t|?d^eL+PRJC-H7lQqOK!{o^r#hOi1*VxNgDb@gt;Vz)H%gDvFDO82s-4^t5Dq(jYe6OYMg0gL{LhQp<7p}Yz{@rc_?XV z29T{*&U(DE=oT2YHih*wu#7LEis!hMS%xwh?|$m)ikL{?U9uopqd-g8)Ym|;y+i_n zjr<^MKN7|~paVHk_dO>$AK(BgycNz%( z{_Q{j2wTyl`bHR`d1L?(7_rsdC?bOI?Q9DF01@l3v65pn11rvQe?K=`2OUR9*-Y?G z7L+6|0Mu!Xk1@%hTXC*2xs5MN7E{#~lA-Yrm|7XkfpnRIMg_zv84H&6qiStz^x!BD zXd`{ZdOwsoaz8kN1e;wxk|Kl87Hl6N74 zOEVGyjdZ-LE~u3n+eBqmnraSYk|u(LdlQ(Lt*TEnIap)Vu@c4@^7@oz5Il~6##j<2 z*H0%EmJt|ql$Hw|Fhg=m+Qotpvayf_FX|0UzH=~A8!q-9VpeR#fUz&BA<|5X5N2Cw z!8RPm$rN=?Q2Bn1nf)5}SzBFA&z)Sna>~l3{YYeTODxK>DmWn1h19Xg4T2$_Jw&i8 zPVxEVWL1z`AxR2?TT@8!2=zDdX_9R{2`Wnw>KHxejKCpd2K7lJH4*C`T-kw4`;RJl zDT@u&^g4@R0l0exmBQAtbSl*V)URM~{hJhbBXwo5L(&BI=@&<1E^Mfdc;XK%$){0f zC2dumnnHDg6>Zu@vg|izq4UM1;SU#vMlnU_qe3)Hl7v+U;n@@hMhvn5BnsgWT7$XJ z{{Ug{1LMqRvs_JpXkg5?UoIGWbwHscm>Z_`0HBrYuDcf0A839H43O(+qtsX|a+t@Q zv_nfEkcD+k07n|G�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≈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 VECD9$kO@1U=N@DB9clwt|Jk)v&zJxJ literal 0 HcmV?d00001 diff --git a/assets/README.md b/assets/README.md new file mode 100644 index 0000000..afd350a --- /dev/null +++ b/assets/README.md @@ -0,0 +1,5 @@ +# Assets + +`20260524_132700.jpg` is the reference image the colour palette is extracted +from (JPEG, 523 by 930 pixels, 172,725 bytes, supplied by S01 on 2026-10-07). +`REFERENCE_IMAGE_PATH` in `src/hirst_painting.py` points at it. diff --git a/docs/artifact-registry.md b/docs/artifact-registry.md index 5881609..a373739 100644 --- a/docs/artifact-registry.md +++ b/docs/artifact-registry.md @@ -15,7 +15,7 @@ document of a type. `Primary File` may contain a glob (e.g. | SA | Stakeholder Analysis | docs/stakeholder-analysis.md | 002 | | PP | Project Plan | docs/project-plan.md | 002 | | MIL | Milestone / Gateway | docs/milestones/*.md | 003 | -| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 005 | +| RC | SQA Review Record | docs/sqa/reviews/rc-*.md | 006 | ## Languages diff --git a/docs/sqa/reviews/rc-005-code-mil-001.md b/docs/sqa/reviews/rc-005-code-mil-001.md new file mode 100644 index 0000000..5629c68 --- /dev/null +++ b/docs/sqa/reviews/rc-005-code-mil-001.md @@ -0,0 +1,73 @@ +# Review Record: Code of MIL-001 (Colour Palette) + +## Metadata +| Key | Value | +| --- | --- | +| ID | RC-005 | +| CrossReference | [MIL-001], [BC-001], [QC-PY-001] | + +## Version History +| Date | Status | Author | Reviewer | Change | Commit | +| --- | --- | --- | --- | --- | --- | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Verdict `Go` confirmed by S01 | pending | + +--- + +## Artifact Under Review + +- Instance reviewed: the code of [MIL-001] tasks 2, 3 and 5 (issues #2, #3 and #5): `src/hirst_painting.py` (`is_white_shade`, `remove_white_shades`, `extract_palette`), `tests/test_hirst_painting.py` (18 tests), and the tool configuration in `pyproject.toml` +- Checklist used: [QC-PY-001] +- Scope: full review +- Language and domain: n/a (source code is a technical type, always IT Professional English) +- Language reviewer: none. S01 is also the author of record; S01 accepted a documented self-review on 2026-10-07 (see the review record of BC-001, Action Item 1) + +**Status of this record:** final. The assistant read the code against every +criterion and ran the checks named below; S01 confirmed the verdict `Go` on +2026-10-07. + +Evidence gathered on 2026-10-07 with Python 3.13.14, `ruff` 0.16.10, `mypy` +2.4.0 and `pytest` 9.1.1: + +- `ruff format --check .` reports no changes and `ruff check .` reports "All checks passed!" +- `mypy` (strict, set in `pyproject.toml`) reports "no issues found in 2 source files" +- `pytest` reports 18 passed; the same 18 pass in reverse order and each passes when run alone +- A search of `src/` and `tests/` for `print(`, `logging`, `except`, `noqa`, `type: ignore` and `nosec` finds nothing +- An abstract-syntax-tree scan of both files finds no shadowed builtin + +## Checklist Results + +| # | Criterion | Status | Evidence/Notes | +| --- | --- | --- | --- | +| 1 | Packages, modules, functions, variables, classes and constants follow PEP 8 casing (`snake_case`, `PascalCase`, `UPPER_SNAKE`) | Pass | Module `hirst_painting`; functions `is_white_shade`, `remove_white_shades`, `extract_palette`; constants `WHITE_THRESHOLD`, `EXTRACTED_COLOUR_COUNT`, `REFERENCE_IMAGE_PATH`, `STRIPE_WIDTH`; type alias `Colour`; private test helper `_write_stripes`; `ruff` rule set `N` (pep8-naming) is on and clean | +| 2 | Names state purpose in the domain's language; no unexplained abbreviations, no single-letter names outside tiny scopes | Pass | Names follow the PO terms of [BC-001] (colour, palette, white shade). No single-letter names; `rgb.r`, `rgb.g` and `rgb.b` are the `colorgram` library's own attribute names. The code spells "colour" as the documents do, while library calls keep their own spelling | +| 3 | Code is produced by the project's formatter and passes its linter with no unexplained suppressions | Pass | Both commands above are clean. There is no inline suppression. The only configured exemption is `D103` for `tests/*`, and `pyproject.toml` explains it (test names state the behaviour) | +| 4 | Every function and method signature is type-annotated, including `-> None` | Pass | Every function in `src/` and `tests/`, including each test and the helper, is annotated; the `ANN` rules and `mypy --strict` enforce it | +| 5 | No bare `except:`, no swallowed exceptions; specific exceptions are raised and the cause is kept (`raise ... from`) | Pass | The code has no `try` or `except`. A missing image raises `FileNotFoundError` from the library and propagates; `test_extract_palette_raises_when_image_is_missing` proves it. The code raises no exception of its own, so no `raise ... from` is needed | +| 6 | No mutable default arguments and no shadowed builtins | Pass | The only default argument is the integer constant `EXTRACTED_COLOUR_COUNT`; the syntax-tree scan finds no shadowed builtin | +| 7 | Files, locks and connections are managed with context managers | N-A | The code opens no file itself: `colorgram` opens the image from its path. After `extract_palette` the file can be deleted on Windows, which shows that no handle stays open | +| 8 | Public modules, classes and functions have docstrings that say what, not how | Pass | Module docstring and a docstring on each public function; `extract_palette` also states its `FileNotFoundError`. `ruff` rules `D` (pydocstyle, PEP 257) are on and clean | +| 9 | Logging uses `logging`, not `print`; no secrets or personal data in log output | Pass | `src/` has no `print`, no logging and no secrets. The program does not read `.env` | +| 10 | Classes and operations trace to the Design Class Diagram they implement; deviations are recorded | N-A | No Design Class Diagram exists: design artifacts are out of scope in [BC-001] and the code has no classes. The functions trace to tasks 2, 3 and 5 of [MIL-001] instead | +| 11 | Tests exist for new behaviour, are named for the behaviour, and do not depend on order or the network | Pass | 18 tests named `test__`; they use `tmp_path` and the repository's own image, and need no network. Order independence was run, not assumed (reverse order and each test alone). Review found one test name that said "at most" while asserting an exact count; it was renamed to `test_extract_palette_returns_requested_count_when_image_has_more_colours` before this record | +| 12 | Type checker runs in strict mode without errors; `Any` is justified in a comment | Pass | `mypy` strict is clean. `colorgram` ships no type information, so `pyproject.toml` sets `ignore_missing_imports` for that one module with a comment saying so; its values are converted to `Colour` at the boundary in `extract_palette` | +| 13 | Dependencies are declared and pinned in the project's dependency file, none unused | Pass | `pyproject.toml` pins `colorgram.py==1.2.0` and, in the `dev` group, `mypy==2.4.0`, `pillow==12.3.0`, `pytest==9.1.1` and `ruff==0.16.10`. Each is used: `pillow` by the tests, the others by the code and tools. There is no lock file; the pins are in `pyproject.toml` | + +## Overall Verdict + +Go — confirmed by S01 on 2026-10-07. All 11 applicable criteria of [QC-PY-001] pass, +including the 3 optional ones (8, 12, 13); criteria 7 and 10 are N-A with the +reasons above. The one defect found in review (a misleading test name) was +fixed before the record. A `Go` here is Go criterion 5 of [MIL-001], the last +open one. + +## Action Items + +| # | Action | Owner | Due | +| --- | --- | --- | --- | +| 1 | Confirm the verdict `Go` for the code of [MIL-001], or name the criterion you disagree with. **Closed 2026-10-07:** S01 confirmed `Go` | S01 | 2026-10-09 | + +--- + +[MIL-001]: ../../milestones/mil-001-palette.md +[BC-001]: ../../business-case.md +[QC-PY-001]: ../../../framework/qc/qc-programming-python.md diff --git a/pyproject.toml b/pyproject.toml new file mode 100644 index 0000000..57b587e --- /dev/null +++ b/pyproject.toml @@ -0,0 +1,53 @@ +[project] +name = "hirst-painting" +version = "0.1.0" +description = "Hirst-style spot painting drawn with turtle (Day 18 of 100 Days of Code)." +readme = "README.md" +requires-python = ">=3.13" +# Toolchain verified on 2026-10-07 (MIL-001 task 1): Python 3.13.14, Tk 8.6 and +# colorgram.py 1.2.0 with Pillow 12.3.0. Pillow is pulled in by colorgram.py. +dependencies = [ + "colorgram.py==1.2.0", +] + +[dependency-groups] +dev = [ + "mypy==2.4.0", + "pillow==12.3.0", + "pytest==9.1.1", + "ruff==0.16.10", +] + +[tool.ruff] +line-length = 88 +target-version = "py313" +src = ["src", "tests"] +# The framework is a mounted submodule and is never edited from this project. +extend-exclude = ["framework", ".agents", ".claude"] + +[tool.ruff.lint] +# pycodestyle, pyflakes, isort, pep8-naming, pydocstyle, pyupgrade, bugbear, +# flake8-annotations: the rules behind QC-PY-001 criteria 1, 3, 4, 6 and 8. +select = ["E", "W", "F", "I", "N", "D", "UP", "B", "ANN"] + +[tool.ruff.lint.pydocstyle] +convention = "pep257" + +[tool.ruff.lint.per-file-ignores] +# Test names state the behaviour, so a docstring would only repeat them. +"tests/*" = ["D103"] + +[tool.mypy] +strict = true +mypy_path = "src" +files = ["src", "tests"] + +[[tool.mypy.overrides]] +# colorgram.py ships no type information; its values are converted to Colour at +# the boundary in extract_palette, so nothing untyped spreads further. +module = "colorgram" +ignore_missing_imports = true + +[tool.pytest.ini_options] +pythonpath = ["src"] +testpaths = ["tests"] diff --git a/src/hirst_painting.py b/src/hirst_painting.py new file mode 100644 index 0000000..387ba57 --- /dev/null +++ b/src/hirst_painting.py @@ -0,0 +1,43 @@ +"""Hirst-style spot painting drawn with turtle.""" + +from collections.abc import Iterable +from pathlib import Path + +import colorgram + +type Colour = tuple[int, int, int] +"""A colour as red, green and blue values from 0 to 255.""" + +REFERENCE_IMAGE_PATH = ( + Path(__file__).resolve().parent.parent / "assets" / "20260524_132700.jpg" +) + +# More than a Hirst painting has colours; colorgram returns fewer when the image +# has fewer, so the palette is never padded. +EXTRACTED_COLOUR_COUNT = 30 + +# A colour counts as a white shade when red, green and blue are all at or above +# this value: such dots are invisible on the white background (SC3 of BC-001). +WHITE_THRESHOLD = 240 + + +def is_white_shade(colour: Colour) -> bool: + """Return whether red, green and blue are all at or above the threshold.""" + return all(channel >= WHITE_THRESHOLD for channel in colour) + + +def remove_white_shades(colours: Iterable[Colour]) -> list[Colour]: + """Return the colours without white shades, in their original order.""" + return [colour for colour in colours if not is_white_shade(colour)] + + +def extract_palette( + image_path: Path, colour_count: int = EXTRACTED_COLOUR_COUNT +) -> list[Colour]: + """Return the image's colours without white shades, most common first. + + Raises FileNotFoundError when the image does not exist. + """ + extracted = colorgram.extract(str(image_path), colour_count) + colours = [(found.rgb.r, found.rgb.g, found.rgb.b) for found in extracted] + return remove_white_shades(colours) diff --git a/tests/test_hirst_painting.py b/tests/test_hirst_painting.py new file mode 100644 index 0000000..f89fa64 --- /dev/null +++ b/tests/test_hirst_painting.py @@ -0,0 +1,120 @@ +"""Tests for the colour palette helpers of the Hirst painting program.""" + +from pathlib import Path + +import pytest +from PIL import Image + +from hirst_painting import ( + REFERENCE_IMAGE_PATH, + Colour, + extract_palette, + is_white_shade, + remove_white_shades, +) + +STRIPE_WIDTH = 20 + + +def _write_stripes(path: Path, colours: list[Colour]) -> None: + """Save an image of vertical stripes, one flat stripe per colour.""" + image = Image.new("RGB", (STRIPE_WIDTH * len(colours), STRIPE_WIDTH)) + for index, colour in enumerate(colours): + left = index * STRIPE_WIDTH + image.paste(colour, (left, 0, left + STRIPE_WIDTH, STRIPE_WIDTH)) + image.save(path) + + +@pytest.mark.parametrize( + "colour", + [ + (255, 255, 255), + (240, 240, 240), + (245, 250, 241), + ], +) +def test_is_white_shade_when_all_channels_at_or_above_240(colour: Colour) -> None: + assert is_white_shade(colour) + + +@pytest.mark.parametrize( + "colour", + [ + (239, 239, 239), + (255, 255, 239), + (239, 255, 255), + (255, 239, 255), + (200, 30, 40), + (0, 0, 0), + ], +) +def test_is_not_white_shade_when_any_channel_below_240(colour: Colour) -> None: + assert not is_white_shade(colour) + + +def test_remove_white_shades_keeps_order_and_drops_white() -> None: + colours: list[Colour] = [ + (255, 255, 255), + (200, 30, 40), + (245, 245, 245), + (30, 90, 160), + (240, 240, 240), + ] + + assert remove_white_shades(colours) == [(200, 30, 40), (30, 90, 160)] + + +def test_remove_white_shades_returns_empty_list_when_all_white() -> None: + assert remove_white_shades([(255, 255, 255), (240, 240, 240)]) == [] + + +def test_remove_white_shades_returns_empty_list_when_no_colours() -> None: + assert remove_white_shades([]) == [] + + +def test_remove_white_shades_accepts_any_iterable() -> None: + colours = ((colour, 30, 40) for colour in (200, 255)) + + assert remove_white_shades(colours) == [(200, 30, 40), (255, 30, 40)] + + +def test_remove_white_shades_leaves_input_unchanged() -> None: + colours: list[Colour] = [(255, 255, 255), (200, 30, 40)] + + remove_white_shades(colours) + + assert colours == [(255, 255, 255), (200, 30, 40)] + + +def test_extract_palette_returns_colours_without_white_shades(tmp_path: Path) -> None: + image_path = tmp_path / "stripes.png" + _write_stripes( + image_path, + [(200, 30, 40), (255, 255, 255), (30, 90, 160), (240, 240, 240)], + ) + + assert set(extract_palette(image_path)) == {(200, 30, 40), (30, 90, 160)} + + +def test_extract_palette_returns_requested_count_when_image_has_more_colours( + tmp_path: Path, +) -> None: + image_path = tmp_path / "stripes.png" + _write_stripes( + image_path, + [(200, 30, 40), (30, 90, 160), (20, 120, 60), (90, 40, 130)], + ) + + assert len(extract_palette(image_path, colour_count=2)) == 2 + + +def test_extract_palette_raises_when_image_is_missing(tmp_path: Path) -> None: + with pytest.raises(FileNotFoundError): + extract_palette(tmp_path / "missing.png") + + +def test_reference_image_palette_has_two_colours_and_no_white_shade() -> None: + palette = extract_palette(REFERENCE_IMAGE_PATH) + + assert len(palette) >= 2 + assert not any(is_white_shade(colour) for colour in palette) -- 2.54.0 From 412237afe46bb0f356795797503b47b957ba9108 Mon Sep 17 00:00:00 2001 From: Jens Tirsvad Nielsen Date: Wed, 7 Oct 2026 23:49:56 +0800 Subject: [PATCH 4/4] Resolve pending commit links Replace `pending` in the Version History of BC-001, SA-001, PP-001, MIL-001, MIL-002 and RC-001 to RC-005 with links to the commits that introduced the rows. --- docs/business-case.md | 3 ++- docs/milestones/mil-001-palette.md | 3 ++- docs/milestones/mil-002-painting.md | 3 ++- docs/project-plan.md | 3 ++- docs/sqa/reviews/rc-001-business-case.md | 3 ++- docs/sqa/reviews/rc-002-stakeholder-analysis.md | 3 ++- docs/sqa/reviews/rc-003-milestone-mil-001.md | 3 ++- docs/sqa/reviews/rc-004-milestone-mil-002.md | 3 ++- docs/sqa/reviews/rc-005-code-mil-001.md | 3 ++- docs/stakeholder-analysis.md | 3 ++- 10 files changed, 20 insertions(+), 10 deletions(-) diff --git a/docs/business-case.md b/docs/business-case.md index 47a23e0..6317271 100644 --- a/docs/business-case.md +++ b/docs/business-case.md @@ -11,7 +11,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [b823d1f] | --- @@ -167,3 +167,4 @@ Proceed — the work is small, the cost is only S01's time, and every objective --- [SA-001]: ./stakeholder-analysis.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/milestones/mil-001-palette.md b/docs/milestones/mil-001-palette.md index c1d7966..83cf92e 100644 --- a/docs/milestones/mil-001-palette.md +++ b/docs/milestones/mil-001-palette.md @@ -11,7 +11,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [b823d1f] | --- @@ -81,3 +81,4 @@ Tasks column "Needs its own Use Case/User Story?" says `No` throughout. --- [BC-001]: ../business-case.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/milestones/mil-002-painting.md b/docs/milestones/mil-002-painting.md index b3004a4..a7c8f1e 100644 --- a/docs/milestones/mil-002-painting.md +++ b/docs/milestones/mil-002-painting.md @@ -11,7 +11,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [b823d1f] | --- @@ -84,3 +84,4 @@ Tasks column "Needs its own Use Case/User Story?" says `No` throughout. --- [BC-001]: ../business-case.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/project-plan.md b/docs/project-plan.md index a94d35a..9a94fa4 100644 --- a/docs/project-plan.md +++ b/docs/project-plan.md @@ -11,7 +11,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [b823d1f] | --- @@ -90,3 +90,4 @@ one-week limit then has no slack, so S01 decides whether to extend it. [MIL-002]: ./milestones/mil-002-painting.md [MIL-001 milestone]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/milestone/68 [MIL-002 milestone]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/milestone/69 +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/sqa/reviews/rc-001-business-case.md b/docs/sqa/reviews/rc-001-business-case.md index 7da30e5..6438bfa 100644 --- a/docs/sqa/reviews/rc-001-business-case.md +++ b/docs/sqa/reviews/rc-001-business-case.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Self-review and defaults confirmed by S01 | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Self-review and defaults confirmed by S01 | [b823d1f] | --- @@ -71,3 +71,4 @@ Go — every criterion of [QC-BC-001] and of [QC-LANG-001] passes (criterion 8 o [SA-001]: ../../stakeholder-analysis.md [QC-BC-001]: ../../../framework/qc/qc-business-case.md [QC-LANG-001]: ../../../framework/qc/qc-language-domain.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/sqa/reviews/rc-002-stakeholder-analysis.md b/docs/sqa/reviews/rc-002-stakeholder-analysis.md index 4a81961..1fb08bd 100644 --- a/docs/sqa/reviews/rc-002-stakeholder-analysis.md +++ b/docs/sqa/reviews/rc-002-stakeholder-analysis.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
S02 removed on S01's instruction; self-review confirmed by S01 | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
S02 removed on S01's instruction; self-review confirmed by S01 | [b823d1f] | --- @@ -71,3 +71,4 @@ Go — every criterion of [QC-SA-001] and of [QC-LANG-001] passes (criterion 8 o [BC-001]: ../../business-case.md [QC-SA-001]: ../../../framework/qc/qc-stakeholder-analysis.md [QC-LANG-001]: ../../../framework/qc/qc-language-domain.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/sqa/reviews/rc-003-milestone-mil-001.md b/docs/sqa/reviews/rc-003-milestone-mil-001.md index e44d23f..f20b562 100644 --- a/docs/sqa/reviews/rc-003-milestone-mil-001.md +++ b/docs/sqa/reviews/rc-003-milestone-mil-001.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | [b823d1f] | --- @@ -67,3 +67,4 @@ Go — every criterion of [QC-MIL-001] and of [QC-LANG-001] passes (criterion 8 [BC-001]: ../../business-case.md [QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md [QC-LANG-001]: ../../../framework/qc/qc-language-domain.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/sqa/reviews/rc-004-milestone-mil-002.md b/docs/sqa/reviews/rc-004-milestone-mil-002.md index 7407437..5ca9e88 100644 --- a/docs/sqa/reviews/rc-004-milestone-mil-002.md +++ b/docs/sqa/reviews/rc-004-milestone-mil-002.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm) | [b823d1f] | --- @@ -67,3 +67,4 @@ Go — every criterion of [QC-MIL-001] and of [QC-LANG-001] passes (criterion 8 [BC-001]: ../../business-case.md [QC-MIL-001]: ../../../framework/qc/qc-milestones-gateways.md [QC-LANG-001]: ../../../framework/qc/qc-language-domain.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 diff --git a/docs/sqa/reviews/rc-005-code-mil-001.md b/docs/sqa/reviews/rc-005-code-mil-001.md index 5629c68..5d6bd83 100644 --- a/docs/sqa/reviews/rc-005-code-mil-001.md +++ b/docs/sqa/reviews/rc-005-code-mil-001.md @@ -9,7 +9,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Verdict `Go` confirmed by S01 | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version (draft prepared by the assistant for S01 to confirm)
Verdict `Go` confirmed by S01 | [b7119ef] | --- @@ -71,3 +71,4 @@ open one. [MIL-001]: ../../milestones/mil-001-palette.md [BC-001]: ../../business-case.md [QC-PY-001]: ../../../framework/qc/qc-programming-python.md +[b7119ef]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b7119ef907b1ae56d7aa231a460aeab8fa2f2e11 diff --git a/docs/stakeholder-analysis.md b/docs/stakeholder-analysis.md index 103f01a..3d5a711 100644 --- a/docs/stakeholder-analysis.md +++ b/docs/stakeholder-analysis.md @@ -11,7 +11,7 @@ ## Version History | Date | Status | Author | Reviewer | Change | Commit | | --- | --- | --- | --- | --- | --- | -| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | pending | +| 2026-10-07 | Accepted | Jens Tirsvad Nielsen | S01 | Initial version | [b823d1f] | --- @@ -92,3 +92,4 @@ The assistant does not mark the document `Accepted`. --- [BC-001]: ./business-case.md +[b823d1f]: https://git.tirsystem.com/Tirsvad-Udemy-100-days-of-code/018-hirst-painting/commit/b823d1f405ebac6b0198605edd9d802518529725 -- 2.54.0