fairit 1.0.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.
- fairit-1.0.0/LICENSE +21 -0
- fairit-1.0.0/PKG-INFO +147 -0
- fairit-1.0.0/README.md +131 -0
- fairit-1.0.0/pyproject.toml +27 -0
- fairit-1.0.0/setup.cfg +4 -0
- fairit-1.0.0/src/fairit/__init__.py +64 -0
- fairit-1.0.0/src/fairit/brief.py +131 -0
- fairit-1.0.0/src/fairit/dialects.py +54 -0
- fairit-1.0.0/src/fairit/evidence.py +177 -0
- fairit-1.0.0/src/fairit/ground.py +123 -0
- fairit-1.0.0/src/fairit/hypotheses.py +218 -0
- fairit-1.0.0/src/fairit/ledger.py +132 -0
- fairit-1.0.0/src/fairit/overlay.py +184 -0
- fairit-1.0.0/src/fairit.egg-info/PKG-INFO +147 -0
- fairit-1.0.0/src/fairit.egg-info/SOURCES.txt +19 -0
- fairit-1.0.0/src/fairit.egg-info/dependency_links.txt +1 -0
- fairit-1.0.0/src/fairit.egg-info/top_level.txt +1 -0
- fairit-1.0.0/tests/test_ground_evidence.py +128 -0
- fairit-1.0.0/tests/test_hypotheses.py +150 -0
- fairit-1.0.0/tests/test_ledger.py +48 -0
- fairit-1.0.0/tests/test_protocol_brief.py +219 -0
fairit-1.0.0/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Ryan Wesley Hitchcock
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
fairit-1.0.0/PKG-INFO
ADDED
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: fairit
|
|
3
|
+
Version: 1.0.0
|
|
4
|
+
Summary: Ground mapping and evasion-hypothesis testing: hold the state, test the language, hand off the seeds. Never presumes evasion.
|
|
5
|
+
Author: Ryan Wesley Hitchcock
|
|
6
|
+
License: MIT
|
|
7
|
+
Project-URL: Homepage, https://github.com/ohpersonperson/frontface
|
|
8
|
+
Keywords: hypothesis-testing,evasion,audit,interrogation,evidence
|
|
9
|
+
Classifier: Programming Language :: Python :: 3
|
|
10
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
11
|
+
Classifier: Operating System :: OS Independent
|
|
12
|
+
Requires-Python: >=3.10
|
|
13
|
+
Description-Content-Type: text/markdown
|
|
14
|
+
License-File: LICENSE
|
|
15
|
+
Dynamic: license-file
|
|
16
|
+
|
|
17
|
+
# fairit — ground mapping and evasion-hypothesis testing
|
|
18
|
+
|
|
19
|
+
Tests whether evasive-looking communication is actually evading
|
|
20
|
+
anything — and if so, what. It never presumes evasion. Three stages:
|
|
21
|
+
|
|
22
|
+
1. **Ground mapping.** Establish the exact situational state without
|
|
23
|
+
forcing narrative resolution: opposing facts as parallel,
|
|
24
|
+
non-subordinating sentences ("X is true. Y is also true." — never
|
|
25
|
+
"X, but Y"), people as time-indexed states, every correction typed
|
|
26
|
+
as detail vs core-claim.
|
|
27
|
+
2. **Evasion-hypothesis testing.** Every candidate evasion — therapy-speak,
|
|
28
|
+
corporate-speak, bureaucratic passive voice, fogging, DARVO,
|
|
29
|
+
projection — becomes competing hypotheses: **H-obfuscation /
|
|
30
|
+
H-genuine / H-both**, tested against discriminating evidence.
|
|
31
|
+
3. **Handoff.** 2–3 seed frames plus the audited ground, handed to
|
|
32
|
+
whatever comes next. Then the package stands down.
|
|
33
|
+
|
|
34
|
+
The one rule everything else serves: **a hypothesis wins only on
|
|
35
|
+
discriminating evidence** — observations that would differ between the
|
|
36
|
+
hypotheses. Category membership ("this is therapy-speak") is never
|
|
37
|
+
evidence. "Insufficient evidence to distinguish" is an honest finding,
|
|
38
|
+
not a failure.
|
|
39
|
+
|
|
40
|
+
No prerequisites. The mechanism is the whole product. No model, no
|
|
41
|
+
network — this is pure machinery. The operator (human or model) supplies
|
|
42
|
+
the evidence; the library enforces the testing discipline.
|
|
43
|
+
|
|
44
|
+
*(Lineage: the front-facing conversion of two skills from Ryan's
|
|
45
|
+
skill-suite, MIT — `responsibility-obfuscation-probe` and `field-forge`,
|
|
46
|
+
converted as one unit because they share one hypothesis-testing core.
|
|
47
|
+
What changed in the conversion is documented in CHANGELOG.md.)*
|
|
48
|
+
|
|
49
|
+
## Quickstart
|
|
50
|
+
|
|
51
|
+
```python
|
|
52
|
+
from fairit import scan_and_test, evidence_tag_for
|
|
53
|
+
|
|
54
|
+
text = "Mistakes were made in the scoping phase. We're going to circle back."
|
|
55
|
+
flags = scan_and_test(text, {
|
|
56
|
+
"mistakes were made": (
|
|
57
|
+
"the passive phrasing names no actor; internal emails name the PM "
|
|
58
|
+
"who cut scoping short — the actor is known",
|
|
59
|
+
"",
|
|
60
|
+
),
|
|
61
|
+
"circle back": (
|
|
62
|
+
"used to end the paragraph about the miss without giving a new date",
|
|
63
|
+
"",
|
|
64
|
+
),
|
|
65
|
+
})
|
|
66
|
+
for flag in flags:
|
|
67
|
+
print(flag.phrase, "->", flag.record.verdict, "->", evidence_tag_for(flag.record))
|
|
68
|
+
# mistakes were made -> H-obfuscation -> OBFUSCATION
|
|
69
|
+
# circle back -> H-obfuscation -> OBFUSCATION
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
Full cycle demo (ground → probe → brief), no model needed:
|
|
73
|
+
|
|
74
|
+
```bash
|
|
75
|
+
cd probe-forge
|
|
76
|
+
PYTHONPATH=src python3 examples/run_cycle.py
|
|
77
|
+
```
|
|
78
|
+
|
|
79
|
+
## What it does
|
|
80
|
+
|
|
81
|
+
**Ground mapping** (`ground.py`) — the pre-interrogation audit:
|
|
82
|
+
|
|
83
|
+
- Held tensions must be parallel non-subordinating sentences. The
|
|
84
|
+
subordinating-conjunction scan is in code: "promised a refund, *but*
|
|
85
|
+
none arrived" is rejected; "promised a refund. None arrived." passes.
|
|
86
|
+
- People are time-indexed states, never one flat characterization.
|
|
87
|
+
- Corrections are typed detail vs core-claim. Core-claim changes — the
|
|
88
|
+
account itself shifting — are the signal, and the library surfaces
|
|
89
|
+
them.
|
|
90
|
+
|
|
91
|
+
**Hypothesis testing** (`hypotheses.py`) — the shared core:
|
|
92
|
+
|
|
93
|
+
- Four verdicts: H-obfuscation, H-genuine, H-both, insufficient-evidence.
|
|
94
|
+
- Code-enforced rules: H-obfuscation needs ≥1 evidence item favoring it;
|
|
95
|
+
H-both needs evidence on *both* sides; insufficient-evidence must name
|
|
96
|
+
what would resolve the open hypotheses.
|
|
97
|
+
- The category-presumption check: evidence that only names a category
|
|
98
|
+
("therapy-speak", "uses corporate speak") is rejected with
|
|
99
|
+
`CategoryPresumptionError`. This is the old probe's failure mode
|
|
100
|
+
(pre-judging therapy-speak as armor), made impossible in code.
|
|
101
|
+
|
|
102
|
+
**Probe protocol** (`overlay.py`) — jargon in three dialects (therapy,
|
|
103
|
+
corporate, bureaucratic; see `dialects.py`):
|
|
104
|
+
|
|
105
|
+
- Detection produces *candidates*, never verdicts. A detected phrase with
|
|
106
|
+
no evidence gets insufficient-evidence — the probe refuses to convict
|
|
107
|
+
*or* exonerate on detection alone.
|
|
108
|
+
- The evidence tag: **OBFUSCATION** only when H-obfuscation is supported
|
|
109
|
+
by discriminating evidence. Everything else stays **CLAIM** with a
|
|
110
|
+
hypothesis marker. Suspected-but-untested material is never upgraded
|
|
111
|
+
on category membership alone.
|
|
112
|
+
|
|
113
|
+
**Audit brief** (`brief.py`) — the output artifact with frontmatter
|
|
114
|
+
(`artifact:`, `date:`, `protocol:`, `lifecycle:`), Markdown
|
|
115
|
+
render/parse, and the stand-down rule enforced in code: the brief must
|
|
116
|
+
never contain collision, adjudication, refinement, or surprise sections.
|
|
117
|
+
Those belong to the interrogation engine. If the engine isn't running,
|
|
118
|
+
the brief is reported and the work stops.
|
|
119
|
+
|
|
120
|
+
**Evidence taxonomy** (`evidence.py`) — one authoritative mapping.
|
|
121
|
+
field-forge shipped an 8-tier list; the interrogation engine uses 11
|
|
122
|
+
tags (10 core + overlay-only OBFUSCATION). The forge's 8 map 1:1 onto
|
|
123
|
+
the engine's; the 3 the forge never named (HYPOTHESIS, REQUIREMENT,
|
|
124
|
+
DEPENDENCY) are documented as recognized. `check_matches_engine()`
|
|
125
|
+
guards against silent forks when the sibling package is importable.
|
|
126
|
+
|
|
127
|
+
## What's honest about the limits
|
|
128
|
+
|
|
129
|
+
- The library cannot prove a counter-hypothesis is *strong* — the
|
|
130
|
+
evidence-quality judgment stays with the operator. What it makes
|
|
131
|
+
impossible is the failure modes: verdicts without discriminating
|
|
132
|
+
evidence, category-based presumption, silent upgrades to OBFUSCATION,
|
|
133
|
+
subordinated tensions, and the overlay doing the engine's job.
|
|
134
|
+
- The dialog procedure (asking for raw input, precision-forcing
|
|
135
|
+
questions) is an operator protocol, documented in the examples — it
|
|
136
|
+
isn't codeable without a model in the loop, and isn't pretended to be.
|
|
137
|
+
|
|
138
|
+
## Tests
|
|
139
|
+
|
|
140
|
+
```bash
|
|
141
|
+
PYTHONPATH=src python3 -m unittest discover -s tests
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
55 offline tests: verdict rules, category-presumption rejection (the v1
|
|
145
|
+
regression suite), the OBFUSCATION gate, the subordinator scan,
|
|
146
|
+
correction typing, taxonomy reconciliation, brief round-trip and
|
|
147
|
+
stand-down, dialect detection, and a full ground→probe→brief cycle.
|
fairit-1.0.0/README.md
ADDED
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
# fairit — ground mapping and evasion-hypothesis testing
|
|
2
|
+
|
|
3
|
+
Tests whether evasive-looking communication is actually evading
|
|
4
|
+
anything — and if so, what. It never presumes evasion. Three stages:
|
|
5
|
+
|
|
6
|
+
1. **Ground mapping.** Establish the exact situational state without
|
|
7
|
+
forcing narrative resolution: opposing facts as parallel,
|
|
8
|
+
non-subordinating sentences ("X is true. Y is also true." — never
|
|
9
|
+
"X, but Y"), people as time-indexed states, every correction typed
|
|
10
|
+
as detail vs core-claim.
|
|
11
|
+
2. **Evasion-hypothesis testing.** Every candidate evasion — therapy-speak,
|
|
12
|
+
corporate-speak, bureaucratic passive voice, fogging, DARVO,
|
|
13
|
+
projection — becomes competing hypotheses: **H-obfuscation /
|
|
14
|
+
H-genuine / H-both**, tested against discriminating evidence.
|
|
15
|
+
3. **Handoff.** 2–3 seed frames plus the audited ground, handed to
|
|
16
|
+
whatever comes next. Then the package stands down.
|
|
17
|
+
|
|
18
|
+
The one rule everything else serves: **a hypothesis wins only on
|
|
19
|
+
discriminating evidence** — observations that would differ between the
|
|
20
|
+
hypotheses. Category membership ("this is therapy-speak") is never
|
|
21
|
+
evidence. "Insufficient evidence to distinguish" is an honest finding,
|
|
22
|
+
not a failure.
|
|
23
|
+
|
|
24
|
+
No prerequisites. The mechanism is the whole product. No model, no
|
|
25
|
+
network — this is pure machinery. The operator (human or model) supplies
|
|
26
|
+
the evidence; the library enforces the testing discipline.
|
|
27
|
+
|
|
28
|
+
*(Lineage: the front-facing conversion of two skills from Ryan's
|
|
29
|
+
skill-suite, MIT — `responsibility-obfuscation-probe` and `field-forge`,
|
|
30
|
+
converted as one unit because they share one hypothesis-testing core.
|
|
31
|
+
What changed in the conversion is documented in CHANGELOG.md.)*
|
|
32
|
+
|
|
33
|
+
## Quickstart
|
|
34
|
+
|
|
35
|
+
```python
|
|
36
|
+
from fairit import scan_and_test, evidence_tag_for
|
|
37
|
+
|
|
38
|
+
text = "Mistakes were made in the scoping phase. We're going to circle back."
|
|
39
|
+
flags = scan_and_test(text, {
|
|
40
|
+
"mistakes were made": (
|
|
41
|
+
"the passive phrasing names no actor; internal emails name the PM "
|
|
42
|
+
"who cut scoping short — the actor is known",
|
|
43
|
+
"",
|
|
44
|
+
),
|
|
45
|
+
"circle back": (
|
|
46
|
+
"used to end the paragraph about the miss without giving a new date",
|
|
47
|
+
"",
|
|
48
|
+
),
|
|
49
|
+
})
|
|
50
|
+
for flag in flags:
|
|
51
|
+
print(flag.phrase, "->", flag.record.verdict, "->", evidence_tag_for(flag.record))
|
|
52
|
+
# mistakes were made -> H-obfuscation -> OBFUSCATION
|
|
53
|
+
# circle back -> H-obfuscation -> OBFUSCATION
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
Full cycle demo (ground → probe → brief), no model needed:
|
|
57
|
+
|
|
58
|
+
```bash
|
|
59
|
+
cd probe-forge
|
|
60
|
+
PYTHONPATH=src python3 examples/run_cycle.py
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## What it does
|
|
64
|
+
|
|
65
|
+
**Ground mapping** (`ground.py`) — the pre-interrogation audit:
|
|
66
|
+
|
|
67
|
+
- Held tensions must be parallel non-subordinating sentences. The
|
|
68
|
+
subordinating-conjunction scan is in code: "promised a refund, *but*
|
|
69
|
+
none arrived" is rejected; "promised a refund. None arrived." passes.
|
|
70
|
+
- People are time-indexed states, never one flat characterization.
|
|
71
|
+
- Corrections are typed detail vs core-claim. Core-claim changes — the
|
|
72
|
+
account itself shifting — are the signal, and the library surfaces
|
|
73
|
+
them.
|
|
74
|
+
|
|
75
|
+
**Hypothesis testing** (`hypotheses.py`) — the shared core:
|
|
76
|
+
|
|
77
|
+
- Four verdicts: H-obfuscation, H-genuine, H-both, insufficient-evidence.
|
|
78
|
+
- Code-enforced rules: H-obfuscation needs ≥1 evidence item favoring it;
|
|
79
|
+
H-both needs evidence on *both* sides; insufficient-evidence must name
|
|
80
|
+
what would resolve the open hypotheses.
|
|
81
|
+
- The category-presumption check: evidence that only names a category
|
|
82
|
+
("therapy-speak", "uses corporate speak") is rejected with
|
|
83
|
+
`CategoryPresumptionError`. This is the old probe's failure mode
|
|
84
|
+
(pre-judging therapy-speak as armor), made impossible in code.
|
|
85
|
+
|
|
86
|
+
**Probe protocol** (`overlay.py`) — jargon in three dialects (therapy,
|
|
87
|
+
corporate, bureaucratic; see `dialects.py`):
|
|
88
|
+
|
|
89
|
+
- Detection produces *candidates*, never verdicts. A detected phrase with
|
|
90
|
+
no evidence gets insufficient-evidence — the probe refuses to convict
|
|
91
|
+
*or* exonerate on detection alone.
|
|
92
|
+
- The evidence tag: **OBFUSCATION** only when H-obfuscation is supported
|
|
93
|
+
by discriminating evidence. Everything else stays **CLAIM** with a
|
|
94
|
+
hypothesis marker. Suspected-but-untested material is never upgraded
|
|
95
|
+
on category membership alone.
|
|
96
|
+
|
|
97
|
+
**Audit brief** (`brief.py`) — the output artifact with frontmatter
|
|
98
|
+
(`artifact:`, `date:`, `protocol:`, `lifecycle:`), Markdown
|
|
99
|
+
render/parse, and the stand-down rule enforced in code: the brief must
|
|
100
|
+
never contain collision, adjudication, refinement, or surprise sections.
|
|
101
|
+
Those belong to the interrogation engine. If the engine isn't running,
|
|
102
|
+
the brief is reported and the work stops.
|
|
103
|
+
|
|
104
|
+
**Evidence taxonomy** (`evidence.py`) — one authoritative mapping.
|
|
105
|
+
field-forge shipped an 8-tier list; the interrogation engine uses 11
|
|
106
|
+
tags (10 core + overlay-only OBFUSCATION). The forge's 8 map 1:1 onto
|
|
107
|
+
the engine's; the 3 the forge never named (HYPOTHESIS, REQUIREMENT,
|
|
108
|
+
DEPENDENCY) are documented as recognized. `check_matches_engine()`
|
|
109
|
+
guards against silent forks when the sibling package is importable.
|
|
110
|
+
|
|
111
|
+
## What's honest about the limits
|
|
112
|
+
|
|
113
|
+
- The library cannot prove a counter-hypothesis is *strong* — the
|
|
114
|
+
evidence-quality judgment stays with the operator. What it makes
|
|
115
|
+
impossible is the failure modes: verdicts without discriminating
|
|
116
|
+
evidence, category-based presumption, silent upgrades to OBFUSCATION,
|
|
117
|
+
subordinated tensions, and the overlay doing the engine's job.
|
|
118
|
+
- The dialog procedure (asking for raw input, precision-forcing
|
|
119
|
+
questions) is an operator protocol, documented in the examples — it
|
|
120
|
+
isn't codeable without a model in the loop, and isn't pretended to be.
|
|
121
|
+
|
|
122
|
+
## Tests
|
|
123
|
+
|
|
124
|
+
```bash
|
|
125
|
+
PYTHONPATH=src python3 -m unittest discover -s tests
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
55 offline tests: verdict rules, category-presumption rejection (the v1
|
|
129
|
+
regression suite), the OBFUSCATION gate, the subordinator scan,
|
|
130
|
+
correction typing, taxonomy reconciliation, brief round-trip and
|
|
131
|
+
stand-down, dialect detection, and a full ground→probe→brief cycle.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=61"]
|
|
3
|
+
build-backend = "setuptools.build_meta"
|
|
4
|
+
|
|
5
|
+
[project]
|
|
6
|
+
name = "fairit"
|
|
7
|
+
version = "1.0.0"
|
|
8
|
+
description = "Ground mapping and evasion-hypothesis testing: hold the state, test the language, hand off the seeds. Never presumes evasion."
|
|
9
|
+
readme = "README.md"
|
|
10
|
+
license = { text = "MIT" }
|
|
11
|
+
requires-python = ">=3.10"
|
|
12
|
+
authors = [{ name = "Ryan Wesley Hitchcock" }]
|
|
13
|
+
keywords = ["hypothesis-testing", "evasion", "audit", "interrogation", "evidence"]
|
|
14
|
+
classifiers = [
|
|
15
|
+
"Programming Language :: Python :: 3",
|
|
16
|
+
"License :: OSI Approved :: MIT License",
|
|
17
|
+
"Operating System :: OS Independent",
|
|
18
|
+
]
|
|
19
|
+
|
|
20
|
+
[project.urls]
|
|
21
|
+
Homepage = "https://github.com/ohpersonperson/frontface"
|
|
22
|
+
|
|
23
|
+
[tool.setuptools.packages.find]
|
|
24
|
+
where = ["src"]
|
|
25
|
+
|
|
26
|
+
[tool.pytest.ini_options]
|
|
27
|
+
testpaths = ["tests"]
|
fairit-1.0.0/setup.cfg
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
"""fairit — pre-interrogation ground mapping and evasion testing.
|
|
2
|
+
|
|
3
|
+
Map the ground (held tensions, time-indexed states, typed corrections),
|
|
4
|
+
test evasion hypotheses (never presume them), and hand off 2-3 seed
|
|
5
|
+
frames to whatever comes next. The package never collides, adjudicates,
|
|
6
|
+
refines, or extracts surprise — it audits and hands off.
|
|
7
|
+
"""
|
|
8
|
+
|
|
9
|
+
from .brief import AuditBrief, BriefError, check_stand_down, parse
|
|
10
|
+
from .dialects import DIALECTS, SHIELD_MARKERS, detect_markers
|
|
11
|
+
from .evidence import (
|
|
12
|
+
TAXONOMY,
|
|
13
|
+
EvidenceError,
|
|
14
|
+
EvidenceItem,
|
|
15
|
+
check_matches_engine,
|
|
16
|
+
forge_mapping_table,
|
|
17
|
+
normalize_tag,
|
|
18
|
+
tag_list,
|
|
19
|
+
)
|
|
20
|
+
from .ground import (
|
|
21
|
+
Correction,
|
|
22
|
+
GroundError,
|
|
23
|
+
GroundMap,
|
|
24
|
+
HeldTension,
|
|
25
|
+
PersonState,
|
|
26
|
+
find_subordinators,
|
|
27
|
+
)
|
|
28
|
+
from .hypotheses import (
|
|
29
|
+
H_BOTH,
|
|
30
|
+
H_GENUINE,
|
|
31
|
+
H_OBFUSCATION,
|
|
32
|
+
INSUFFICIENT_EVIDENCE,
|
|
33
|
+
SUPPORT_STATUSES,
|
|
34
|
+
VERDICTS,
|
|
35
|
+
CategoryPresumptionError,
|
|
36
|
+
DiscriminatingEvidence,
|
|
37
|
+
HypothesisError,
|
|
38
|
+
HypothesisRecord,
|
|
39
|
+
Mechanism,
|
|
40
|
+
)
|
|
41
|
+
from .overlay import (
|
|
42
|
+
JargonFlag,
|
|
43
|
+
ObfuscatedObject,
|
|
44
|
+
ProbeError,
|
|
45
|
+
evidence_tag_for,
|
|
46
|
+
run_jargon_test,
|
|
47
|
+
scan_and_test,
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
__all__ = [
|
|
51
|
+
"AuditBrief", "BriefError", "check_stand_down", "parse",
|
|
52
|
+
"DIALECTS", "SHIELD_MARKERS", "detect_markers",
|
|
53
|
+
"TAXONOMY", "EvidenceError", "EvidenceItem", "check_matches_engine",
|
|
54
|
+
"forge_mapping_table", "normalize_tag", "tag_list",
|
|
55
|
+
"Correction", "GroundError", "GroundMap", "HeldTension", "PersonState",
|
|
56
|
+
"find_subordinators",
|
|
57
|
+
"H_BOTH", "H_GENUINE", "H_OBFUSCATION", "INSUFFICIENT_EVIDENCE",
|
|
58
|
+
"SUPPORT_STATUSES", "VERDICTS", "CategoryPresumptionError",
|
|
59
|
+
"DiscriminatingEvidence", "HypothesisError", "HypothesisRecord", "Mechanism",
|
|
60
|
+
"JargonFlag", "ObfuscatedObject", "ProbeError", "evidence_tag_for",
|
|
61
|
+
"run_jargon_test", "scan_and_test",
|
|
62
|
+
]
|
|
63
|
+
|
|
64
|
+
__version__ = "1.0.0"
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
"""The audit brief — the overlay's output artifact.
|
|
2
|
+
|
|
3
|
+
The brief records what was audited, which evasion hypotheses were tested
|
|
4
|
+
and their support status, and what seeds were handed off. It is a brief,
|
|
5
|
+
not an interrogation result: the interrogation engine's state artifact
|
|
6
|
+
stays canonical.
|
|
7
|
+
|
|
8
|
+
Stand-down rule, enforced in code: the brief must NEVER contain
|
|
9
|
+
collision, adjudication, refinement, or surprise sections. Those belong
|
|
10
|
+
to the interrogation engine. If the engine isn't running, the brief is
|
|
11
|
+
reported and the work stops.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
import re
|
|
17
|
+
from dataclasses import dataclass, field
|
|
18
|
+
from datetime import date
|
|
19
|
+
|
|
20
|
+
from .ground import GroundMap
|
|
21
|
+
from .hypotheses import HypothesisRecord, H_OBFUSCATION
|
|
22
|
+
|
|
23
|
+
# Headings the overlay must never emit. A brief that collides or
|
|
24
|
+
# adjudicates has left its lane — the schema rejects it.
|
|
25
|
+
FORBIDDEN_SECTIONS = (
|
|
26
|
+
"collision", "collide", "adjudicat", "refinement", "refine",
|
|
27
|
+
"surprise",
|
|
28
|
+
)
|
|
29
|
+
|
|
30
|
+
|
|
31
|
+
class BriefError(ValueError):
|
|
32
|
+
"""Raised when a brief breaks the schema or the stand-down rule."""
|
|
33
|
+
|
|
34
|
+
|
|
35
|
+
def check_stand_down(rendered: str) -> None:
|
|
36
|
+
"""Reject rendered text that contains engine-owned sections."""
|
|
37
|
+
headings = re.findall(r"^#{1,3}\s*(.+)$", rendered, re.MULTILINE)
|
|
38
|
+
for heading in headings:
|
|
39
|
+
lowered = heading.lower()
|
|
40
|
+
for forbidden in FORBIDDEN_SECTIONS:
|
|
41
|
+
if forbidden in lowered:
|
|
42
|
+
raise BriefError(
|
|
43
|
+
f"Stand-down violation: the brief must not contain a "
|
|
44
|
+
f"'{heading.strip()}' section. Collision, adjudication, "
|
|
45
|
+
"refinement, and surprise belong to the interrogation "
|
|
46
|
+
"engine — this overlay audits and hands off, nothing more."
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
@dataclass
|
|
51
|
+
class AuditBrief:
|
|
52
|
+
"""Audited ground + tested hypotheses + handoff seeds."""
|
|
53
|
+
|
|
54
|
+
case_name: str
|
|
55
|
+
ground: GroundMap = field(default_factory=GroundMap)
|
|
56
|
+
records: list[HypothesisRecord] = field(default_factory=list)
|
|
57
|
+
seeds: list[str] = field(default_factory=list)
|
|
58
|
+
lifecycle: str = "FINAL"
|
|
59
|
+
brief_date: str = field(default_factory=lambda: date.today().isoformat())
|
|
60
|
+
|
|
61
|
+
def __post_init__(self) -> None:
|
|
62
|
+
if not self.case_name.strip():
|
|
63
|
+
raise BriefError("Brief needs a case name.")
|
|
64
|
+
if self.lifecycle not in ("INITIAL", "ITERATIVE", "FINAL"):
|
|
65
|
+
raise BriefError(f"Bad lifecycle: {self.lifecycle!r}.")
|
|
66
|
+
if not (2 <= len(self.seeds) <= 3):
|
|
67
|
+
raise BriefError(
|
|
68
|
+
f"Handoff must produce 2-3 seed frames, got {len(self.seeds)}."
|
|
69
|
+
)
|
|
70
|
+
for seed in self.seeds:
|
|
71
|
+
if not seed.strip():
|
|
72
|
+
raise BriefError("Seed frames must not be empty.")
|
|
73
|
+
|
|
74
|
+
def obfuscation_supported(self) -> bool:
|
|
75
|
+
"""True only when some record's H-obfuscation is supported."""
|
|
76
|
+
return any(r.supports_obfuscation() for r in self.records)
|
|
77
|
+
|
|
78
|
+
def render(self) -> str:
|
|
79
|
+
lines = [
|
|
80
|
+
"---",
|
|
81
|
+
"artifact: audit-brief",
|
|
82
|
+
f"date: {self.brief_date}",
|
|
83
|
+
"protocol: fairit/1.0",
|
|
84
|
+
f"lifecycle: {self.lifecycle}",
|
|
85
|
+
"---",
|
|
86
|
+
"",
|
|
87
|
+
f"# Audit brief: {self.case_name}",
|
|
88
|
+
"",
|
|
89
|
+
self.ground.render(),
|
|
90
|
+
"",
|
|
91
|
+
"## Tested hypotheses",
|
|
92
|
+
]
|
|
93
|
+
for record in self.records:
|
|
94
|
+
lines.append("")
|
|
95
|
+
lines.append(record.summary())
|
|
96
|
+
lines.extend(["", "## Handoff seeds"])
|
|
97
|
+
for i, seed in enumerate(self.seeds, 1):
|
|
98
|
+
lines.append(f"{i}. {seed}")
|
|
99
|
+
rendered = "\n".join(lines)
|
|
100
|
+
check_stand_down(rendered)
|
|
101
|
+
return rendered
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def parse(rendered: str) -> AuditBrief:
|
|
105
|
+
"""Parse a rendered brief back into an AuditBrief.
|
|
106
|
+
|
|
107
|
+
Round-trip fidelity: case name, lifecycle, date, and seeds survive.
|
|
108
|
+
Ground and hypothesis records parse back as their raw text — the
|
|
109
|
+
structured re-validation is the caller's job on re-entry.
|
|
110
|
+
"""
|
|
111
|
+
check_stand_down(rendered)
|
|
112
|
+
front = dict(
|
|
113
|
+
line.split(":", 1)
|
|
114
|
+
for line in rendered.splitlines()
|
|
115
|
+
if re.match(r"^(artifact|date|protocol|lifecycle):", line)
|
|
116
|
+
)
|
|
117
|
+
if front.get("artifact", "").strip() != "audit-brief":
|
|
118
|
+
raise BriefError("Not an audit-brief artifact.")
|
|
119
|
+
match = re.search(r"^# Audit brief: (.+)$", rendered, re.MULTILINE)
|
|
120
|
+
if not match:
|
|
121
|
+
raise BriefError("Brief is missing its case-name heading.")
|
|
122
|
+
seed_lines: list[str] = []
|
|
123
|
+
if "## Handoff seeds" in rendered:
|
|
124
|
+
tail = rendered.split("## Handoff seeds", 1)[1]
|
|
125
|
+
seed_lines = [s.strip() for s in re.findall(r"^\d+\. (.+)$", tail, re.MULTILINE)]
|
|
126
|
+
return AuditBrief(
|
|
127
|
+
case_name=match.group(1).strip(),
|
|
128
|
+
lifecycle=front.get("lifecycle", "FINAL").strip() or "FINAL",
|
|
129
|
+
brief_date=front.get("date", "").strip() or date.today().isoformat(),
|
|
130
|
+
seeds=seed_lines,
|
|
131
|
+
)
|
|
@@ -0,0 +1,54 @@
|
|
|
1
|
+
"""Shield-vocabulary marker lists, by dialect.
|
|
2
|
+
|
|
3
|
+
These lists detect *candidate* instances of jargon that MAY function as
|
|
4
|
+
evasion. Detection is the start of the hypothesis protocol, never its
|
|
5
|
+
conclusion: a marker hit feeds H-obfuscation / H-genuine / H-both
|
|
6
|
+
testing in `hypotheses.py`. No marker, list, or dialect ever produces a
|
|
7
|
+
verdict by itself.
|
|
8
|
+
|
|
9
|
+
The original skill only knew therapy-speak. The same structure covers
|
|
10
|
+
corporate-speak and bureaucratic passive voice — the mechanism doesn't
|
|
11
|
+
care which dialect the shield is written in.
|
|
12
|
+
"""
|
|
13
|
+
|
|
14
|
+
from __future__ import annotations
|
|
15
|
+
|
|
16
|
+
# (dialect, phrases). Phrases are matched case-insensitively as
|
|
17
|
+
# substrings — they are tripwires, not judgments.
|
|
18
|
+
SHIELD_MARKERS: dict[str, tuple[str, ...]] = {
|
|
19
|
+
"therapy": (
|
|
20
|
+
"holding space", "doing the work", "inner child", "boundaries",
|
|
21
|
+
"triggered", "processing", "my truth", "self-care", "nervous system",
|
|
22
|
+
"regulation", "co-regulation", "shadow work", "attachment style",
|
|
23
|
+
"parts of me", "sitting with", "unpacking",
|
|
24
|
+
),
|
|
25
|
+
"corporate": (
|
|
26
|
+
"circle back", "synergy", "leverage", "bandwidth", "deep dive",
|
|
27
|
+
"move the needle", "take this offline", "low-hanging fruit",
|
|
28
|
+
"best practices", "thought leadership", "rightsizing",
|
|
29
|
+
"synergize", "actionable", "paradigm",
|
|
30
|
+
),
|
|
31
|
+
"bureaucratic": (
|
|
32
|
+
"mistakes were made", "regrettable", "inappropriate", "it was decided",
|
|
33
|
+
"processes were followed", "at this time", "going forward",
|
|
34
|
+
"lessons learned", "unfortunate", "miscommunication",
|
|
35
|
+
"administrative error", "procedural",
|
|
36
|
+
),
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
DIALECTS = tuple(SHIELD_MARKERS)
|
|
40
|
+
|
|
41
|
+
|
|
42
|
+
def detect_markers(text: str) -> list[tuple[str, str]]:
|
|
43
|
+
"""Find candidate shield-vocabulary instances.
|
|
44
|
+
|
|
45
|
+
Returns a list of (dialect, phrase) hits. A hit is a candidate for
|
|
46
|
+
hypothesis testing — it says "examine this," nothing more.
|
|
47
|
+
"""
|
|
48
|
+
lowered = text.lower()
|
|
49
|
+
hits: list[tuple[str, str]] = []
|
|
50
|
+
for dialect, phrases in SHIELD_MARKERS.items():
|
|
51
|
+
for phrase in phrases:
|
|
52
|
+
if phrase in lowered:
|
|
53
|
+
hits.append((dialect, phrase))
|
|
54
|
+
return hits
|