lingity 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- lingity-0.1.0/LICENSE +202 -0
- lingity-0.1.0/NOTICE +28 -0
- lingity-0.1.0/PKG-INFO +419 -0
- lingity-0.1.0/README.md +383 -0
- lingity-0.1.0/lingity/__init__.py +8 -0
- lingity-0.1.0/lingity/__main__.py +3 -0
- lingity-0.1.0/lingity/analyzer.py +1553 -0
- lingity-0.1.0/lingity/cli.py +354 -0
- lingity-0.1.0/lingity/critique.py +166 -0
- lingity-0.1.0/lingity/improve.py +322 -0
- lingity-0.1.0/lingity/invariants.py +2216 -0
- lingity-0.1.0/lingity/lexicon.py +7 -0
- lingity-0.1.0/lingity/markdown.py +440 -0
- lingity-0.1.0/lingity/models.py +54 -0
- lingity-0.1.0/lingity/morphology.py +375 -0
- lingity-0.1.0/lingity/nlp.py +371 -0
- lingity-0.1.0/lingity/profiles/architecture-review.v1.json +440 -0
- lingity-0.1.0/lingity/profiles/product-strategy.v1.json +556 -0
- lingity-0.1.0/lingity/profiles/resume-review.v1.json +417 -0
- lingity-0.1.0/lingity/profiles/web-copy.v1.json +650 -0
- lingity-0.1.0/lingity/profiles.py +121 -0
- lingity-0.1.0/lingity/providers/__init__.py +87 -0
- lingity-0.1.0/lingity/providers/agent.py +133 -0
- lingity-0.1.0/lingity/providers/anthropic_provider.py +548 -0
- lingity-0.1.0/lingity/providers/base.py +239 -0
- lingity-0.1.0/lingity/providers/openai_provider.py +419 -0
- lingity-0.1.0/lingity/py.typed +0 -0
- lingity-0.1.0/lingity/schemas/v1/analysis.schema.json +516 -0
- lingity-0.1.0/lingity/schemas/v1/critique.schema.json +153 -0
- lingity-0.1.0/lingity/schemas/v1/profile.schema.json +98 -0
- lingity-0.1.0/lingity/schemas/v1/provider-challenge.schema.json +28 -0
- lingity-0.1.0/lingity/schemas/v1/provider-proposal.schema.json +24 -0
- lingity-0.1.0/lingity/schemas/v1/verdict.schema.json +94 -0
- lingity-0.1.0/lingity/schemas/v1/verification.schema.json +81 -0
- lingity-0.1.0/lingity/scoring.py +95 -0
- lingity-0.1.0/lingity/text.py +94 -0
- lingity-0.1.0/lingity.egg-info/PKG-INFO +419 -0
- lingity-0.1.0/lingity.egg-info/SOURCES.txt +59 -0
- lingity-0.1.0/lingity.egg-info/dependency_links.txt +1 -0
- lingity-0.1.0/lingity.egg-info/entry_points.txt +2 -0
- lingity-0.1.0/lingity.egg-info/requires.txt +15 -0
- lingity-0.1.0/lingity.egg-info/top_level.txt +1 -0
- lingity-0.1.0/pyproject.toml +120 -0
- lingity-0.1.0/setup.cfg +4 -0
- lingity-0.1.0/tests/test_analysis.py +687 -0
- lingity-0.1.0/tests/test_cli.py +111 -0
- lingity-0.1.0/tests/test_determinism.py +20 -0
- lingity-0.1.0/tests/test_documentation.py +994 -0
- lingity-0.1.0/tests/test_improve.py +367 -0
- lingity-0.1.0/tests/test_invariants.py +972 -0
- lingity-0.1.0/tests/test_markdown.py +683 -0
- lingity-0.1.0/tests/test_meaning_equivalence.py +205 -0
- lingity-0.1.0/tests/test_morphology.py +445 -0
- lingity-0.1.0/tests/test_product_strategy_profile.py +269 -0
- lingity-0.1.0/tests/test_provider_anthropic.py +303 -0
- lingity-0.1.0/tests/test_provider_openai.py +241 -0
- lingity-0.1.0/tests/test_release.py +362 -0
- lingity-0.1.0/tests/test_resume_review_profile.py +533 -0
- lingity-0.1.0/tests/test_schemas.py +107 -0
- lingity-0.1.0/tests/test_scoring.py +320 -0
- lingity-0.1.0/tests/test_web_copy_profile.py +179 -0
lingity-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
|
|
2
|
+
Apache License
|
|
3
|
+
Version 2.0, January 2004
|
|
4
|
+
http://www.apache.org/licenses/
|
|
5
|
+
|
|
6
|
+
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
|
7
|
+
|
|
8
|
+
1. Definitions.
|
|
9
|
+
|
|
10
|
+
"License" shall mean the terms and conditions for use, reproduction,
|
|
11
|
+
and distribution as defined by Sections 1 through 9 of this document.
|
|
12
|
+
|
|
13
|
+
"Licensor" shall mean the copyright owner or entity authorized by
|
|
14
|
+
the copyright owner that is granting the License.
|
|
15
|
+
|
|
16
|
+
"Legal Entity" shall mean the union of the acting entity and all
|
|
17
|
+
other entities that control, are controlled by, or are under common
|
|
18
|
+
control with that entity. For the purposes of this definition,
|
|
19
|
+
"control" means (i) the power, direct or indirect, to cause the
|
|
20
|
+
direction or management of such entity, whether by contract or
|
|
21
|
+
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
|
22
|
+
outstanding shares, or (iii) beneficial ownership of such entity.
|
|
23
|
+
|
|
24
|
+
"You" (or "Your") shall mean an individual or Legal Entity
|
|
25
|
+
exercising permissions granted by this License.
|
|
26
|
+
|
|
27
|
+
"Source" form shall mean the preferred form for making modifications,
|
|
28
|
+
including but not limited to software source code, documentation
|
|
29
|
+
source, and configuration files.
|
|
30
|
+
|
|
31
|
+
"Object" form shall mean any form resulting from mechanical
|
|
32
|
+
transformation or translation of a Source form, including but
|
|
33
|
+
not limited to compiled object code, generated documentation,
|
|
34
|
+
and conversions to other media types.
|
|
35
|
+
|
|
36
|
+
"Work" shall mean the work of authorship, whether in Source or
|
|
37
|
+
Object form, made available under the License, as indicated by a
|
|
38
|
+
copyright notice that is included in or attached to the work
|
|
39
|
+
(an example is provided in the Appendix below).
|
|
40
|
+
|
|
41
|
+
"Derivative Works" shall mean any work, whether in Source or Object
|
|
42
|
+
form, that is based on (or derived from) the Work and for which the
|
|
43
|
+
editorial revisions, annotations, elaborations, or other modifications
|
|
44
|
+
represent, as a whole, an original work of authorship. For the purposes
|
|
45
|
+
of this License, Derivative Works shall not include works that remain
|
|
46
|
+
separable from, or merely link (or bind by name) to the interfaces of,
|
|
47
|
+
the Work and Derivative Works thereof.
|
|
48
|
+
|
|
49
|
+
"Contribution" shall mean any work of authorship, including
|
|
50
|
+
the original version of the Work and any modifications or additions
|
|
51
|
+
to that Work or Derivative Works thereof, that is intentionally
|
|
52
|
+
submitted to Licensor for inclusion in the Work by the copyright owner
|
|
53
|
+
or by an individual or Legal Entity authorized to submit on behalf of
|
|
54
|
+
the copyright owner. For the purposes of this definition, "submitted"
|
|
55
|
+
means any form of electronic, verbal, or written communication sent
|
|
56
|
+
to the Licensor or its representatives, including but not limited to
|
|
57
|
+
communication on electronic mailing lists, source code control systems,
|
|
58
|
+
and issue tracking systems that are managed by, or on behalf of, the
|
|
59
|
+
Licensor for the purpose of discussing and improving the Work, but
|
|
60
|
+
excluding communication that is conspicuously marked or otherwise
|
|
61
|
+
designated in writing by the copyright owner as "Not a Contribution."
|
|
62
|
+
|
|
63
|
+
"Contributor" shall mean Licensor and any individual or Legal Entity
|
|
64
|
+
on behalf of whom a Contribution has been received by Licensor and
|
|
65
|
+
subsequently incorporated within the Work.
|
|
66
|
+
|
|
67
|
+
2. Grant of Copyright License. Subject to the terms and conditions of
|
|
68
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
69
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
70
|
+
copyright license to reproduce, prepare Derivative Works of,
|
|
71
|
+
publicly display, publicly perform, sublicense, and distribute the
|
|
72
|
+
Work and such Derivative Works in Source or Object form.
|
|
73
|
+
|
|
74
|
+
3. Grant of Patent License. Subject to the terms and conditions of
|
|
75
|
+
this License, each Contributor hereby grants to You a perpetual,
|
|
76
|
+
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
|
77
|
+
(except as stated in this section) patent license to make, have made,
|
|
78
|
+
use, offer to sell, sell, import, and otherwise transfer the Work,
|
|
79
|
+
where such license applies only to those patent claims licensable
|
|
80
|
+
by such Contributor that are necessarily infringed by their
|
|
81
|
+
Contribution(s) alone or by combination of their Contribution(s)
|
|
82
|
+
with the Work to which such Contribution(s) was submitted. If You
|
|
83
|
+
institute patent litigation against any entity (including a
|
|
84
|
+
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
|
85
|
+
or a Contribution incorporated within the Work constitutes direct
|
|
86
|
+
or contributory patent infringement, then any patent licenses
|
|
87
|
+
granted to You under this License for that Work shall terminate
|
|
88
|
+
as of the date such litigation is filed.
|
|
89
|
+
|
|
90
|
+
4. Redistribution. You may reproduce and distribute copies of the
|
|
91
|
+
Work or Derivative Works thereof in any medium, with or without
|
|
92
|
+
modifications, and in Source or Object form, provided that You
|
|
93
|
+
meet the following conditions:
|
|
94
|
+
|
|
95
|
+
(a) You must give any other recipients of the Work or
|
|
96
|
+
Derivative Works a copy of this License; and
|
|
97
|
+
|
|
98
|
+
(b) You must cause any modified files to carry prominent notices
|
|
99
|
+
stating that You changed the files; and
|
|
100
|
+
|
|
101
|
+
(c) You must retain, in the Source form of any Derivative Works
|
|
102
|
+
that You distribute, all copyright, patent, trademark, and
|
|
103
|
+
attribution notices from the Source form of the Work,
|
|
104
|
+
excluding those notices that do not pertain to any part of
|
|
105
|
+
the Derivative Works; and
|
|
106
|
+
|
|
107
|
+
(d) If the Work includes a "NOTICE" text file as part of its
|
|
108
|
+
distribution, then any Derivative Works that You distribute must
|
|
109
|
+
include a readable copy of the attribution notices contained
|
|
110
|
+
within such NOTICE file, excluding those notices that do not
|
|
111
|
+
pertain to any part of the Derivative Works, in at least one
|
|
112
|
+
of the following places: within a NOTICE text file distributed
|
|
113
|
+
as part of the Derivative Works; within the Source form or
|
|
114
|
+
documentation, if provided along with the Derivative Works; or,
|
|
115
|
+
within a display generated by the Derivative Works, if and
|
|
116
|
+
wherever such third-party notices normally appear. The contents
|
|
117
|
+
of the NOTICE file are for informational purposes only and
|
|
118
|
+
do not modify the License. You may add Your own attribution
|
|
119
|
+
notices within Derivative Works that You distribute, alongside
|
|
120
|
+
or as an addendum to the NOTICE text from the Work, provided
|
|
121
|
+
that such additional attribution notices cannot be construed
|
|
122
|
+
as modifying the License.
|
|
123
|
+
|
|
124
|
+
You may add Your own copyright statement to Your modifications and
|
|
125
|
+
may provide additional or different license terms and conditions
|
|
126
|
+
for use, reproduction, or distribution of Your modifications, or
|
|
127
|
+
for any such Derivative Works as a whole, provided Your use,
|
|
128
|
+
reproduction, and distribution of the Work otherwise complies with
|
|
129
|
+
the conditions stated in this License.
|
|
130
|
+
|
|
131
|
+
5. Submission of Contributions. Unless You explicitly state otherwise,
|
|
132
|
+
any Contribution intentionally submitted for inclusion in the Work
|
|
133
|
+
by You to the Licensor shall be under the terms and conditions of
|
|
134
|
+
this License, without any additional terms or conditions.
|
|
135
|
+
Notwithstanding the above, nothing herein shall supersede or modify
|
|
136
|
+
the terms of any separate license agreement you may have executed
|
|
137
|
+
with Licensor regarding such Contributions.
|
|
138
|
+
|
|
139
|
+
6. Trademarks. This License does not grant permission to use the trade
|
|
140
|
+
names, trademarks, service marks, or product names of the Licensor,
|
|
141
|
+
except as required for reasonable and customary use in describing the
|
|
142
|
+
origin of the Work and reproducing the content of the NOTICE file.
|
|
143
|
+
|
|
144
|
+
7. Disclaimer of Warranty. Unless required by applicable law or
|
|
145
|
+
agreed to in writing, Licensor provides the Work (and each
|
|
146
|
+
Contributor provides its Contributions) on an "AS IS" BASIS,
|
|
147
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
|
148
|
+
implied, including, without limitation, any warranties or conditions
|
|
149
|
+
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
|
150
|
+
PARTICULAR PURPOSE. You are solely responsible for determining the
|
|
151
|
+
appropriateness of using or redistributing the Work and assume any
|
|
152
|
+
risks associated with Your exercise of permissions under this License.
|
|
153
|
+
|
|
154
|
+
8. Limitation of Liability. In no event and under no legal theory,
|
|
155
|
+
whether in tort (including negligence), contract, or otherwise,
|
|
156
|
+
unless required by applicable law (such as deliberate and grossly
|
|
157
|
+
negligent acts) or agreed to in writing, shall any Contributor be
|
|
158
|
+
liable to You for damages, including any direct, indirect, special,
|
|
159
|
+
incidental, or consequential damages of any character arising as a
|
|
160
|
+
result of this License or out of the use or inability to use the
|
|
161
|
+
Work (including but not limited to damages for loss of goodwill,
|
|
162
|
+
work stoppage, computer failure or malfunction, or any and all
|
|
163
|
+
other commercial damages or losses), even if such Contributor
|
|
164
|
+
has been advised of the possibility of such damages.
|
|
165
|
+
|
|
166
|
+
9. Accepting Warranty or Additional Liability. While redistributing
|
|
167
|
+
the Work or Derivative Works thereof, You may choose to offer,
|
|
168
|
+
and charge a fee for, acceptance of support, warranty, indemnity,
|
|
169
|
+
or other liability obligations and/or rights consistent with this
|
|
170
|
+
License. However, in accepting such obligations, You may act only
|
|
171
|
+
on Your own behalf and on Your sole responsibility, not on behalf
|
|
172
|
+
of any other Contributor, and only if You agree to indemnify,
|
|
173
|
+
defend, and hold each Contributor harmless for any liability
|
|
174
|
+
incurred by, or claims asserted against, such Contributor by reason
|
|
175
|
+
of your accepting any such warranty or additional liability.
|
|
176
|
+
|
|
177
|
+
END OF TERMS AND CONDITIONS
|
|
178
|
+
|
|
179
|
+
APPENDIX: How to apply the Apache License to your work.
|
|
180
|
+
|
|
181
|
+
To apply the Apache License to your work, attach the following
|
|
182
|
+
boilerplate notice, with the fields enclosed by brackets "[]"
|
|
183
|
+
replaced with your own identifying information. (Don't include
|
|
184
|
+
the brackets!) The text should be enclosed in the appropriate
|
|
185
|
+
comment syntax for the file format. We also recommend that a
|
|
186
|
+
file or class name and description of purpose be included on the
|
|
187
|
+
same "printed page" as the copyright notice for easier
|
|
188
|
+
identification within third-party archives.
|
|
189
|
+
|
|
190
|
+
Copyright [yyyy] [name of copyright owner]
|
|
191
|
+
|
|
192
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
193
|
+
you may not use this file except in compliance with the License.
|
|
194
|
+
You may obtain a copy of the License at
|
|
195
|
+
|
|
196
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
197
|
+
|
|
198
|
+
Unless required by applicable law or agreed to in writing, software
|
|
199
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
200
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
201
|
+
See the License for the specific language governing permissions and
|
|
202
|
+
limitations under the License.
|
lingity-0.1.0/NOTICE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
Lingity
|
|
2
|
+
Copyright 2026 phrocker
|
|
3
|
+
|
|
4
|
+
Licensed under the Apache License, Version 2.0 (the "License");
|
|
5
|
+
you may not use this file except in compliance with the License.
|
|
6
|
+
You may obtain a copy of the License at
|
|
7
|
+
|
|
8
|
+
http://www.apache.org/licenses/LICENSE-2.0
|
|
9
|
+
|
|
10
|
+
Unless required by applicable law or agreed to in writing, software
|
|
11
|
+
distributed under the License is distributed on an "AS IS" BASIS,
|
|
12
|
+
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
13
|
+
See the License for the specific language governing permissions and
|
|
14
|
+
limitations under the License.
|
|
15
|
+
|
|
16
|
+
--------------------------------------------------------------------------------
|
|
17
|
+
|
|
18
|
+
This product bundles no third-party source. It depends at runtime on the
|
|
19
|
+
packages declared in pyproject.toml, and on two data artifacts that are not
|
|
20
|
+
declared as dependencies and must be installed separately:
|
|
21
|
+
|
|
22
|
+
* spaCy's en_core_web_sm model, distributed under the MIT License by
|
|
23
|
+
Explosion AI (https://github.com/explosion/spacy-models).
|
|
24
|
+
* The NLTK WordNet corpus, distributed under the WordNet 3.0 License by
|
|
25
|
+
Princeton University (https://wordnet.princeton.edu/license-and-commercial-use).
|
|
26
|
+
|
|
27
|
+
Neither is redistributed here. Each is downloaded by the installing user under
|
|
28
|
+
its own terms.
|
lingity-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,419 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: lingity
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Deterministic governed text analysis
|
|
5
|
+
Author: phrocker
|
|
6
|
+
License-Expression: Apache-2.0
|
|
7
|
+
Project-URL: Homepage, https://github.com/phrocker/lingitylingity
|
|
8
|
+
Project-URL: Repository, https://github.com/phrocker/lingitylingity
|
|
9
|
+
Project-URL: Issues, https://github.com/phrocker/lingitylingity/issues
|
|
10
|
+
Keywords: linguistics,readability,governance,nlp,deterministic
|
|
11
|
+
Classifier: Development Status :: 3 - Alpha
|
|
12
|
+
Classifier: Intended Audience :: Developers
|
|
13
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
14
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
15
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
16
|
+
Classifier: Topic :: Text Processing :: Linguistic
|
|
17
|
+
Classifier: Typing :: Typed
|
|
18
|
+
Requires-Python: >=3.11
|
|
19
|
+
Description-Content-Type: text/markdown
|
|
20
|
+
License-File: LICENSE
|
|
21
|
+
License-File: NOTICE
|
|
22
|
+
Requires-Dist: jsonschema<5,>=4.23
|
|
23
|
+
Requires-Dist: markdown-it-py<4,>=3
|
|
24
|
+
Requires-Dist: nltk<4,>=3.9
|
|
25
|
+
Requires-Dist: spacy<3.9,>=3.8
|
|
26
|
+
Provides-Extra: dev
|
|
27
|
+
Requires-Dist: mypy<2,>=1.13; extra == "dev"
|
|
28
|
+
Requires-Dist: pytest<9,>=8.3; extra == "dev"
|
|
29
|
+
Requires-Dist: trove-classifiers>=2024.10.0; extra == "dev"
|
|
30
|
+
Requires-Dist: types-jsonschema<5,>=4.23; extra == "dev"
|
|
31
|
+
Provides-Extra: release
|
|
32
|
+
Requires-Dist: lingity[dev]; extra == "release"
|
|
33
|
+
Requires-Dist: build<2,>=1.2; extra == "release"
|
|
34
|
+
Requires-Dist: twine<7,>=5; extra == "release"
|
|
35
|
+
Dynamic: license-file
|
|
36
|
+
|
|
37
|
+
# Lingity
|
|
38
|
+
|
|
39
|
+
Lingity currently provides deterministic, governed text analysis for
|
|
40
|
+
LLM-authored or human-authored content. This milestone ships two local CLI
|
|
41
|
+
commands: `analyze` produces a reproducible JSON analysis artifact, and
|
|
42
|
+
`verify` validates and replays that artifact.
|
|
43
|
+
|
|
44
|
+
There are no network calls or LLM calls in the current runtime. Provider
|
|
45
|
+
interfaces and schemas exist for planned rewrite-proposal and semantic-drift
|
|
46
|
+
adapters, but they are not invoked by `analyze` or `verify`.
|
|
47
|
+
|
|
48
|
+
## Core contract
|
|
49
|
+
|
|
50
|
+
1. The source text is immutable.
|
|
51
|
+
2. Every score is reproducible from a versioned profile and analyzer.
|
|
52
|
+
3. The current runtime must not emit success-shaped fallback results.
|
|
53
|
+
4. Protected facts, identifiers, quantities, modality, negation, citations,
|
|
54
|
+
ownership, and governance status must survive unchanged.
|
|
55
|
+
5. Planned rewrite candidates must improve the configured linguistic
|
|
56
|
+
thresholds without introducing a hard-gate violation.
|
|
57
|
+
6. Planned rewrite runs must surface semantic uncertainty as `needs_human`,
|
|
58
|
+
not disguise it as success.
|
|
59
|
+
|
|
60
|
+
## Current workflow
|
|
61
|
+
|
|
62
|
+
```text
|
|
63
|
+
source text
|
|
64
|
+
-> deterministic analysis
|
|
65
|
+
-> schema-valid analysis artifact
|
|
66
|
+
-> deterministic verification replay
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
## Planned workflow
|
|
70
|
+
|
|
71
|
+
```text
|
|
72
|
+
source text
|
|
73
|
+
-> deterministic analysis
|
|
74
|
+
-> bounded LLM proposal
|
|
75
|
+
-> invariant validation
|
|
76
|
+
-> deterministic rescoring
|
|
77
|
+
-> semantic-drift challenge
|
|
78
|
+
-> accept, iterate, reject, or require human review
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Four profiles ship. `architecture-review` reads recommendations, ADR summaries,
|
|
82
|
+
findings, risks, and review decisions that must remain precise while reading
|
|
83
|
+
like professional human communication. `product-strategy` reads need
|
|
84
|
+
statements, value propositions, positioning, and go-to-market plans.
|
|
85
|
+
`web-copy` reads public-facing prose: landing pages, product descriptions,
|
|
86
|
+
job listings. `resume-review` reads resume and CV accomplishment bullets.
|
|
87
|
+
|
|
88
|
+
A strategy document fails differently from an architecture review. It claims
|
|
89
|
+
something unfalsifiable, or it claims it without naming who acts, so
|
|
90
|
+
`product-strategy` weights agency and lexical clarity highest and structure
|
|
91
|
+
lowest.
|
|
92
|
+
|
|
93
|
+
It also sets `require_responsible_actor`. Under that threshold a directive must
|
|
94
|
+
name an actor the profile recognises, rather than any noun at all, and
|
|
95
|
+
`product-strategy` omits "market", "industry", and "space" from its actor
|
|
96
|
+
terms. "The market should prioritize retention" therefore reports
|
|
97
|
+
`LING-ACTOR-001`, because a sentence whose only actor is the market names
|
|
98
|
+
nobody who can act.
|
|
99
|
+
|
|
100
|
+
The difference is measurable. On the same hyped paragraph,
|
|
101
|
+
`architecture-review` scores 88.65 and reports no jargon at all, while
|
|
102
|
+
`product-strategy` scores 69.53 and reports five jargon findings. Neither
|
|
103
|
+
profile penalises prose that names a number, an actor, and a limit.
|
|
104
|
+
|
|
105
|
+
A resume fails differently again. A bullet drops its subject by convention, so
|
|
106
|
+
`resume-review` sets `allow_implied_first_person`: a directive that carries no
|
|
107
|
+
subject at all is read as the author rather than as missing agency. The reading
|
|
108
|
+
is narrow on purpose. It suppresses the missing-subject finding and nothing
|
|
109
|
+
else, so "Must be completed before the release" still reports both
|
|
110
|
+
`LING-AGENCY-001` and `LING-PASSIVE-001` — a resume written in the passive
|
|
111
|
+
hides the work, which is the defect this profile exists to find.
|
|
112
|
+
|
|
113
|
+
`resume-review` also sets `count_repetition_across_blocks`. Redundancy is
|
|
114
|
+
otherwise counted within a single block, which a bullet list defeats: every
|
|
115
|
+
bullet is its own block, so a verb opening six of them never repeats inside
|
|
116
|
+
one. The flag compares the document as one bucket, and widens which tokens are
|
|
117
|
+
compared rather than which are read.
|
|
118
|
+
|
|
119
|
+
Without the threshold the actor rule taxes the wrong lines. "Cut checkout
|
|
120
|
+
latency from 1.2 seconds to 300 milliseconds" reports `LING-ACTOR-001` under
|
|
121
|
+
both prose profiles, while "Responsible for the migration of the reporting
|
|
122
|
+
platform" reports nothing at all under either. The measured effect on a six
|
|
123
|
+
bullet resume: `architecture-review` scores the weak version 91.56 and the
|
|
124
|
+
strong version 94.95, `product-strategy` scores them 89.44 and 93.69, and
|
|
125
|
+
`resume-review` scores them 66.43 and 100.00. Only `resume-review` ranks the two documents the way a reader would.
|
|
126
|
+
|
|
127
|
+
## Installation
|
|
128
|
+
|
|
129
|
+
Lingity is not yet published to a package index, so install it from a clone.
|
|
130
|
+
Three commands are required, and the second and third are not optional:
|
|
131
|
+
|
|
132
|
+
```text
|
|
133
|
+
python -m pip install .
|
|
134
|
+
python -m pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl
|
|
135
|
+
python -m nltk.downloader wordnet omw-1.4
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The linguistic model and the WordNet corpus are installed separately because
|
|
139
|
+
neither can be declared as a dependency. `en_core_web_sm` is not on a package
|
|
140
|
+
index, so naming it in `pyproject.toml` would require a direct URL reference,
|
|
141
|
+
and a public index rejects any distribution whose metadata carries one. WordNet
|
|
142
|
+
is corpus data rather than a Python package, so `nltk` ships the downloader and
|
|
143
|
+
not the corpus.
|
|
144
|
+
|
|
145
|
+
Skipping either step does not degrade an analysis quietly. The model loader
|
|
146
|
+
requires exactly the pinned version and raises `LinguisticModelError` on any
|
|
147
|
+
other, and canonicalization raises `WordNetDataError` when the corpus is
|
|
148
|
+
absent. Both name the command that fixes them.
|
|
149
|
+
|
|
150
|
+
## CLI
|
|
151
|
+
|
|
152
|
+
```text
|
|
153
|
+
lingity analyze review.md --profile architecture-review
|
|
154
|
+
lingity verify analysis.json
|
|
155
|
+
|
|
156
|
+
lingity critique review.md --output brief.json
|
|
157
|
+
lingity judge review.md --candidate rewrite.md
|
|
158
|
+
lingity improve review.md --provider subagent --candidate rewrite.md
|
|
159
|
+
```
|
|
160
|
+
|
|
161
|
+
`analyze` emits a deterministic, schema-valid JSON artifact containing located
|
|
162
|
+
findings, the attributed Human Readability Index, protected-element manifests,
|
|
163
|
+
and content/profile hashes. `verify` validates the schema and hashes, resolves
|
|
164
|
+
the recorded profile, and replays the analysis; altered or non-reproducible
|
|
165
|
+
artifacts fail explicitly. Both commands are pure and offline.
|
|
166
|
+
|
|
167
|
+
`critique`, `judge`, and `improve` drive rewriting. `critique` emits an
|
|
168
|
+
improvement brief — the ranked defects and the elements a rewrite may not
|
|
169
|
+
change. `judge` decides a single candidate. `improve` runs the bounded loop,
|
|
170
|
+
feeding each rejection back into the next brief. All three exit `0` on success,
|
|
171
|
+
`1` on a reasoned rejection, and `2` on an error, so a host agent can branch on
|
|
172
|
+
the exit code alone.
|
|
173
|
+
|
|
174
|
+
The current analyzer is a versioned English dependency-parse model covering
|
|
175
|
+
every deterministic signal published in the [DESIGN.md](DESIGN.md) dimension
|
|
176
|
+
table:
|
|
177
|
+
|
|
178
|
+
- **Sentence load** — words, clauses, punctuation depth, and actions per
|
|
179
|
+
sentence.
|
|
180
|
+
- **Morphology** — nominalization density and weak verb constructions.
|
|
181
|
+
- **Noun stacking** — consecutive noun modifiers and hyphenated compound depth.
|
|
182
|
+
A stack must be contiguous, and a named entity counts as one unit, so
|
|
183
|
+
`Azure Kubernetes Service cluster` is two units rather than four and a
|
|
184
|
+
person's name is never reported as a stack. Detection reads the dependency
|
|
185
|
+
relation rather than the part-of-speech tag, because the tagger reads
|
|
186
|
+
`messaging` in `messaging loss hypotheses` as a noun in one sentence and a
|
|
187
|
+
verb in another. The finding reports `words` for the span and `units` for the
|
|
188
|
+
naming units the threshold compares.
|
|
189
|
+
- **Agency** — agentless directives and missing explicit actor-action pairs.
|
|
190
|
+
- **Voice** — passive constructions and indirect predicates. Passive detection
|
|
191
|
+
is structural: it requires a passive auxiliary or passive subject relation
|
|
192
|
+
(`auxpass`/`nsubjpass`), so the active perfect (`has expired`) can never be
|
|
193
|
+
mistaken for the passive (`has been approved`).
|
|
194
|
+
- **Lexical clarity** — jargon, uncommon compounds, and undefined abbreviation
|
|
195
|
+
density.
|
|
196
|
+
- **Structure** — paragraph length, list suitability, and mixed-purpose
|
|
197
|
+
sentences.
|
|
198
|
+
- **Redundancy** — repeated qualifiers, duplicated recommendations, and filler
|
|
199
|
+
phrases. Lingity counts a repeated content word within one block, not across
|
|
200
|
+
the whole text. Governance prose has to call one concept by one name in every
|
|
201
|
+
section, so a term that recurs between sections shows consistency. Counting
|
|
202
|
+
document-wide made a finding depend on wording far away from it. Joining clear
|
|
203
|
+
paragraphs then manufactured findings that no paragraph had alone.
|
|
204
|
+
|
|
205
|
+
Every rule is block-scoped: the findings for a document are exactly the findings
|
|
206
|
+
of its blocks. A passage therefore scores the same alone as it does inside the
|
|
207
|
+
document that contains it.
|
|
208
|
+
|
|
209
|
+
A finding quotes source text the way the parser read it. The parser joins a
|
|
210
|
+
block's wrapped lines with a single space, so an observed value never carries a
|
|
211
|
+
line break or a list marker's indentation.
|
|
212
|
+
|
|
213
|
+
Noun stacking findings are reported under the `morphology` dimension and voice
|
|
214
|
+
findings under `agency`, so the score always resolves to the six weighted
|
|
215
|
+
dimensions.
|
|
216
|
+
|
|
217
|
+
Lingity reads Markdown structure before it parses prose. Headings, list items,
|
|
218
|
+
blockquotes, and paragraphs carry prose. Fenced code, indented code, tables, and
|
|
219
|
+
thematic breaks do not, so the analyzer never reads them. Each prose block
|
|
220
|
+
parses on its own, so a heading cannot glue itself onto the paragraph beneath it
|
|
221
|
+
and a table row cannot register as one long sentence. An identifier written
|
|
222
|
+
inside a code span is a name a rewrite must not change, so a finding falling
|
|
223
|
+
wholly inside one is dropped.
|
|
224
|
+
|
|
225
|
+
Each block is parsed as one unit, so a sentence wrapped across two source lines
|
|
226
|
+
stays one sentence and neither the wrap point nor the line-ending style changes
|
|
227
|
+
a score.
|
|
228
|
+
|
|
229
|
+
Block structure comes from `markdown-it-py`, which is CommonMark compliant and
|
|
230
|
+
tested against the specification's own suite. The parser is part of the analysis
|
|
231
|
+
contract exactly as the linguistic model is: its identity is published in every
|
|
232
|
+
artifact under `ingest`, and a major-version change is refused rather than
|
|
233
|
+
silently re-segmented. The artifact also publishes `unresolved_lines` and
|
|
234
|
+
`uncovered_lines`, so text that left the analysis is counted rather than lost in
|
|
235
|
+
silence. `verify` replays the segmentation.
|
|
236
|
+
|
|
237
|
+
Every finding carries a rule ID, severity, character location, observed value,
|
|
238
|
+
threshold, and remediation. Overlapping spans within a dimension are
|
|
239
|
+
de-duplicated so a single defect is not penalised twice.
|
|
240
|
+
|
|
241
|
+
Rules read sentence structure rather than surface strings, so detection
|
|
242
|
+
generalises to unseen wording. They inspect predicates, subjects, auxiliaries,
|
|
243
|
+
negation, and modifier chains. The parse is part of the analysis contract, so
|
|
244
|
+
Lingity pins the pipeline to `en_core_web_sm` at an exact version and loads it
|
|
245
|
+
fail-closed. It records the parser name, version, runtime, and digest as
|
|
246
|
+
`linguistic_model` inside the hashed artifact. `verify` refuses any artifact
|
|
247
|
+
produced by a different pipeline instead of silently re-analysing it.
|
|
248
|
+
|
|
249
|
+
The Human Readability Index weights those six dimensions and converts each
|
|
250
|
+
dimension's deducted points into a score with a half-life decay, so worse text
|
|
251
|
+
never scores higher than better text. The exact arithmetic is published in the
|
|
252
|
+
artifact's `score.formula` field.
|
|
253
|
+
|
|
254
|
+
Provider protocols exist for future proposal and semantic-challenge adapters,
|
|
255
|
+
but `analyze` and `verify` themselves perform no network or LLM calls.
|
|
256
|
+
|
|
257
|
+
See [DESIGN.md](DESIGN.md), the
|
|
258
|
+
[AgenticTuner comparison](docs/agentictuner-comparison.md), and the
|
|
259
|
+
[implementation plan](docs/implementation-plan.md).
|
|
260
|
+
|
|
261
|
+
## Rewriting
|
|
262
|
+
|
|
263
|
+
A model may *propose* a rewrite. Only deterministic code decides whether to
|
|
264
|
+
accept one. A candidate is accepted when, and only when, all of the following
|
|
265
|
+
hold:
|
|
266
|
+
|
|
267
|
+
- protected meaning is equivalent to the source,
|
|
268
|
+
- the Human Readability Index strictly improves,
|
|
269
|
+
- no new high-severity finding appears,
|
|
270
|
+
- and no semantic-drift challenge raised material doubt.
|
|
271
|
+
|
|
272
|
+
Lingity never accepts a regression, never accepts a tie, and never accepts an
|
|
273
|
+
unresolved meaning comparison. When nothing qualifies, it returns the source
|
|
274
|
+
text unchanged together with the reasons every candidate failed. It rejects a
|
|
275
|
+
candidate that scores a perfect 100 but drops a protected claim. A higher score
|
|
276
|
+
never buys a change in meaning.
|
|
277
|
+
|
|
278
|
+
Rejections are actionable. Every verdict carries `protected_delta`, naming the
|
|
279
|
+
exact elements dropped, introduced, or left unresolved, so the next attempt can
|
|
280
|
+
restore them by name instead of guessing:
|
|
281
|
+
|
|
282
|
+
```text
|
|
283
|
+
$ lingity judge source.txt --candidate shorter.txt
|
|
284
|
+
accepted False 70.46 -> 89.50
|
|
285
|
+
reason: protected meaning is changed: 9 protected element(s) dropped
|
|
286
|
+
MISSING quantity:count:2
|
|
287
|
+
MISSING governance:term:ratify
|
|
288
|
+
MISSING order:sequence:earlier=require closure evidence govern recommendation;later=target architecture return human decision
|
|
289
|
+
...
|
|
290
|
+
```
|
|
291
|
+
|
|
292
|
+
The gate compares meaning as propositions rather than as wording. It parses
|
|
293
|
+
each sentence into a claim signature: action, actor, target, modality,
|
|
294
|
+
polarity, and status. It also records the ordering relations between claims.
|
|
295
|
+
"Close the findings before sign-off" therefore agrees with "sign-off happens
|
|
296
|
+
only after the findings are closed". "Approve" and "ratify" do not agree. The
|
|
297
|
+
gate reads linking verbs as state claims. "The fix is complete and
|
|
298
|
+
fail-closed" therefore disagrees with "the fix is incomplete and fail-open".
|
|
299
|
+
No profile contains protected sentence patterns.
|
|
300
|
+
|
|
301
|
+
A held-out corpus of 32 pairs measures how well the gate generalises. It shares
|
|
302
|
+
no wording with any profile or fixture. The corpus documents the eight pairs
|
|
303
|
+
the gate does not resolve, with the linguistic reason for each. Every one of
|
|
304
|
+
them answers `unresolved` or `changed` rather than `equivalent`. The corpus is
|
|
305
|
+
evidence, not proof: it once masked a false `equivalent` on copular text behind
|
|
306
|
+
an unrelated coverage failure. Treat a passing corpus as a floor.
|
|
307
|
+
|
|
308
|
+
Providers are transports, never authorities:
|
|
309
|
+
|
|
310
|
+
- **`subagent`** (default) — no network and no API key. The host agent, such as
|
|
311
|
+
Agency, *is* the model: Lingity hands it a brief, the host writes a candidate,
|
|
312
|
+
and Lingity judges the result. Use `critique` and `judge` interactively, or
|
|
313
|
+
pass `--candidate` files to `improve`.
|
|
314
|
+
- **`openai`** and **`anthropic`** — direct API calls over the standard library.
|
|
315
|
+
Credentials come only from `OPENAI_API_KEY` and `ANTHROPIC_API_KEY`, and are
|
|
316
|
+
never accepted as arguments, logged, or written to an artifact. There is no
|
|
317
|
+
default model: omitting `--model` is an error rather than a guess.
|
|
318
|
+
|
|
319
|
+
A drift challenger may only *raise* doubt. It can block an acceptance, but it
|
|
320
|
+
can never clear a deterministic failure, and an unparseable challenge response
|
|
321
|
+
is an error rather than a quiet `no_material_change`.
|
|
322
|
+
|
|
323
|
+
## Development
|
|
324
|
+
|
|
325
|
+
```text
|
|
326
|
+
python -m pip install -e '.[dev]'
|
|
327
|
+
python -m pip install https://github.com/explosion/spacy-models/releases/download/en_core_web_sm-3.8.0/en_core_web_sm-3.8.0-py3-none-any.whl
|
|
328
|
+
python -m nltk.downloader wordnet omw-1.4
|
|
329
|
+
python -m pytest
|
|
330
|
+
python -m mypy
|
|
331
|
+
python -m compileall -q lingity tests
|
|
332
|
+
```
|
|
333
|
+
|
|
334
|
+
These are the commands CI runs, in this order, on Python 3.11 and 3.12, for
|
|
335
|
+
every push to `main` and every pull request; see `.github/workflows/ci.yml`.
|
|
336
|
+
`tests/test_documentation.py` compares this block against the workflow and fails
|
|
337
|
+
if the two diverge. The command strings are identical; CI differs only in when
|
|
338
|
+
it runs the second one, skipping the corpora download when its cache restores
|
|
339
|
+
`~/nltk_data`. Locally that step is unconditional, and repeating it once the
|
|
340
|
+
corpora are present is a no-op. Each tool is invoked through `python -m` so it
|
|
341
|
+
runs under the interpreter that has the package installed rather than whichever
|
|
342
|
+
console script happens to be first on `PATH`, and the extras spec is quoted
|
|
343
|
+
because `zsh` treats the brackets as a glob. `pytest` and `mypy` take their settings from `pyproject.toml`, so no flags
|
|
344
|
+
are needed.
|
|
345
|
+
|
|
346
|
+
Analysis needs the spaCy model and the WordNet corpora present locally. Both are
|
|
347
|
+
install-time steps on purpose: nothing downloads anything at analysis time, so a
|
|
348
|
+
run cannot silently depend on the network or quietly change behaviour when a
|
|
349
|
+
corpus is missing. Missing data is an error, not a fallback.
|
|
350
|
+
|
|
351
|
+
The model is the pinned `en_core_web_sm` 3.8.0 wheel, installed by explicit URL
|
|
352
|
+
in its own step. It is deliberately not a declared dependency: it is not on a
|
|
353
|
+
package index, so declaring it would require a direct URL reference, and a
|
|
354
|
+
public index rejects any distribution whose metadata carries one. Do not
|
|
355
|
+
substitute `python -m spacy download en_core_web_sm`: that resolves whatever
|
|
356
|
+
model version is current at the time, and `lingity/nlp.py` rejects anything but
|
|
357
|
+
3.8.0. WordNet is not a Python distribution and stays a separate download.
|
|
358
|
+
|
|
359
|
+
WordNet drives morphology — deriving the verb behind a nominalization
|
|
360
|
+
("ratification" → "ratify") and separating a word from its antonyms — rather
|
|
361
|
+
than a hand-maintained suffix list.
|
|
362
|
+
|
|
363
|
+
## Prior art
|
|
364
|
+
|
|
365
|
+
The rule families follow published work on requirements and plain-language
|
|
366
|
+
quality:
|
|
367
|
+
|
|
368
|
+
- Femmer, Méndez Fernández, Wagner, Eder, *Rapid Quality Assurance with
|
|
369
|
+
Requirements Smells* (Journal of Systems and Software, 2017) — the
|
|
370
|
+
smell-detection framing behind nominalization, passive voice, and vague-term
|
|
371
|
+
rules.
|
|
372
|
+
- INCOSE-TP-010-009, *Guide to Writing Requirements* (2019) — rules on
|
|
373
|
+
imperatives, ambiguity, and quantification.
|
|
374
|
+
- U.S. Federal Plain Language Guidelines (PLAIN) — actor-first sentences, active
|
|
375
|
+
voice, and short sentence targets.
|
|
376
|
+
|
|
377
|
+
No existing package was found that detects nominalizations, noun stacks, hidden
|
|
378
|
+
agency, or bureaucratic phrasing as attributed findings, or that gates a rewrite
|
|
379
|
+
on preserved governed meaning, so those are implemented here.
|
|
380
|
+
|
|
381
|
+
## License
|
|
382
|
+
|
|
383
|
+
Apache License 2.0. The full text is in [LICENSE](LICENSE), and [NOTICE](NOTICE)
|
|
384
|
+
carries the copyright statement and the attribution required by section 4(d).
|
|
385
|
+
|
|
386
|
+
The two data artifacts Lingity needs are not redistributed with it and are not
|
|
387
|
+
declared as dependencies. Each is downloaded by the installing user under its
|
|
388
|
+
own terms: the `en_core_web_sm` spaCy model under the MIT License, and the
|
|
389
|
+
NLTK WordNet corpus under the WordNet 3.0 License. `NOTICE` records both.
|
|
390
|
+
|
|
391
|
+
## Release
|
|
392
|
+
|
|
393
|
+
Publishing is one command, run from a machine whose twine is already
|
|
394
|
+
authenticated. No credential is read, written, or passed as an argument: twine
|
|
395
|
+
resolves them itself from `~/.pypirc`, the system keyring, or `TWINE_*`
|
|
396
|
+
environment variables.
|
|
397
|
+
|
|
398
|
+
```
|
|
399
|
+
python -m pip install -e ".[release]"
|
|
400
|
+
python scripts/release.py --repository testpypi --dry-run
|
|
401
|
+
python scripts/release.py --repository testpypi
|
|
402
|
+
python scripts/release.py --repository pypi
|
|
403
|
+
```
|
|
404
|
+
|
|
405
|
+
`--repository` is required. There is no default, because the difference between
|
|
406
|
+
the two indexes is not something to get wrong by omission.
|
|
407
|
+
|
|
408
|
+
The script refuses to publish anything it cannot verify. It stops on an unclean
|
|
409
|
+
working tree, a failing guard test, a version the index already holds, a direct
|
|
410
|
+
URL dependency, missing licence metadata, or an unknown classifier. Three of
|
|
411
|
+
those are invisible to `twine check`, which validates README rendering and
|
|
412
|
+
nothing else, and are otherwise answered for the first time by an HTTP 400 from
|
|
413
|
+
the index.
|
|
414
|
+
|
|
415
|
+
An unreachable index stops the release rather than reading as an absent version.
|
|
416
|
+
Uploads cannot be undone and a version number can never be reused, so a check
|
|
417
|
+
that cannot run must say so rather than fall silent.
|
|
418
|
+
|
|
419
|
+
`--dry-run` performs every check and builds the artifacts without uploading.
|