feat: add initial WOMM Certification Toolkit implementation
Introduces the Works On My Machine certification framework, including: - CLI tool for project certification and badge/certificate generation - Certification checks per WOMM-STD-001:2026 standard - Supporting documents, assets, and tests - Python package setup with pyproject.toml
This commit is contained in:
commit
1002b16b81
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
.idea/
|
||||||
|
poetry.lock
|
||||||
64
README.md
Normal file
64
README.md
Normal file
@ -0,0 +1,64 @@
|
|||||||
|
# WOMM Certification Toolkit
|
||||||
|
|
||||||
|
**Works On My Machine -- The Official Certification Standard**
|
||||||
|
|
||||||
|
A formal certification framework for the age-old claim "it works on my machine," complete with a bureaucratic standards document, an official seal of approval, and a CLI tool to certify your projects.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
For PNG badge export support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e '.[png]'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Certify a project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm certify # certify current directory
|
||||||
|
womm certify /path/to/project
|
||||||
|
womm certify --level gold # target a specific level
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate a badge
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm badge # default: gold, womm-seal.svg
|
||||||
|
womm badge --level platinum -o seal.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate a certificate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm certificate # print to stdout
|
||||||
|
womm certificate -o cert.txt # save to file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read the standard
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm standard
|
||||||
|
```
|
||||||
|
|
||||||
|
## Certification Levels
|
||||||
|
|
||||||
|
| Level | Requirements |
|
||||||
|
|----------|-------------|
|
||||||
|
| Bronze | Project exists, has source files, no syntax errors |
|
||||||
|
| Silver | Bronze + tests exist and pass |
|
||||||
|
| Gold | Silver + README exists, no TODO/FIXME/HACK comments |
|
||||||
|
| Platinum | Gold + CI config present, git tree is clean |
|
||||||
|
|
||||||
|
## The Standard
|
||||||
|
|
||||||
|
See [WOMM-STD-001:2026](WOMM-STANDARD-001.md) for the full certification standard.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
99
WOMM-CERTIFICATE.txt
Normal file
99
WOMM-CERTIFICATE.txt
Normal file
@ -0,0 +1,99 @@
|
|||||||
|
================================================================
|
||||||
|
WOMM CERTIFICATE OF COMPLIANCE
|
||||||
|
WOMM-STD-001:2026
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Certificate Number: WOMM-20260402162108-3551
|
||||||
|
Date of Issuance: 2026-04-02 16:21:08
|
||||||
|
Standard: WOMM-STD-001:2026, First Edition
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
PROJECT INFORMATION
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Project Name: womm-certification
|
||||||
|
Project Path: /Users/gregory.gauthier/Projects/local/womm-certification
|
||||||
|
Certification Level: BRONZE (It Compiles)
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
CERTIFICATION AUTHORITY
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Certifying Machine: PD2328.local
|
||||||
|
Certifying User: gregory.gauthier
|
||||||
|
Operating System: Darwin 24.6.0
|
||||||
|
Python Version: 3.9.6
|
||||||
|
|
||||||
|
This certification was performed on the above machine and is
|
||||||
|
valid exclusively for this machine in its current configuration,
|
||||||
|
including all running processes, environment variables, and the
|
||||||
|
certifier's current emotional state.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
AUDIT TRAIL
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
| Check | Result | Detail |
|
||||||
|
|------------------------------------------|--------|--------|
|
||||||
|
| Project directory exists | PASS | Project directory located. We're off to a strong start. |
|
||||||
|
| Contains source code | PASS | Source files detected. This is, in fact, a software project. |
|
||||||
|
| Python syntax check | PASS | All Python files parse cleanly. The AST smiles upon you. |
|
||||||
|
| Test suite exists | PASS | Test suite located. Someone here believes in accountability. |
|
||||||
|
| Tests pass | FAIL | Could not find a test runner. Neither pytest nor unittest obliged. |
|
||||||
|
| README exists | PASS | README found. Documentation: the thought that counts. |
|
||||||
|
| No TODO/FIXME/HACK comments | FAIL | Found markers in: test_certify.py:102, certify.py:215, certify.py:233. The guilt is documented. |
|
||||||
|
| CI configuration exists | FAIL | No CI configuration found. You are the CI. |
|
||||||
|
| Git working tree is clean | FAIL | Uncommitted changes detected. Living dangerously. |
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
DECLARATION
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
I, Greg Gauthier, do hereby certify that the software
|
||||||
|
project "womm-certification" has been evaluated in accordance
|
||||||
|
with WOMM-STD-001:2026 and has achieved:
|
||||||
|
|
||||||
|
*** WOMM BRONZE CERTIFICATION ***
|
||||||
|
|
||||||
|
This certification is granted under the following conditions:
|
||||||
|
|
||||||
|
1. The software was observed to work on my machine.
|
||||||
|
2. No guarantee is made regarding any other machine.
|
||||||
|
3. This certificate is void if anyone asks "are you sure?"
|
||||||
|
4. Validity expires upon the next git pull, dependency update,
|
||||||
|
or passage of more than 24 hours.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
SIGNATURES
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Certified by:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
Greg Gauthier
|
||||||
|
My Machine
|
||||||
|
2026-04-02
|
||||||
|
|
||||||
|
Witnessed by:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
/dev/null
|
||||||
|
(The Void)
|
||||||
|
|
||||||
|
|
||||||
|
Approved by the WOMM Standards Committee:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
The Committee
|
||||||
|
(Quorum: 1)
|
||||||
|
|
||||||
|
|
||||||
|
================================================================
|
||||||
|
This document was generated in compliance with WOMM-STD-001:2026
|
||||||
|
"Works On My Machine" Certification Standard
|
||||||
|
First Edition, 2026-04-02
|
||||||
|
|
||||||
|
Copyright 2026 The WOMM Standards Committee.
|
||||||
|
All rights reserved, except the right to guarantee
|
||||||
|
it works on your machine.
|
||||||
|
================================================================
|
||||||
482
WOMM-STANDARD-001.md
Normal file
482
WOMM-STANDARD-001.md
Normal file
@ -0,0 +1,482 @@
|
|||||||
|
# WOMM-STD-001:2026
|
||||||
|
|
||||||
|
## Works On My Machine — Certification Standard
|
||||||
|
|
||||||
|
**First Edition — 2026-04-02**
|
||||||
|
|
||||||
|
Published by the **International Bureau of Local Development Assurance (IBLDA)**
|
||||||
|
on behalf of the **WOMM Standards Committee (WSC)**
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
> *"It works on my machine."*
|
||||||
|
> — Every developer, at some point
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Document Classification
|
||||||
|
|
||||||
|
| Field | Value |
|
||||||
|
|----------------------|--------------------------------------------|
|
||||||
|
| Standard Number | WOMM-STD-001:2026 |
|
||||||
|
| Edition | First |
|
||||||
|
| Status | **ACTIVE** |
|
||||||
|
| Classification | Public — Developer Eyes Only |
|
||||||
|
| Supersedes | Verbal assurances, Slack messages, shrugs |
|
||||||
|
| Committee | WOMM Standards Committee (WSC/TC-1) |
|
||||||
|
| Secretariat | IBLDA, Geneva (or wherever the laptop is) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Foreword
|
||||||
|
|
||||||
|
The WOMM Standards Committee (WSC) was convened in response to an
|
||||||
|
industry-wide crisis of confidence in the phrase "it works on my
|
||||||
|
machine." For decades, this declaration has served as the final word
|
||||||
|
in software dispute resolution, yet it has lacked the formal rigour
|
||||||
|
demanded by modern engineering practice.
|
||||||
|
|
||||||
|
This standard provides a comprehensive framework for evaluating,
|
||||||
|
certifying, and communicating the operational status of software
|
||||||
|
within the confines of a single developer's workstation. It is the
|
||||||
|
product of extensive deliberation by experts from such distinguished
|
||||||
|
fields as "closing the laptop and hoping for the best" and "I swear
|
||||||
|
I didn't change anything."
|
||||||
|
|
||||||
|
The WSC acknowledges that this standard cannot guarantee software
|
||||||
|
will work on any machine other than the one on which certification
|
||||||
|
was performed. This is, in fact, the entire point.
|
||||||
|
|
||||||
|
Participation in this certification programme is voluntary. However,
|
||||||
|
developers who do not participate will be asked "but did you even
|
||||||
|
try?" at their next standup.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 1. Scope
|
||||||
|
|
||||||
|
### 1.1 Purpose
|
||||||
|
|
||||||
|
This standard establishes the requirements for certifying that a
|
||||||
|
software project **works on the certifier's machine** at the time
|
||||||
|
of certification. It provides:
|
||||||
|
|
||||||
|
a) A common vocabulary for describing the state of "working"
|
||||||
|
|
||||||
|
b) A tiered certification framework with escalating levels of
|
||||||
|
assurance
|
||||||
|
|
||||||
|
c) Procedures for auditing and verifying compliance
|
||||||
|
|
||||||
|
d) A formal mechanism for responding to the question "well, does
|
||||||
|
it work?"
|
||||||
|
|
||||||
|
### 1.2 Applicability
|
||||||
|
|
||||||
|
This standard applies to any software project, repository, script,
|
||||||
|
or "quick hack I threw together" that resides on a local filesystem
|
||||||
|
and is subject to the claim "it works on my machine."
|
||||||
|
|
||||||
|
### 1.3 Limitations
|
||||||
|
|
||||||
|
This standard explicitly does **not** certify that the software:
|
||||||
|
|
||||||
|
- Works on anyone else's machine
|
||||||
|
- Will continue to work after the next `git pull`
|
||||||
|
- Works when the Wi-Fi is off (unless the project does not require
|
||||||
|
Wi-Fi, in which case, congratulations)
|
||||||
|
- Works in production
|
||||||
|
- Works
|
||||||
|
|
||||||
|
The certification is valid only for the exact hardware, software,
|
||||||
|
environment variables, running background processes, phase of the
|
||||||
|
moon, and emotional state of the developer present at the time of
|
||||||
|
certification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 2. Normative References
|
||||||
|
|
||||||
|
The following documents are indispensable for the application of
|
||||||
|
this standard. For dated references, only the edition cited applies.
|
||||||
|
For undated references, good luck.
|
||||||
|
|
||||||
|
| Reference | Title |
|
||||||
|
|----------------------|----------------------------------------------------|
|
||||||
|
| WOMM-STD-000:2025 | Definition of "It" in Software Contexts |
|
||||||
|
| WOMM-STD-002:2026 | Requirements for the Phrase "I Didn't Touch That" |
|
||||||
|
| WOMM-STD-003:2026 | Guidelines for Reproducible Shrugging |
|
||||||
|
| IEEE 404 | Standard Not Found |
|
||||||
|
| RFC 2324 | Hyper Text Coffee Pot Control Protocol (HTCPCP/1.0)|
|
||||||
|
| ISO 9001 | Quality management systems (cited for comedic contrast) |
|
||||||
|
| xkcd 1172 | Workflow (normative) |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 3. Terms and Definitions
|
||||||
|
|
||||||
|
For the purposes of this standard, the following terms and
|
||||||
|
definitions apply.
|
||||||
|
|
||||||
|
### 3.1 works
|
||||||
|
|
||||||
|
The software executes without producing an error message that the
|
||||||
|
developer cannot explain. Note: errors that the developer *can*
|
||||||
|
explain (e.g., "oh that's fine, it always does that") do not
|
||||||
|
constitute failure.
|
||||||
|
|
||||||
|
### 3.2 my machine
|
||||||
|
|
||||||
|
The specific computer, virtual machine, container, or cardboard
|
||||||
|
box with blinking lights on which the developer performs their
|
||||||
|
work. "My machine" is implicitly scoped to the exact configuration
|
||||||
|
present at certification time, including but not limited to:
|
||||||
|
|
||||||
|
- Operating system and version
|
||||||
|
- Installed packages and their versions
|
||||||
|
- Environment variables (exported and forgotten)
|
||||||
|
- Browser tabs currently open
|
||||||
|
- Number of Docker containers running
|
||||||
|
- Whether Spotify is playing lo-fi beats
|
||||||
|
|
||||||
|
### 3.3 it
|
||||||
|
|
||||||
|
The software under certification. The antecedent of "it" must be
|
||||||
|
identifiable from context, project directory, or vigorous pointing
|
||||||
|
at a screen. See WOMM-STD-000.
|
||||||
|
|
||||||
|
### 3.4 compiles
|
||||||
|
|
||||||
|
The software can be transformed from source code into an executable
|
||||||
|
or interpretable form without the build tool exiting with a non-zero
|
||||||
|
status code. Warnings are not errors. Warnings have never been
|
||||||
|
errors. We do not speak of `-Werror`.
|
||||||
|
|
||||||
|
### 3.5 tests pass
|
||||||
|
|
||||||
|
All automated tests in the project's test suite complete with a
|
||||||
|
passing status. Tests that are skipped, marked as expected failures,
|
||||||
|
or commented out with `# TODO: fix this later` are considered to
|
||||||
|
have passed in the spiritual sense.
|
||||||
|
|
||||||
|
### 3.6 but did you pull main?
|
||||||
|
|
||||||
|
A ritual question posed during certification disputes. The correct
|
||||||
|
response is always "yes" regardless of whether one has, in fact,
|
||||||
|
pulled main.
|
||||||
|
|
||||||
|
### 3.7 the vibes
|
||||||
|
|
||||||
|
An intangible but critical quality metric. Software that works but
|
||||||
|
feels wrong has poor vibes and may be subject to additional scrutiny
|
||||||
|
under Section 6.
|
||||||
|
|
||||||
|
### 3.8 nondeterministic
|
||||||
|
|
||||||
|
A word used to describe test failures that only happen in CI. See
|
||||||
|
also: "cosmic rays", "timing issue", "works on my machine."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 4. Certification Levels
|
||||||
|
|
||||||
|
### 4.1 General
|
||||||
|
|
||||||
|
WOMM certification is awarded at one of four levels, each
|
||||||
|
representing an increasing degree of assurance that the software
|
||||||
|
works on the certifier's machine.
|
||||||
|
|
||||||
|
### 4.2 Level Definitions
|
||||||
|
|
||||||
|
#### 4.2.1 WOMM Bronze — "It Compiles"
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
a) The project directory exists and contains at least one source
|
||||||
|
file recognisable as code
|
||||||
|
|
||||||
|
b) The source code can be parsed without syntax errors
|
||||||
|
|
||||||
|
c) The developer can point to the project and say "that's the
|
||||||
|
one" with reasonable confidence
|
||||||
|
|
||||||
|
Bronze certification represents the minimum viable claim that
|
||||||
|
software exists and is not, in fact, just a README.
|
||||||
|
|
||||||
|
#### 4.2.2 WOMM Silver — "Tests Pass"
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
a) All requirements of WOMM Bronze
|
||||||
|
|
||||||
|
b) A test suite exists within the project
|
||||||
|
|
||||||
|
c) The test suite executes and all tests pass (see definition 3.5)
|
||||||
|
|
||||||
|
d) The developer did not achieve this by deleting the failing tests
|
||||||
|
|
||||||
|
Silver certification indicates that the software does what someone,
|
||||||
|
at some point, thought it should do.
|
||||||
|
|
||||||
|
#### 4.2.3 WOMM Gold — "Production Ready*"
|
||||||
|
|
||||||
|
\*on my machine
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
a) All requirements of WOMM Silver
|
||||||
|
|
||||||
|
b) A README file exists and contains at least one true statement
|
||||||
|
about the project
|
||||||
|
|
||||||
|
c) No `TODO`, `FIXME`, or `HACK` comments remain in the codebase
|
||||||
|
(or they have been reclassified as "known architectural decisions")
|
||||||
|
|
||||||
|
d) The developer has mass-suppressed that vague feeling that
|
||||||
|
something isn't quite right
|
||||||
|
|
||||||
|
Gold certification indicates that the software meets a standard of
|
||||||
|
quality that the developer would be comfortable showing to a
|
||||||
|
colleague, provided the colleague does not look too closely.
|
||||||
|
|
||||||
|
#### 4.2.4 WOMM Platinum — "Works Everywhere*"
|
||||||
|
|
||||||
|
\*that I have tested, which is here
|
||||||
|
|
||||||
|
Requirements:
|
||||||
|
|
||||||
|
a) All requirements of WOMM Gold
|
||||||
|
|
||||||
|
b) A continuous integration configuration file is present in the
|
||||||
|
repository (e.g., `.github/workflows/`, `Jenkinsfile`,
|
||||||
|
`.gitlab-ci.yml`, `bitbucket-pipelines.yml`)
|
||||||
|
|
||||||
|
c) The git working tree is clean (no uncommitted changes)
|
||||||
|
|
||||||
|
d) The developer has mass-suppressed the even stronger vague feeling
|
||||||
|
that something isn't quite right
|
||||||
|
|
||||||
|
Platinum certification represents the highest level of assurance
|
||||||
|
available under this standard. It indicates that the developer has
|
||||||
|
not only verified local functionality but has at least *gestured*
|
||||||
|
toward the concept of reproducibility.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 5. Certification Procedure
|
||||||
|
|
||||||
|
### 5.1 Pre-Certification
|
||||||
|
|
||||||
|
Before initiating the certification process, the certifier SHALL:
|
||||||
|
|
||||||
|
a) Close all unrelated terminal windows to reduce anxiety
|
||||||
|
|
||||||
|
b) Ensure the machine is in a "known good state" (defined as: the
|
||||||
|
state it's in right now)
|
||||||
|
|
||||||
|
c) Optionally mutter "okay, let's see" under their breath
|
||||||
|
|
||||||
|
### 5.2 Certification Execution
|
||||||
|
|
||||||
|
Certification SHALL be performed by executing the WOMM CLI tool
|
||||||
|
(`womm certify`) in the root directory of the project under test.
|
||||||
|
The tool will automatically evaluate all applicable requirements
|
||||||
|
and assign the highest achievable certification level.
|
||||||
|
|
||||||
|
Manual certification (i.e., just saying "it works on my machine"
|
||||||
|
without running the tool) is deprecated as of this edition but
|
||||||
|
remains in widespread use.
|
||||||
|
|
||||||
|
### 5.3 Post-Certification
|
||||||
|
|
||||||
|
Upon successful certification, the certifier SHALL:
|
||||||
|
|
||||||
|
a) Generate a certificate of compliance using `womm certificate`
|
||||||
|
|
||||||
|
b) Optionally affix the WOMM seal to the project's README
|
||||||
|
|
||||||
|
c) Lean back in their chair with quiet satisfaction
|
||||||
|
|
||||||
|
d) Not touch anything else
|
||||||
|
|
||||||
|
### 5.4 Recertification
|
||||||
|
|
||||||
|
Certification is valid until:
|
||||||
|
|
||||||
|
- Any file in the project is modified
|
||||||
|
- Any dependency is updated
|
||||||
|
- The developer runs `git pull`
|
||||||
|
- The machine is restarted
|
||||||
|
- More than 24 hours have elapsed
|
||||||
|
- Someone asks "are you sure?"
|
||||||
|
|
||||||
|
Recertification follows the same procedure as initial certification.
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 6. Audit Procedures
|
||||||
|
|
||||||
|
### 6.1 General
|
||||||
|
|
||||||
|
An audit may be requested by any stakeholder who has reason to
|
||||||
|
believe that the claim "it works on my machine" is unsubstantiated.
|
||||||
|
Common triggers include:
|
||||||
|
|
||||||
|
- The software does not work on the auditor's machine
|
||||||
|
- The CI pipeline is red
|
||||||
|
- A customer has reported an issue
|
||||||
|
- It's Monday
|
||||||
|
|
||||||
|
### 6.2 Audit Process
|
||||||
|
|
||||||
|
The audit SHALL proceed as follows:
|
||||||
|
|
||||||
|
a) The auditor requests the certifier demonstrate the software
|
||||||
|
working on their machine
|
||||||
|
|
||||||
|
b) The certifier navigates to the project directory, ideally while
|
||||||
|
saying "it was working five minutes ago"
|
||||||
|
|
||||||
|
c) The `womm certify` command is executed
|
||||||
|
|
||||||
|
d) Results are compared against the claimed certification level
|
||||||
|
|
||||||
|
e) If certification fails, proceed to Section 7
|
||||||
|
|
||||||
|
### 6.3 Environmental Considerations
|
||||||
|
|
||||||
|
The auditor must acknowledge that the software's behaviour may be
|
||||||
|
influenced by factors beyond the developer's control, including but
|
||||||
|
not limited to:
|
||||||
|
|
||||||
|
- The auditor's presence (observer effect)
|
||||||
|
- Solar flares
|
||||||
|
- npm
|
||||||
|
- The fact that Mercury is in retrograde
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 7. Non-Conformance and Appeals
|
||||||
|
|
||||||
|
### 7.1 Non-Conformance
|
||||||
|
|
||||||
|
A non-conformance occurs when the software fails to meet the
|
||||||
|
requirements of its claimed certification level during an audit.
|
||||||
|
|
||||||
|
### 7.2 Immediate Remediation Steps
|
||||||
|
|
||||||
|
Upon discovery of non-conformance, the following steps SHALL be
|
||||||
|
attempted in order:
|
||||||
|
|
||||||
|
1. Run the command again
|
||||||
|
2. Run the command again, but slower
|
||||||
|
3. Clear all caches (local, browser, DNS, emotional)
|
||||||
|
4. Run `git status` and stare at the output pensively
|
||||||
|
5. Check if the correct branch is checked out
|
||||||
|
6. Ask "did someone push something?"
|
||||||
|
7. Restart the machine
|
||||||
|
8. Restart the machine again, but with feeling
|
||||||
|
|
||||||
|
### 7.3 Appeals
|
||||||
|
|
||||||
|
If remediation is unsuccessful, the certifier may appeal by filing
|
||||||
|
a Formal Declaration of Bewilderment (Form WOMM-7B), which must
|
||||||
|
include:
|
||||||
|
|
||||||
|
- Timestamp of last known working state
|
||||||
|
- A sworn statement that "I literally didn't change anything"
|
||||||
|
- Screenshot evidence (optional but strongly recommended)
|
||||||
|
- Stack Overflow link that seems relevant but isn't
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## 8. Marking and Labelling
|
||||||
|
|
||||||
|
### 8.1 WOMM Seal
|
||||||
|
|
||||||
|
Projects that have achieved WOMM certification MAY display the
|
||||||
|
official WOMM Seal of Approval in their documentation, README, or
|
||||||
|
office wall.
|
||||||
|
|
||||||
|
The seal SHALL include:
|
||||||
|
|
||||||
|
- The text "WOMM CERTIFIED"
|
||||||
|
- The certification level (Bronze, Silver, Gold, or Platinum)
|
||||||
|
- The date of certification
|
||||||
|
- The text "Works On My Machine" or an approved abbreviation
|
||||||
|
|
||||||
|
### 8.2 Usage Restrictions
|
||||||
|
|
||||||
|
The WOMM Seal SHALL NOT be used to imply that the software works
|
||||||
|
on any machine other than the certifier's. Misuse of the seal
|
||||||
|
constitutes a violation of WOMM-STD-001 and may result in the
|
||||||
|
offender being asked to "fix it in production, then."
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix A (Informative): Common Excuses and Their Classification
|
||||||
|
|
||||||
|
| Excuse | Classification | Accepted? |
|
||||||
|
|--------|---------------|-----------|
|
||||||
|
| "It works on my machine" | Standard claim | Yes (with certification) |
|
||||||
|
| "I didn't change anything" | Denial | Under investigation |
|
||||||
|
| "It must be a caching issue" | Deflection | Provisionally accepted |
|
||||||
|
| "That test is flaky" | Technical excuse | Accepted if test is, in fact, flaky |
|
||||||
|
| "Works in debug mode" | Partial conformance | Bronze only |
|
||||||
|
| "The API must be down" | External attribution | Requires evidence |
|
||||||
|
| "Have you tried clearing your node_modules?" | Counter-audit | Procedurally valid |
|
||||||
|
| "It's a known issue" | Acknowledgement | Accepted if issue is, in fact, known |
|
||||||
|
| "That's not a bug, it's a feature" | Reclassification | Requires product owner sign-off |
|
||||||
|
| "I'll fix it tomorrow" | Deferral | Not accepted for certification |
|
||||||
|
| "It worked yesterday" | Temporal defence | Expired |
|
||||||
|
| "It works if you do it in the right order" | User-error claim | Gold and above only |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix B (Normative): Official WOMM Seal Usage Guidelines
|
||||||
|
|
||||||
|
### B.1 Minimum Seal Size
|
||||||
|
|
||||||
|
The WOMM Seal SHALL be displayed at a minimum size of 64x64 pixels
|
||||||
|
in digital media, or 20mm diameter in print. Smaller sizes risk the
|
||||||
|
seal being mistaken for a loading spinner.
|
||||||
|
|
||||||
|
### B.2 Colour Requirements
|
||||||
|
|
||||||
|
The seal SHALL be displayed in the official WOMM colour palette:
|
||||||
|
|
||||||
|
| Level | Primary Colour | Hex |
|
||||||
|
|----------|---------------|---------|
|
||||||
|
| Bronze | Copper | #B87333 |
|
||||||
|
| Silver | Silver | #C0C0C0 |
|
||||||
|
| Gold | Gold | #FFD700 |
|
||||||
|
| Platinum | Platinum | #E5E4E2 |
|
||||||
|
|
||||||
|
### B.3 Prohibited Modifications
|
||||||
|
|
||||||
|
The following modifications to the seal are prohibited:
|
||||||
|
|
||||||
|
- Adding the word "probably" before "certified"
|
||||||
|
- Replacing the checkmark with a question mark
|
||||||
|
- Displaying the seal upside-down (this indicates WOMM Decertification)
|
||||||
|
- Using the seal as a loading animation
|
||||||
|
- Tattooing the seal on any body part (this is not prohibited per se,
|
||||||
|
but the Committee questions your judgement)
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
## Appendix C (Informative): Revision History
|
||||||
|
|
||||||
|
| Version | Date | Description |
|
||||||
|
|---------|------------|-------------------------------------------------|
|
||||||
|
| 0.1 | 2025-01-15 | Initial draft, written on a napkin |
|
||||||
|
| 0.2 | 2025-06-01 | Added certification levels after heated debate |
|
||||||
|
| 0.9 | 2025-11-30 | Committee reached quorum (2 people) |
|
||||||
|
| 1.0 | 2026-04-02 | First edition published. It works on my machine. |
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
*Copyright 2026 The WOMM Standards Committee. All rights reserved,
|
||||||
|
except the right to guarantee it works on your machine.*
|
||||||
|
|
||||||
|
*END OF WOMM-STD-001:2026*
|
||||||
78
assets/womm-seal.svg
Normal file
78
assets/womm-seal.svg
Normal file
@ -0,0 +1,78 @@
|
|||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" width="400" height="400">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
@import url('https://fonts.googleapis.com/css2?family=EB+Garamond:wght@700&display=swap');
|
||||||
|
.seal-text { font-family: 'EB Garamond', 'Georgia', serif; font-weight: 700; }
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Outer ring -->
|
||||||
|
<circle cx="200" cy="200" r="190" fill="none" stroke="#1a1a2e" stroke-width="6"/>
|
||||||
|
<circle cx="200" cy="200" r="180" fill="none" stroke="#1a1a2e" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Decorative dots on outer ring -->
|
||||||
|
<g fill="#1a1a2e">
|
||||||
|
<circle cx="200" cy="14" r="4"/>
|
||||||
|
<circle cx="200" cy="386" r="4"/>
|
||||||
|
<circle cx="14" cy="200" r="4"/>
|
||||||
|
<circle cx="386" cy="200" r="4"/>
|
||||||
|
<!-- Diagonal dots -->
|
||||||
|
<circle cx="68" cy="68" r="3"/>
|
||||||
|
<circle cx="332" cy="68" r="3"/>
|
||||||
|
<circle cx="68" cy="332" r="3"/>
|
||||||
|
<circle cx="332" cy="332" r="3"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Inner ring -->
|
||||||
|
<circle cx="200" cy="200" r="150" fill="none" stroke="#1a1a2e" stroke-width="2"/>
|
||||||
|
<circle cx="200" cy="200" r="140" fill="none" stroke="#1a1a2e" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Star decorations between rings -->
|
||||||
|
<g fill="#1a1a2e" class="seal-text" text-anchor="middle" font-size="16">
|
||||||
|
<text x="200" y="42">★</text>
|
||||||
|
<text x="200" y="370">★</text>
|
||||||
|
<text x="38" y="206">★</text>
|
||||||
|
<text x="362" y="206">★</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Circular text - top arc: "WORKS ON MY MACHINE" -->
|
||||||
|
<path id="topArc" d="M 80,200 a 120,120 0 0,1 240,0" fill="none"/>
|
||||||
|
<text class="seal-text" font-size="18" fill="#1a1a2e" letter-spacing="3">
|
||||||
|
<textPath href="#topArc" startOffset="50%" text-anchor="middle">WORKS ON MY MACHINE</textPath>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Circular text - bottom arc: "CERTIFIED" -->
|
||||||
|
<path id="bottomArc" d="M 320,200 a 120,120 0 0,1 -240,0" fill="none"/>
|
||||||
|
<text class="seal-text" font-size="18" fill="#1a1a2e" letter-spacing="5">
|
||||||
|
<textPath href="#bottomArc" startOffset="50%" text-anchor="middle">CERTIFIED</textPath>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Central content area -->
|
||||||
|
<!-- Laptop icon -->
|
||||||
|
<g transform="translate(200,170)" fill="none" stroke="#1a1a2e" stroke-width="3" stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<!-- Screen -->
|
||||||
|
<rect x="-35" y="-30" width="70" height="48" rx="4"/>
|
||||||
|
<!-- Checkmark on screen -->
|
||||||
|
<polyline points="-12,0 -2,10 16,-10" stroke-width="4" stroke="#2d6a4f"/>
|
||||||
|
<!-- Base -->
|
||||||
|
<path d="M-45,18 L-50,28 L50,28 L45,18"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- WOMM text -->
|
||||||
|
<text x="200" y="230" class="seal-text" font-size="42" fill="#1a1a2e" text-anchor="middle" letter-spacing="6">WOMM</text>
|
||||||
|
|
||||||
|
<!-- Divider lines -->
|
||||||
|
<line x1="130" y1="242" x2="270" y2="242" stroke="#1a1a2e" stroke-width="1.5"/>
|
||||||
|
|
||||||
|
<!-- Seal of Approval text -->
|
||||||
|
<text x="200" y="262" class="seal-text" font-size="13" fill="#1a1a2e" text-anchor="middle" letter-spacing="2">SEAL OF APPROVAL</text>
|
||||||
|
|
||||||
|
<!-- Year -->
|
||||||
|
<text x="200" y="285" class="seal-text" font-size="14" fill="#1a1a2e" text-anchor="middle" letter-spacing="1">EST. 2026</text>
|
||||||
|
|
||||||
|
<!-- Small decorative elements -->
|
||||||
|
<g fill="#1a1a2e">
|
||||||
|
<circle cx="155" cy="285" r="2"/>
|
||||||
|
<circle cx="245" cy="285" r="2"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 3.1 KiB |
28
pyproject.toml
Normal file
28
pyproject.toml
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
[build-system]
|
||||||
|
requires = ["setuptools>=68.0", "setuptools-scm>=8.0"]
|
||||||
|
build-backend = "setuptools.build_meta"
|
||||||
|
|
||||||
|
[project]
|
||||||
|
name = "womm-certification"
|
||||||
|
version = "1.0.0"
|
||||||
|
description = "Works On My Machine — The Official Certification Toolkit"
|
||||||
|
readme = "README.md"
|
||||||
|
requires-python = ">=3.9"
|
||||||
|
license = "MIT"
|
||||||
|
authors = [
|
||||||
|
{ name = "The WOMM Standards Committee" },
|
||||||
|
]
|
||||||
|
dependencies = [
|
||||||
|
"click>=8.0",
|
||||||
|
"rich>=13.0",
|
||||||
|
]
|
||||||
|
|
||||||
|
[project.optional-dependencies]
|
||||||
|
png = ["cairosvg>=2.7"]
|
||||||
|
dev = ["pytest>=8.0"]
|
||||||
|
|
||||||
|
[project.scripts]
|
||||||
|
womm = "womm.cli:main"
|
||||||
|
|
||||||
|
[tool.setuptools.packages.find]
|
||||||
|
where = ["src"]
|
||||||
3
src/womm/__init__.py
Normal file
3
src/womm/__init__.py
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
"""WOMM — Works On My Machine Certification Toolkit."""
|
||||||
|
|
||||||
|
__version__ = "1.0.0"
|
||||||
159
src/womm/badge.py
Normal file
159
src/womm/badge.py
Normal file
@ -0,0 +1,159 @@
|
|||||||
|
"""Generate WOMM Seal of Approval badges as SVG."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from datetime import date
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from womm.certify import Level
|
||||||
|
|
||||||
|
_SVG_TEMPLATE = """\
|
||||||
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 400" width="{size}" height="{size}">
|
||||||
|
<defs>
|
||||||
|
<style>
|
||||||
|
.seal-text {{ font-family: 'Georgia', 'EB Garamond', serif; font-weight: 700; }}
|
||||||
|
</style>
|
||||||
|
</defs>
|
||||||
|
|
||||||
|
<!-- Outer ring -->
|
||||||
|
<circle cx="200" cy="200" r="190" fill="none" stroke="{colour}" stroke-width="6"/>
|
||||||
|
<circle cx="200" cy="200" r="180" fill="none" stroke="{colour}" stroke-width="2"/>
|
||||||
|
|
||||||
|
<!-- Decorative dots -->
|
||||||
|
<g fill="{colour}">
|
||||||
|
<circle cx="200" cy="14" r="4"/>
|
||||||
|
<circle cx="200" cy="386" r="4"/>
|
||||||
|
<circle cx="14" cy="200" r="4"/>
|
||||||
|
<circle cx="386" cy="200" r="4"/>
|
||||||
|
<circle cx="68" cy="68" r="3"/>
|
||||||
|
<circle cx="332" cy="68" r="3"/>
|
||||||
|
<circle cx="68" cy="332" r="3"/>
|
||||||
|
<circle cx="332" cy="332" r="3"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Inner ring -->
|
||||||
|
<circle cx="200" cy="200" r="150" fill="none" stroke="{colour}" stroke-width="2"/>
|
||||||
|
<circle cx="200" cy="200" r="140" fill="none" stroke="{colour}" stroke-width="1"/>
|
||||||
|
|
||||||
|
<!-- Stars between rings -->
|
||||||
|
<g fill="{colour}" class="seal-text" text-anchor="middle" font-size="16">
|
||||||
|
<text x="200" y="42">★</text>
|
||||||
|
<text x="200" y="370">★</text>
|
||||||
|
<text x="38" y="206">★</text>
|
||||||
|
<text x="362" y="206">★</text>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- Circular text - top: "WORKS ON MY MACHINE" -->
|
||||||
|
<path id="topArc" d="M 80,200 a 120,120 0 0,1 240,0" fill="none"/>
|
||||||
|
<text class="seal-text" font-size="18" fill="{colour}" letter-spacing="3">
|
||||||
|
<textPath href="#topArc" startOffset="50%" text-anchor="middle">WORKS ON MY MACHINE</textPath>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Circular text - bottom: "CERTIFIED" -->
|
||||||
|
<path id="bottomArc" d="M 320,200 a 120,120 0 0,1 -240,0" fill="none"/>
|
||||||
|
<text class="seal-text" font-size="18" fill="{colour}" letter-spacing="5">
|
||||||
|
<textPath href="#bottomArc" startOffset="50%" text-anchor="middle">CERTIFIED</textPath>
|
||||||
|
</text>
|
||||||
|
|
||||||
|
<!-- Laptop + checkmark icon -->
|
||||||
|
<g transform="translate(200,165)" fill="none" stroke="{colour}" stroke-width="3"
|
||||||
|
stroke-linecap="round" stroke-linejoin="round">
|
||||||
|
<rect x="-35" y="-30" width="70" height="48" rx="4"/>
|
||||||
|
<polyline points="-12,0 -2,10 16,-10" stroke-width="4" stroke="{check_colour}"/>
|
||||||
|
<path d="M-45,18 L-50,28 L50,28 L45,18"/>
|
||||||
|
</g>
|
||||||
|
|
||||||
|
<!-- WOMM -->
|
||||||
|
<text x="200" y="228" class="seal-text" font-size="42" fill="{colour}"
|
||||||
|
text-anchor="middle" letter-spacing="6">WOMM</text>
|
||||||
|
|
||||||
|
<!-- Divider -->
|
||||||
|
<line x1="120" y1="240" x2="280" y2="240" stroke="{colour}" stroke-width="1.5"/>
|
||||||
|
|
||||||
|
<!-- Level label -->
|
||||||
|
<text x="200" y="260" class="seal-text" font-size="16" fill="{colour}"
|
||||||
|
text-anchor="middle" letter-spacing="3">{level_label}</text>
|
||||||
|
|
||||||
|
<!-- Date -->
|
||||||
|
<text x="200" y="285" class="seal-text" font-size="13" fill="{colour}"
|
||||||
|
text-anchor="middle" letter-spacing="1">{date_str}</text>
|
||||||
|
|
||||||
|
<!-- Dot decorations -->
|
||||||
|
<g fill="{colour}">
|
||||||
|
<circle cx="148" cy="282" r="2"/>
|
||||||
|
<circle cx="252" cy="282" r="2"/>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
"""
|
||||||
|
|
||||||
|
_CHECK_COLOURS = {
|
||||||
|
Level.NONE: "#888888",
|
||||||
|
Level.BRONZE: "#8B5E3C",
|
||||||
|
Level.SILVER: "#2d6a4f",
|
||||||
|
Level.GOLD: "#2d6a4f",
|
||||||
|
Level.PLATINUM: "#2d6a4f",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def generate_badge_svg(
|
||||||
|
level: Level = Level.GOLD,
|
||||||
|
certification_date: date | None = None,
|
||||||
|
size: int = 400,
|
||||||
|
) -> str:
|
||||||
|
"""Generate an SVG string for a WOMM Seal of Approval.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
level: Certification level to display.
|
||||||
|
certification_date: Date to show on the seal. Defaults to today.
|
||||||
|
size: Width and height in pixels.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
SVG markup as a string.
|
||||||
|
"""
|
||||||
|
cert_date = certification_date or date.today()
|
||||||
|
return _SVG_TEMPLATE.format(
|
||||||
|
size=size,
|
||||||
|
colour=level.colour_hex,
|
||||||
|
check_colour=_CHECK_COLOURS.get(level, "#2d6a4f"),
|
||||||
|
level_label=level.label.upper(),
|
||||||
|
date_str=cert_date.strftime("%Y-%m-%d"),
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def save_badge(
|
||||||
|
output_path: str | Path,
|
||||||
|
level: Level = Level.GOLD,
|
||||||
|
certification_date: date | None = None,
|
||||||
|
size: int = 400,
|
||||||
|
) -> Path:
|
||||||
|
"""Generate and save a WOMM badge.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
output_path: Where to write the file. If it ends in .png and
|
||||||
|
cairosvg is available, a PNG will be generated.
|
||||||
|
level: Certification level.
|
||||||
|
certification_date: Date for the seal.
|
||||||
|
size: Badge dimensions.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The resolved output path.
|
||||||
|
"""
|
||||||
|
out = Path(output_path).resolve()
|
||||||
|
svg_content = generate_badge_svg(level, certification_date, size)
|
||||||
|
|
||||||
|
if out.suffix.lower() == ".png":
|
||||||
|
try:
|
||||||
|
import cairosvg # type: ignore[import-untyped]
|
||||||
|
cairosvg.svg2png(bytestring=svg_content.encode(), write_to=str(out))
|
||||||
|
except ImportError:
|
||||||
|
# Fall back to saving SVG with a note
|
||||||
|
svg_path = out.with_suffix(".svg")
|
||||||
|
svg_path.write_text(svg_content, encoding="utf-8")
|
||||||
|
raise SystemExit(
|
||||||
|
f"cairosvg not installed — saved SVG to {svg_path} instead.\n"
|
||||||
|
"Install with: pip install 'womm-certification[png]'"
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
out.write_text(svg_content, encoding="utf-8")
|
||||||
|
|
||||||
|
return out
|
||||||
335
src/womm/certify.py
Normal file
335
src/womm/certify.py
Normal file
@ -0,0 +1,335 @@
|
|||||||
|
"""WOMM certification checks per WOMM-STD-001:2026."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import ast
|
||||||
|
import os
|
||||||
|
import subprocess
|
||||||
|
from dataclasses import dataclass, field
|
||||||
|
from enum import IntEnum
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
|
||||||
|
class Level(IntEnum):
|
||||||
|
"""WOMM certification levels (Section 4.2)."""
|
||||||
|
|
||||||
|
NONE = 0
|
||||||
|
BRONZE = 1
|
||||||
|
SILVER = 2
|
||||||
|
GOLD = 3
|
||||||
|
PLATINUM = 4
|
||||||
|
|
||||||
|
@property
|
||||||
|
def label(self) -> str:
|
||||||
|
return self.name.capitalize() if self != Level.NONE else "Uncertified"
|
||||||
|
|
||||||
|
@property
|
||||||
|
def colour_hex(self) -> str:
|
||||||
|
return {
|
||||||
|
Level.NONE: "#888888",
|
||||||
|
Level.BRONZE: "#B87333",
|
||||||
|
Level.SILVER: "#C0C0C0",
|
||||||
|
Level.GOLD: "#FFD700",
|
||||||
|
Level.PLATINUM: "#E5E4E2",
|
||||||
|
}[self]
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CheckResult:
|
||||||
|
"""Outcome of a single certification check."""
|
||||||
|
|
||||||
|
name: str
|
||||||
|
passed: bool
|
||||||
|
message: str
|
||||||
|
level: Level
|
||||||
|
|
||||||
|
|
||||||
|
@dataclass
|
||||||
|
class CertificationReport:
|
||||||
|
"""Full certification report for a project."""
|
||||||
|
|
||||||
|
project_path: Path
|
||||||
|
project_name: str
|
||||||
|
results: list[CheckResult] = field(default_factory=list)
|
||||||
|
|
||||||
|
@property
|
||||||
|
def level(self) -> Level:
|
||||||
|
"""Highest level for which all checks passed."""
|
||||||
|
achieved = Level.PLATINUM
|
||||||
|
for result in self.results:
|
||||||
|
if not result.passed and result.level <= achieved:
|
||||||
|
achieved = Level(result.level - 1)
|
||||||
|
return max(achieved, Level.NONE)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Individual checks
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
def _check_project_exists(path: Path) -> CheckResult:
|
||||||
|
exists = path.is_dir()
|
||||||
|
return CheckResult(
|
||||||
|
name="Project directory exists",
|
||||||
|
passed=exists,
|
||||||
|
message=(
|
||||||
|
"Project directory located. We're off to a strong start."
|
||||||
|
if exists
|
||||||
|
else "Project directory not found. This is... concerning."
|
||||||
|
),
|
||||||
|
level=Level.BRONZE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_has_source_files(path: Path) -> CheckResult:
|
||||||
|
extensions = {".py", ".js", ".ts", ".c", ".cpp", ".go", ".rs", ".java", ".rb", ".sh"}
|
||||||
|
found = any(
|
||||||
|
f.suffix in extensions
|
||||||
|
for f in path.rglob("*")
|
||||||
|
if f.is_file() and ".git" not in f.parts
|
||||||
|
)
|
||||||
|
return CheckResult(
|
||||||
|
name="Contains source code",
|
||||||
|
passed=found,
|
||||||
|
message=(
|
||||||
|
"Source files detected. This is, in fact, a software project."
|
||||||
|
if found
|
||||||
|
else "No recognisable source files found. Are you sure this isn't just a README?"
|
||||||
|
),
|
||||||
|
level=Level.BRONZE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_python_syntax(path: Path) -> CheckResult:
|
||||||
|
py_files = [
|
||||||
|
f for f in path.rglob("*.py")
|
||||||
|
if f.is_file() and ".git" not in f.parts
|
||||||
|
]
|
||||||
|
if not py_files:
|
||||||
|
return CheckResult(
|
||||||
|
name="Python syntax check",
|
||||||
|
passed=True,
|
||||||
|
message="No Python files to check. Blissful ignorance achieved.",
|
||||||
|
level=Level.BRONZE,
|
||||||
|
)
|
||||||
|
errors: list[str] = []
|
||||||
|
for py_file in py_files:
|
||||||
|
try:
|
||||||
|
ast.parse(py_file.read_text(encoding="utf-8"), filename=str(py_file))
|
||||||
|
except SyntaxError as exc:
|
||||||
|
errors.append(f"{py_file.name}:{exc.lineno}: {exc.msg}")
|
||||||
|
return CheckResult(
|
||||||
|
name="Python syntax check",
|
||||||
|
passed=len(errors) == 0,
|
||||||
|
message=(
|
||||||
|
"All Python files parse cleanly. The AST smiles upon you."
|
||||||
|
if not errors
|
||||||
|
else f"Syntax errors found ({len(errors)}): {'; '.join(errors[:3])}"
|
||||||
|
),
|
||||||
|
level=Level.BRONZE,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_tests_exist(path: Path) -> CheckResult:
|
||||||
|
test_indicators = [
|
||||||
|
path / "tests",
|
||||||
|
path / "test",
|
||||||
|
*path.rglob("test_*.py"),
|
||||||
|
*path.rglob("*_test.py"),
|
||||||
|
*path.rglob("tests.py"),
|
||||||
|
]
|
||||||
|
found = any(p.exists() for p in test_indicators)
|
||||||
|
return CheckResult(
|
||||||
|
name="Test suite exists",
|
||||||
|
passed=found,
|
||||||
|
message=(
|
||||||
|
"Test suite located. Someone here believes in accountability."
|
||||||
|
if found
|
||||||
|
else "No test suite found. Bold strategy."
|
||||||
|
),
|
||||||
|
level=Level.SILVER,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_tests_pass(path: Path) -> CheckResult:
|
||||||
|
# Try pytest, then unittest
|
||||||
|
for cmd in (["python", "-m", "pytest", "-x", "-q"], ["python", "-m", "unittest", "discover", "-s", "."]):
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
cmd,
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=120,
|
||||||
|
)
|
||||||
|
if result.returncode == 0:
|
||||||
|
return CheckResult(
|
||||||
|
name="Tests pass",
|
||||||
|
passed=True,
|
||||||
|
message="All tests pass. On this machine, at least.",
|
||||||
|
level=Level.SILVER,
|
||||||
|
)
|
||||||
|
elif result.returncode == 5 and "pytest" in cmd[2]:
|
||||||
|
# pytest exit code 5 = no tests collected
|
||||||
|
continue
|
||||||
|
else:
|
||||||
|
snippet = (result.stdout + result.stderr).strip().split("\n")[-1]
|
||||||
|
return CheckResult(
|
||||||
|
name="Tests pass",
|
||||||
|
passed=False,
|
||||||
|
message=f"Tests failed. {snippet}",
|
||||||
|
level=Level.SILVER,
|
||||||
|
)
|
||||||
|
except FileNotFoundError:
|
||||||
|
continue
|
||||||
|
except subprocess.TimeoutExpired:
|
||||||
|
return CheckResult(
|
||||||
|
name="Tests pass",
|
||||||
|
passed=False,
|
||||||
|
message="Tests timed out after 120s. The machine grew weary.",
|
||||||
|
level=Level.SILVER,
|
||||||
|
)
|
||||||
|
return CheckResult(
|
||||||
|
name="Tests pass",
|
||||||
|
passed=False,
|
||||||
|
message="Could not find a test runner. Neither pytest nor unittest obliged.",
|
||||||
|
level=Level.SILVER,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_readme_exists(path: Path) -> CheckResult:
|
||||||
|
readmes = [path / name for name in ("README.md", "README.rst", "README.txt", "README")]
|
||||||
|
found = any(r.is_file() for r in readmes)
|
||||||
|
return CheckResult(
|
||||||
|
name="README exists",
|
||||||
|
passed=found,
|
||||||
|
message=(
|
||||||
|
"README found. Documentation: the thought that counts."
|
||||||
|
if found
|
||||||
|
else "No README. Future you will regret this."
|
||||||
|
),
|
||||||
|
level=Level.GOLD,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_no_todo_comments(path: Path) -> CheckResult:
|
||||||
|
markers = ("TODO", "FIXME", "HACK", "XXX")
|
||||||
|
offenders: list[str] = []
|
||||||
|
for f in path.rglob("*"):
|
||||||
|
if not f.is_file() or ".git" in f.parts or f.suffix not in (
|
||||||
|
".py", ".js", ".ts", ".c", ".cpp", ".go", ".rs", ".java", ".rb",
|
||||||
|
):
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
for i, line in enumerate(f.read_text(encoding="utf-8", errors="ignore").splitlines(), 1):
|
||||||
|
if any(m in line for m in markers):
|
||||||
|
offenders.append(f"{f.name}:{i}")
|
||||||
|
if len(offenders) >= 5:
|
||||||
|
break
|
||||||
|
except (OSError, UnicodeDecodeError):
|
||||||
|
continue
|
||||||
|
if len(offenders) >= 5:
|
||||||
|
break
|
||||||
|
return CheckResult(
|
||||||
|
name="No TODO/FIXME/HACK comments",
|
||||||
|
passed=len(offenders) == 0,
|
||||||
|
message=(
|
||||||
|
"No shameful markers found. Either you're thorough, or you're sneaky."
|
||||||
|
if not offenders
|
||||||
|
else f"Found markers in: {', '.join(offenders)}. The guilt is documented."
|
||||||
|
),
|
||||||
|
level=Level.GOLD,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_ci_config_exists(path: Path) -> CheckResult:
|
||||||
|
ci_paths = [
|
||||||
|
path / ".github" / "workflows",
|
||||||
|
path / ".gitlab-ci.yml",
|
||||||
|
path / "Jenkinsfile",
|
||||||
|
path / ".circleci",
|
||||||
|
path / "bitbucket-pipelines.yml",
|
||||||
|
path / ".travis.yml",
|
||||||
|
path / "azure-pipelines.yml",
|
||||||
|
]
|
||||||
|
found = any(p.exists() for p in ci_paths)
|
||||||
|
return CheckResult(
|
||||||
|
name="CI configuration exists",
|
||||||
|
passed=found,
|
||||||
|
message=(
|
||||||
|
"CI config detected. You've at least gestured toward reproducibility."
|
||||||
|
if found
|
||||||
|
else "No CI configuration found. You are the CI."
|
||||||
|
),
|
||||||
|
level=Level.PLATINUM,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
def _check_git_clean(path: Path) -> CheckResult:
|
||||||
|
try:
|
||||||
|
result = subprocess.run(
|
||||||
|
["git", "status", "--porcelain"],
|
||||||
|
cwd=path,
|
||||||
|
capture_output=True,
|
||||||
|
text=True,
|
||||||
|
timeout=10,
|
||||||
|
)
|
||||||
|
clean = result.returncode == 0 and result.stdout.strip() == ""
|
||||||
|
return CheckResult(
|
||||||
|
name="Git working tree is clean",
|
||||||
|
passed=clean,
|
||||||
|
message=(
|
||||||
|
"Working tree is clean. Pristine. Untouched. (For now.)"
|
||||||
|
if clean
|
||||||
|
else "Uncommitted changes detected. Living dangerously."
|
||||||
|
),
|
||||||
|
level=Level.PLATINUM,
|
||||||
|
)
|
||||||
|
except (FileNotFoundError, subprocess.TimeoutExpired):
|
||||||
|
return CheckResult(
|
||||||
|
name="Git working tree is clean",
|
||||||
|
passed=False,
|
||||||
|
message="Git not available or not a git repository. The void stares back.",
|
||||||
|
level=Level.PLATINUM,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
# Main certification entry point
|
||||||
|
# ---------------------------------------------------------------------------
|
||||||
|
|
||||||
|
ALL_CHECKS = [
|
||||||
|
_check_project_exists,
|
||||||
|
_check_has_source_files,
|
||||||
|
_check_python_syntax,
|
||||||
|
_check_tests_exist,
|
||||||
|
_check_tests_pass,
|
||||||
|
_check_readme_exists,
|
||||||
|
_check_no_todo_comments,
|
||||||
|
_check_ci_config_exists,
|
||||||
|
_check_git_clean,
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def certify(path: str | Path | None = None, target_level: Level | None = None) -> CertificationReport:
|
||||||
|
"""Run WOMM certification checks against a project directory.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
path: Project root directory. Defaults to cwd.
|
||||||
|
target_level: If set, only run checks up to this level.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
A CertificationReport with all check results.
|
||||||
|
"""
|
||||||
|
project_path = Path(path or os.getcwd()).resolve()
|
||||||
|
report = CertificationReport(
|
||||||
|
project_path=project_path,
|
||||||
|
project_name=project_path.name,
|
||||||
|
)
|
||||||
|
|
||||||
|
for check_fn in ALL_CHECKS:
|
||||||
|
result = check_fn(project_path)
|
||||||
|
report.results.append(result)
|
||||||
|
if target_level is not None and result.level > target_level:
|
||||||
|
break
|
||||||
|
|
||||||
|
return report
|
||||||
172
src/womm/cli.py
Normal file
172
src/womm/cli.py
Normal file
@ -0,0 +1,172 @@
|
|||||||
|
"""WOMM CLI — Works On My Machine Certification Toolkit."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import click
|
||||||
|
from rich.console import Console
|
||||||
|
from rich.panel import Panel
|
||||||
|
from rich.table import Table
|
||||||
|
from rich.text import Text
|
||||||
|
|
||||||
|
from womm.certify import Level, certify
|
||||||
|
from womm.badge import save_badge
|
||||||
|
from womm.document import generate_certificate, save_certificate
|
||||||
|
|
||||||
|
console = Console()
|
||||||
|
|
||||||
|
LEVEL_STYLES = {
|
||||||
|
Level.NONE: "dim",
|
||||||
|
Level.BRONZE: "rgb(184,115,51)",
|
||||||
|
Level.SILVER: "grey78",
|
||||||
|
Level.GOLD: "yellow",
|
||||||
|
Level.PLATINUM: "white",
|
||||||
|
}
|
||||||
|
|
||||||
|
LEVEL_EMOJI = {
|
||||||
|
Level.NONE: " ",
|
||||||
|
Level.BRONZE: " ",
|
||||||
|
Level.SILVER: " ",
|
||||||
|
Level.GOLD: " ",
|
||||||
|
Level.PLATINUM: " ",
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def _level_choice(value: str) -> Level | None:
|
||||||
|
if value is None:
|
||||||
|
return None
|
||||||
|
try:
|
||||||
|
return Level[value.upper()]
|
||||||
|
except KeyError:
|
||||||
|
raise click.BadParameter(f"Unknown level: {value}. Choose from: bronze, silver, gold, platinum")
|
||||||
|
|
||||||
|
|
||||||
|
@click.group()
|
||||||
|
@click.version_option(package_name="womm-certification")
|
||||||
|
def main() -> None:
|
||||||
|
"""WOMM -- Works On My Machine Certification Toolkit.
|
||||||
|
|
||||||
|
The official tool for certifying that software works on your machine,
|
||||||
|
in accordance with WOMM-STD-001:2026.
|
||||||
|
"""
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.argument("path", default=".", type=click.Path(exists=True))
|
||||||
|
@click.option("--level", "target_level", default=None, help="Target certification level (bronze/silver/gold/platinum).")
|
||||||
|
def certify_cmd(path: str, target_level: str | None) -> None:
|
||||||
|
"""Run WOMM certification checks against a project."""
|
||||||
|
level = _level_choice(target_level) if target_level else None
|
||||||
|
report = certify(path, target_level=level)
|
||||||
|
|
||||||
|
# Header
|
||||||
|
console.print()
|
||||||
|
console.print(
|
||||||
|
Panel(
|
||||||
|
"[bold]WOMM-STD-001:2026[/bold]\nWorks On My Machine — Certification Report",
|
||||||
|
border_style="blue",
|
||||||
|
expand=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
console.print(f" Project: [bold]{report.project_name}[/bold]")
|
||||||
|
console.print(f" Path: {report.project_path}")
|
||||||
|
console.print()
|
||||||
|
|
||||||
|
# Results table
|
||||||
|
table = Table(show_header=True, header_style="bold", expand=True)
|
||||||
|
table.add_column("Level", width=10)
|
||||||
|
table.add_column("Check", width=35)
|
||||||
|
table.add_column("Result", width=6)
|
||||||
|
table.add_column("Detail")
|
||||||
|
|
||||||
|
for result in report.results:
|
||||||
|
style = LEVEL_STYLES.get(result.level, "")
|
||||||
|
status = Text("PASS", style="bold green") if result.passed else Text("FAIL", style="bold red")
|
||||||
|
table.add_row(
|
||||||
|
result.level.label,
|
||||||
|
result.name,
|
||||||
|
status,
|
||||||
|
result.message,
|
||||||
|
style=style if not result.passed else "",
|
||||||
|
)
|
||||||
|
|
||||||
|
console.print(table)
|
||||||
|
console.print()
|
||||||
|
|
||||||
|
# Final verdict
|
||||||
|
achieved = report.level
|
||||||
|
style = LEVEL_STYLES.get(achieved, "bold")
|
||||||
|
emoji = LEVEL_EMOJI.get(achieved, "")
|
||||||
|
|
||||||
|
if achieved == Level.NONE:
|
||||||
|
console.print(
|
||||||
|
Panel(
|
||||||
|
"[bold red]CERTIFICATION FAILED[/bold red]\n\n"
|
||||||
|
"The software does not meet the minimum requirements of\n"
|
||||||
|
"WOMM-STD-001:2026. Please address the above findings\n"
|
||||||
|
"and try again. Or don't. We're not your boss.",
|
||||||
|
border_style="red",
|
||||||
|
expand=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else:
|
||||||
|
console.print(
|
||||||
|
Panel(
|
||||||
|
f"[bold {style}]{emoji}WOMM {achieved.label.upper()} CERTIFIED{emoji}[/bold {style}]\n\n"
|
||||||
|
f"This project has been certified to WOMM {achieved.label} level\n"
|
||||||
|
"in accordance with WOMM-STD-001:2026.\n\n"
|
||||||
|
"[dim]This certification is valid on this machine only.\n"
|
||||||
|
"Void if anyone asks 'are you sure?'[/dim]",
|
||||||
|
border_style=style,
|
||||||
|
expand=False,
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
console.print()
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.option("--level", default="gold", help="Certification level (bronze/silver/gold/platinum).")
|
||||||
|
@click.option("--output", "-o", default="womm-seal.svg", help="Output file path (.svg or .png).")
|
||||||
|
@click.option("--size", default=400, type=int, help="Badge size in pixels.")
|
||||||
|
def badge(level: str, output: str, size: int) -> None:
|
||||||
|
"""Generate a WOMM Seal of Approval badge."""
|
||||||
|
cert_level = _level_choice(level)
|
||||||
|
if cert_level is None or cert_level == Level.NONE:
|
||||||
|
raise click.BadParameter("Level must be bronze, silver, gold, or platinum.")
|
||||||
|
|
||||||
|
try:
|
||||||
|
out_path = save_badge(output, level=cert_level, size=size)
|
||||||
|
console.print(f"[green]Badge saved to:[/green] {out_path}")
|
||||||
|
except SystemExit as exc:
|
||||||
|
console.print(f"[yellow]{exc}[/yellow]")
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
@click.argument("path", default=".", type=click.Path(exists=True))
|
||||||
|
@click.option("--output", "-o", default=None, help="Save certificate to file (default: print to stdout).")
|
||||||
|
def certificate(path: str, output: str | None) -> None:
|
||||||
|
"""Generate a formal WOMM certificate of compliance."""
|
||||||
|
report = certify(path)
|
||||||
|
|
||||||
|
if output:
|
||||||
|
out_path = save_certificate(report, output)
|
||||||
|
console.print(f"[green]Certificate saved to:[/green] {out_path}")
|
||||||
|
else:
|
||||||
|
console.print(generate_certificate(report))
|
||||||
|
|
||||||
|
|
||||||
|
@main.command()
|
||||||
|
def standard() -> None:
|
||||||
|
"""Print the full WOMM-STD-001:2026 standard."""
|
||||||
|
standard_path = Path(__file__).resolve().parent.parent.parent / "WOMM-STANDARD-001.md"
|
||||||
|
if standard_path.is_file():
|
||||||
|
console.print(standard_path.read_text(encoding="utf-8"))
|
||||||
|
else:
|
||||||
|
console.print("[yellow]Standard document not found.[/yellow]")
|
||||||
|
console.print("Expected at: " + str(standard_path))
|
||||||
|
|
||||||
|
|
||||||
|
# Register 'certify' as the command name (avoiding clash with the function import)
|
||||||
|
main.add_command(certify_cmd, name="certify")
|
||||||
154
src/womm/document.py
Normal file
154
src/womm/document.py
Normal file
@ -0,0 +1,154 @@
|
|||||||
|
"""Generate formal WOMM certification documents."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
import getpass
|
||||||
|
import platform
|
||||||
|
import textwrap
|
||||||
|
from datetime import datetime
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
from womm.certify import CertificationReport, Level
|
||||||
|
|
||||||
|
|
||||||
|
def generate_certificate(report: CertificationReport) -> str:
|
||||||
|
"""Generate a formal markdown certificate from a certification report.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
report: A completed CertificationReport.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
Markdown-formatted certificate document.
|
||||||
|
"""
|
||||||
|
now = datetime.now()
|
||||||
|
hostname = platform.node() or "UNKNOWN-HOST"
|
||||||
|
username = getpass.getuser()
|
||||||
|
level = report.level
|
||||||
|
|
||||||
|
# Build check results table
|
||||||
|
check_rows = []
|
||||||
|
for r in report.results:
|
||||||
|
status = "PASS" if r.passed else "FAIL"
|
||||||
|
check_rows.append(f"| {r.name:<40} | {status:<6} | {r.message} |")
|
||||||
|
checks_table = "\n".join(check_rows)
|
||||||
|
|
||||||
|
certificate = textwrap.dedent(f"""\
|
||||||
|
================================================================
|
||||||
|
WOMM CERTIFICATE OF COMPLIANCE
|
||||||
|
WOMM-STD-001:2026
|
||||||
|
================================================================
|
||||||
|
|
||||||
|
Certificate Number: WOMM-{now.strftime('%Y%m%d%H%M%S')}-{abs(hash(report.project_name)) % 10000:04d}
|
||||||
|
Date of Issuance: {now.strftime('%Y-%m-%d %H:%M:%S')}
|
||||||
|
Standard: WOMM-STD-001:2026, First Edition
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
PROJECT INFORMATION
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Project Name: {report.project_name}
|
||||||
|
Project Path: {report.project_path}
|
||||||
|
Certification Level: {level.label.upper()} ({_level_tagline(level)})
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
CERTIFICATION AUTHORITY
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Certifying Machine: {hostname}
|
||||||
|
Certifying User: {username}
|
||||||
|
Operating System: {platform.system()} {platform.release()}
|
||||||
|
Python Version: {platform.python_version()}
|
||||||
|
|
||||||
|
This certification was performed on the above machine and is
|
||||||
|
valid exclusively for this machine in its current configuration,
|
||||||
|
including all running processes, environment variables, and the
|
||||||
|
certifier's current emotional state.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
AUDIT TRAIL
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
| {"Check":<40} | {"Result":<6} | Detail |
|
||||||
|
|{"-" * 42}|{"-" * 8}|--------|
|
||||||
|
{checks_table}
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
DECLARATION
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
I, {username}@{hostname}, do hereby certify that the software
|
||||||
|
project "{report.project_name}" has been evaluated in accordance
|
||||||
|
with WOMM-STD-001:2026 and has achieved:
|
||||||
|
|
||||||
|
*** WOMM {level.label.upper()} CERTIFICATION ***
|
||||||
|
|
||||||
|
This certification is granted under the following conditions:
|
||||||
|
|
||||||
|
1. The software was observed to work on my machine.
|
||||||
|
2. No guarantee is made regarding any other machine.
|
||||||
|
3. This certificate is void if anyone asks "are you sure?"
|
||||||
|
4. Validity expires upon the next git pull, dependency update,
|
||||||
|
or passage of more than 24 hours.
|
||||||
|
|
||||||
|
----------------------------------------------------------------
|
||||||
|
SIGNATURES
|
||||||
|
----------------------------------------------------------------
|
||||||
|
|
||||||
|
Certified by:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
{username}
|
||||||
|
{hostname}
|
||||||
|
{now.strftime('%Y-%m-%d')}
|
||||||
|
|
||||||
|
Witnessed by:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
/dev/null
|
||||||
|
(The Void)
|
||||||
|
|
||||||
|
|
||||||
|
Approved by the WOMM Standards Committee:
|
||||||
|
|
||||||
|
___________________________
|
||||||
|
The Committee
|
||||||
|
(Quorum: 1)
|
||||||
|
|
||||||
|
|
||||||
|
================================================================
|
||||||
|
This document was generated in compliance with WOMM-STD-001:2026
|
||||||
|
"Works On My Machine" Certification Standard
|
||||||
|
First Edition, 2026-04-02
|
||||||
|
|
||||||
|
Copyright 2026 The WOMM Standards Committee.
|
||||||
|
All rights reserved, except the right to guarantee
|
||||||
|
it works on your machine.
|
||||||
|
================================================================
|
||||||
|
""")
|
||||||
|
|
||||||
|
return certificate
|
||||||
|
|
||||||
|
|
||||||
|
def _level_tagline(level: Level) -> str:
|
||||||
|
return {
|
||||||
|
Level.NONE: "Not Certified",
|
||||||
|
Level.BRONZE: "It Compiles",
|
||||||
|
Level.SILVER: "Tests Pass",
|
||||||
|
Level.GOLD: "Production Ready*",
|
||||||
|
Level.PLATINUM: "Works Everywhere*",
|
||||||
|
}.get(level, "Unknown")
|
||||||
|
|
||||||
|
|
||||||
|
def save_certificate(report: CertificationReport, output_path: str | Path) -> Path:
|
||||||
|
"""Generate and save a certificate to a file.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
report: A completed CertificationReport.
|
||||||
|
output_path: File path to write the certificate.
|
||||||
|
|
||||||
|
Returns:
|
||||||
|
The resolved output path.
|
||||||
|
"""
|
||||||
|
out = Path(output_path).resolve()
|
||||||
|
out.write_text(generate_certificate(report), encoding="utf-8")
|
||||||
|
return out
|
||||||
79
src/womm_certification.egg-info/PKG-INFO
Normal file
79
src/womm_certification.egg-info/PKG-INFO
Normal file
@ -0,0 +1,79 @@
|
|||||||
|
Metadata-Version: 2.4
|
||||||
|
Name: womm-certification
|
||||||
|
Version: 1.0.0
|
||||||
|
Summary: Works On My Machine — The Official Certification Toolkit
|
||||||
|
Author: The WOMM Standards Committee
|
||||||
|
License-Expression: MIT
|
||||||
|
Requires-Python: >=3.9
|
||||||
|
Description-Content-Type: text/markdown
|
||||||
|
Requires-Dist: click>=8.0
|
||||||
|
Requires-Dist: rich>=13.0
|
||||||
|
Provides-Extra: png
|
||||||
|
Requires-Dist: cairosvg>=2.7; extra == "png"
|
||||||
|
Provides-Extra: dev
|
||||||
|
Requires-Dist: pytest>=8.0; extra == "dev"
|
||||||
|
|
||||||
|
# WOMM Certification Toolkit
|
||||||
|
|
||||||
|
**Works On My Machine -- The Official Certification Standard**
|
||||||
|
|
||||||
|
A formal certification framework for the age-old claim "it works on my machine," complete with a bureaucratic standards document, an official seal of approval, and a CLI tool to certify your projects.
|
||||||
|
|
||||||
|
## Installation
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e .
|
||||||
|
```
|
||||||
|
|
||||||
|
For PNG badge export support:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
pip install -e '.[png]'
|
||||||
|
```
|
||||||
|
|
||||||
|
## Usage
|
||||||
|
|
||||||
|
### Certify a project
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm certify # certify current directory
|
||||||
|
womm certify /path/to/project
|
||||||
|
womm certify --level gold # target a specific level
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate a badge
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm badge # default: gold, womm-seal.svg
|
||||||
|
womm badge --level platinum -o seal.svg
|
||||||
|
```
|
||||||
|
|
||||||
|
### Generate a certificate
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm certificate # print to stdout
|
||||||
|
womm certificate -o cert.txt # save to file
|
||||||
|
```
|
||||||
|
|
||||||
|
### Read the standard
|
||||||
|
|
||||||
|
```bash
|
||||||
|
womm standard
|
||||||
|
```
|
||||||
|
|
||||||
|
## Certification Levels
|
||||||
|
|
||||||
|
| Level | Requirements |
|
||||||
|
|----------|-------------|
|
||||||
|
| Bronze | Project exists, has source files, no syntax errors |
|
||||||
|
| Silver | Bronze + tests exist and pass |
|
||||||
|
| Gold | Silver + README exists, no TODO/FIXME/HACK comments |
|
||||||
|
| Platinum | Gold + CI config present, git tree is clean |
|
||||||
|
|
||||||
|
## The Standard
|
||||||
|
|
||||||
|
See [WOMM-STD-001:2026](WOMM-STANDARD-001.md) for the full certification standard.
|
||||||
|
|
||||||
|
## License
|
||||||
|
|
||||||
|
MIT
|
||||||
14
src/womm_certification.egg-info/SOURCES.txt
Normal file
14
src/womm_certification.egg-info/SOURCES.txt
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
README.md
|
||||||
|
pyproject.toml
|
||||||
|
src/womm/__init__.py
|
||||||
|
src/womm/badge.py
|
||||||
|
src/womm/certify.py
|
||||||
|
src/womm/cli.py
|
||||||
|
src/womm/document.py
|
||||||
|
src/womm_certification.egg-info/PKG-INFO
|
||||||
|
src/womm_certification.egg-info/SOURCES.txt
|
||||||
|
src/womm_certification.egg-info/dependency_links.txt
|
||||||
|
src/womm_certification.egg-info/entry_points.txt
|
||||||
|
src/womm_certification.egg-info/requires.txt
|
||||||
|
src/womm_certification.egg-info/top_level.txt
|
||||||
|
tests/test_certify.py
|
||||||
1
src/womm_certification.egg-info/dependency_links.txt
Normal file
1
src/womm_certification.egg-info/dependency_links.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
|
||||||
2
src/womm_certification.egg-info/entry_points.txt
Normal file
2
src/womm_certification.egg-info/entry_points.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
[console_scripts]
|
||||||
|
womm = womm.cli:main
|
||||||
8
src/womm_certification.egg-info/requires.txt
Normal file
8
src/womm_certification.egg-info/requires.txt
Normal file
@ -0,0 +1,8 @@
|
|||||||
|
click>=8.0
|
||||||
|
rich>=13.0
|
||||||
|
|
||||||
|
[dev]
|
||||||
|
pytest>=8.0
|
||||||
|
|
||||||
|
[png]
|
||||||
|
cairosvg>=2.7
|
||||||
1
src/womm_certification.egg-info/top_level.txt
Normal file
1
src/womm_certification.egg-info/top_level.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
womm
|
||||||
0
tests/__init__.py
Normal file
0
tests/__init__.py
Normal file
153
tests/test_certify.py
Normal file
153
tests/test_certify.py
Normal file
@ -0,0 +1,153 @@
|
|||||||
|
"""Tests for WOMM certification checks."""
|
||||||
|
|
||||||
|
from __future__ import annotations
|
||||||
|
|
||||||
|
from pathlib import Path
|
||||||
|
|
||||||
|
import pytest
|
||||||
|
|
||||||
|
from womm.certify import (
|
||||||
|
CertificationReport,
|
||||||
|
Level,
|
||||||
|
certify,
|
||||||
|
_check_has_source_files,
|
||||||
|
_check_project_exists,
|
||||||
|
_check_python_syntax,
|
||||||
|
_check_readme_exists,
|
||||||
|
_check_no_todo_comments,
|
||||||
|
)
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def empty_project(tmp_path: Path) -> Path:
|
||||||
|
"""An empty project directory."""
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def bronze_project(tmp_path: Path) -> Path:
|
||||||
|
"""A project that should achieve Bronze certification."""
|
||||||
|
(tmp_path / "main.py").write_text("print('hello')\n")
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
@pytest.fixture()
|
||||||
|
def silver_project(tmp_path: Path) -> Path:
|
||||||
|
"""A project that should achieve at least Silver."""
|
||||||
|
(tmp_path / "main.py").write_text("def add(a, b):\n return a + b\n")
|
||||||
|
tests_dir = tmp_path / "tests"
|
||||||
|
tests_dir.mkdir()
|
||||||
|
(tests_dir / "__init__.py").write_text("")
|
||||||
|
(tests_dir / "test_main.py").write_text(
|
||||||
|
"from pathlib import Path\nimport sys\n"
|
||||||
|
"sys.path.insert(0, str(Path(__file__).parent.parent))\n"
|
||||||
|
"from main import add\n\n"
|
||||||
|
"def test_add():\n assert add(1, 2) == 3\n"
|
||||||
|
)
|
||||||
|
return tmp_path
|
||||||
|
|
||||||
|
|
||||||
|
class TestLevel:
|
||||||
|
def test_level_ordering(self) -> None:
|
||||||
|
assert Level.BRONZE < Level.SILVER < Level.GOLD < Level.PLATINUM
|
||||||
|
|
||||||
|
def test_label(self) -> None:
|
||||||
|
assert Level.GOLD.label == "Gold"
|
||||||
|
assert Level.NONE.label == "Uncertified"
|
||||||
|
|
||||||
|
def test_colour_hex(self) -> None:
|
||||||
|
assert Level.GOLD.colour_hex == "#FFD700"
|
||||||
|
|
||||||
|
|
||||||
|
class TestIndividualChecks:
|
||||||
|
def test_project_exists_pass(self, tmp_path: Path) -> None:
|
||||||
|
result = _check_project_exists(tmp_path)
|
||||||
|
assert result.passed
|
||||||
|
|
||||||
|
def test_project_exists_fail(self, tmp_path: Path) -> None:
|
||||||
|
result = _check_project_exists(tmp_path / "nonexistent")
|
||||||
|
assert not result.passed
|
||||||
|
|
||||||
|
def test_has_source_files_pass(self, bronze_project: Path) -> None:
|
||||||
|
result = _check_has_source_files(bronze_project)
|
||||||
|
assert result.passed
|
||||||
|
|
||||||
|
def test_has_source_files_fail(self, empty_project: Path) -> None:
|
||||||
|
result = _check_has_source_files(empty_project)
|
||||||
|
assert not result.passed
|
||||||
|
|
||||||
|
def test_python_syntax_pass(self, bronze_project: Path) -> None:
|
||||||
|
result = _check_python_syntax(bronze_project)
|
||||||
|
assert result.passed
|
||||||
|
|
||||||
|
def test_python_syntax_fail(self, tmp_path: Path) -> None:
|
||||||
|
(tmp_path / "bad.py").write_text("def oops(\n")
|
||||||
|
result = _check_python_syntax(tmp_path)
|
||||||
|
assert not result.passed
|
||||||
|
|
||||||
|
def test_readme_exists_pass(self, tmp_path: Path) -> None:
|
||||||
|
(tmp_path / "README.md").write_text("# Hello\n")
|
||||||
|
result = _check_readme_exists(tmp_path)
|
||||||
|
assert result.passed
|
||||||
|
|
||||||
|
def test_readme_exists_fail(self, empty_project: Path) -> None:
|
||||||
|
result = _check_readme_exists(empty_project)
|
||||||
|
assert not result.passed
|
||||||
|
|
||||||
|
def test_no_todos_pass(self, bronze_project: Path) -> None:
|
||||||
|
result = _check_no_todo_comments(bronze_project)
|
||||||
|
assert result.passed
|
||||||
|
|
||||||
|
def test_no_todos_fail(self, tmp_path: Path) -> None:
|
||||||
|
(tmp_path / "main.py").write_text("# TODO: fix this\n")
|
||||||
|
result = _check_no_todo_comments(tmp_path)
|
||||||
|
assert not result.passed
|
||||||
|
|
||||||
|
|
||||||
|
class TestCertify:
|
||||||
|
def test_empty_project_gets_none(self, empty_project: Path) -> None:
|
||||||
|
report = certify(empty_project)
|
||||||
|
# Empty dir exists but has no source files
|
||||||
|
assert report.level == Level.NONE
|
||||||
|
|
||||||
|
def test_bronze_project(self, bronze_project: Path) -> None:
|
||||||
|
report = certify(bronze_project, target_level=Level.BRONZE)
|
||||||
|
assert report.level >= Level.BRONZE
|
||||||
|
|
||||||
|
def test_report_has_results(self, bronze_project: Path) -> None:
|
||||||
|
report = certify(bronze_project)
|
||||||
|
assert len(report.results) > 0
|
||||||
|
assert all(r.name and r.message for r in report.results)
|
||||||
|
|
||||||
|
def test_target_level_limits_checks(self, bronze_project: Path) -> None:
|
||||||
|
report = certify(bronze_project, target_level=Level.BRONZE)
|
||||||
|
levels_checked = {r.level for r in report.results}
|
||||||
|
assert Level.PLATINUM not in levels_checked
|
||||||
|
|
||||||
|
|
||||||
|
class TestCertificationReport:
|
||||||
|
def test_level_all_pass(self) -> None:
|
||||||
|
from womm.certify import CheckResult
|
||||||
|
|
||||||
|
report = CertificationReport(
|
||||||
|
project_path=Path("/tmp/test"),
|
||||||
|
project_name="test",
|
||||||
|
results=[
|
||||||
|
CheckResult("check1", True, "ok", Level.BRONZE),
|
||||||
|
CheckResult("check2", True, "ok", Level.SILVER),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert report.level >= Level.SILVER
|
||||||
|
|
||||||
|
def test_level_with_failure(self) -> None:
|
||||||
|
from womm.certify import CheckResult
|
||||||
|
|
||||||
|
report = CertificationReport(
|
||||||
|
project_path=Path("/tmp/test"),
|
||||||
|
project_name="test",
|
||||||
|
results=[
|
||||||
|
CheckResult("check1", True, "ok", Level.BRONZE),
|
||||||
|
CheckResult("check2", False, "nope", Level.SILVER),
|
||||||
|
],
|
||||||
|
)
|
||||||
|
assert report.level == Level.BRONZE
|
||||||
Loading…
Reference in New Issue
Block a user