faf-python-sdk 1.1.0__tar.gz → 1.1.2__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.
- faf_python_sdk-1.1.2/CHANGELOG.md +39 -0
- faf_python_sdk-1.1.2/CODE_OF_CONDUCT.md +21 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/PKG-INFO +9 -3
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/README.md +7 -1
- faf_python_sdk-1.1.2/SECURITY.md +19 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/__init__.py +14 -11
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/mk4.py +10 -8
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/pyproject.toml +2 -2
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_mk4.py +18 -18
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/.github/workflows/pypi.yml +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/.gitignore +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/LICENSE +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/docs/GROK-INTEGRATION.md +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/docs/TECHNICAL-SPEC.md +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/examples/basic_usage.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/examples/grok_integration.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/discovery.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/parser.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/types.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/validator.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/project.faf +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/__init__.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/stress_test.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_discovery.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_parser.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_validator.py +0 -0
- {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_wjttc.py +0 -0
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
All notable changes to faf-python-sdk are documented here.
|
|
4
|
+
Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
|
|
5
|
+
|
|
6
|
+
## [1.1.2] - 2026-04-26
|
|
7
|
+
|
|
8
|
+
### Changed
|
|
9
|
+
- Package description aligned with the canonical "Persistent project context for Python" framing on PyPI catalog and GitHub repo metadata.
|
|
10
|
+
- README lede sharpened — leads with the value proposition and the audience (MCP server / CI validator / tool authors), no longer feature-list framing.
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
- `CHANGELOG.md` (this file) — versioned release history, separate from the README's "What's New" section.
|
|
14
|
+
- Brand mantra `FAF defines. MD instructs. AI codes.` anchored in the README and `__init__.py` module docstring.
|
|
15
|
+
|
|
16
|
+
### Notes
|
|
17
|
+
No runtime code changes. Patch release to surface description alignment in the PyPI catalog and tighten positioning copy. The catalog only updates on a new publish.
|
|
18
|
+
|
|
19
|
+
## [1.1.1] - 2026-04-18
|
|
20
|
+
|
|
21
|
+
### Fixed
|
|
22
|
+
- Tier alignment to match faf-cli v6 — clean geometric symbols, no emoji. v1.1.0 mistakenly returned emoji tiers; this patch normalizes them to plain uppercase strings (`TROPHY`, `GOLD`, `SILVER`, `BRONZE`, `GREEN`, `YELLOW`, `RED`).
|
|
23
|
+
|
|
24
|
+
## [1.1.0] - 2026-03-29
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- **Mk4 Championship Scoring Engine** — the same 33-slot scoring algorithm used by the Rust compiler and TypeScript CLI, now in Python. Same slots, same formula, same scores.
|
|
28
|
+
- `score_faf()` — Mk4 scoring with 21-slot Base or 33-slot Enterprise tiers.
|
|
29
|
+
- 100% parity with `faf-wasm-sdk` (Rust) and `faf-cli` (TypeScript).
|
|
30
|
+
- 88 new WJTTC championship-grade tests (concurrency, adversarial input, security).
|
|
31
|
+
- Total test count: 175 (was 87).
|
|
32
|
+
|
|
33
|
+
### Fixed
|
|
34
|
+
- 3 crash bugs in malformed YAML and null project field handling.
|
|
35
|
+
|
|
36
|
+
## [1.0.2] - earlier
|
|
37
|
+
|
|
38
|
+
### Added
|
|
39
|
+
- Initial public release with `parse`, `parse_file`, `stringify`, `validate`, `find_faf_file`, `find_project_root`, and the typed `FafData` model.
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# Code of Conduct
|
|
2
|
+
|
|
3
|
+
This project adopts the [Contributor Covenant, v2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/) as its code of conduct. The full text of that covenant governs behavior in this project and its community spaces.
|
|
4
|
+
|
|
5
|
+
## Summary
|
|
6
|
+
|
|
7
|
+
- We commit to maintaining a respectful, harassment-free environment for everyone who participates, regardless of background.
|
|
8
|
+
- Participation includes contributing code, opening issues, reviewing pull requests, and interacting in any official channel.
|
|
9
|
+
- The Contributor Covenant defines the full scope, expected conduct, and enforcement guidelines. See the link above for the complete text.
|
|
10
|
+
|
|
11
|
+
## Reporting
|
|
12
|
+
|
|
13
|
+
If you observe conduct that violates this Code of Conduct, please email **team@faf.one**. Reports are handled confidentially. Maintainers will review, respond, and take appropriate action consistent with the Contributor Covenant's enforcement guidelines.
|
|
14
|
+
|
|
15
|
+
## Scope
|
|
16
|
+
|
|
17
|
+
This Code of Conduct applies to all project spaces — the repository, issues, pull requests, discussions, and any public communication channels operated by the project. It also applies when representing the project in other forums.
|
|
18
|
+
|
|
19
|
+
## Attribution
|
|
20
|
+
|
|
21
|
+
This Code of Conduct adopts the [Contributor Covenant, version 2.1](https://www.contributor-covenant.org/version/2/1/code_of_conduct/), available under the CC BY 4.0 license.
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: faf-python-sdk
|
|
3
|
-
Version: 1.1.
|
|
4
|
-
Summary:
|
|
3
|
+
Version: 1.1.2
|
|
4
|
+
Summary: Persistent project context for Python — parse, validate, and score `.faf` files. The foundation other Python FAF tools (gemini-faf-mcp, custom MCP servers, CI validators) build on. IANA-registered application/vnd.faf+yaml.
|
|
5
5
|
Project-URL: Homepage, https://faf.one
|
|
6
6
|
Project-URL: Documentation, https://github.com/Wolfe-Jam/faf-python-sdk
|
|
7
7
|
Project-URL: Repository, https://github.com/Wolfe-Jam/faf-python-sdk
|
|
@@ -32,7 +32,9 @@ Description-Content-Type: text/markdown
|
|
|
32
32
|
|
|
33
33
|
# faf-python-sdk
|
|
34
34
|
|
|
35
|
-
|
|
35
|
+
**Persistent project context for Python.** Parse, validate, and score `.faf` files — the foundation other Python FAF tools build on.
|
|
36
|
+
|
|
37
|
+
If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
|
|
36
38
|
|
|
37
39
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
38
40
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
@@ -52,6 +54,8 @@ Description-Content-Type: text/markdown
|
|
|
52
54
|
|
|
53
55
|
**Why this matters:** If you're building on FAF in Python — MCP servers, Gemini extensions, CI pipelines — your scores now match every other FAF tool exactly. No more "it scored 85% in the CLI but 60% in Python." One engine, one truth.
|
|
54
56
|
|
|
57
|
+
> **v1.1.2** is a patch release — package description aligned with the canonical "Persistent project context for Python" framing. CHANGELOG.md added. No code changes.
|
|
58
|
+
|
|
55
59
|
## Installation
|
|
56
60
|
|
|
57
61
|
```bash
|
|
@@ -75,6 +79,8 @@ print(f"Score: {result.score}% {result.tier}")
|
|
|
75
79
|
print(f"Slots: {result.populated}/{result.total} populated")
|
|
76
80
|
```
|
|
77
81
|
|
|
82
|
+
> **FAF defines. MD instructs. AI codes.**
|
|
83
|
+
|
|
78
84
|
## Mk4 Scoring
|
|
79
85
|
|
|
80
86
|
The Mk4 engine scores `.faf` files by checking 21 universal slots (project metadata, human context, tech stack). Each slot is **Populated**, **Empty**, or **Slotignored**. The score is the percentage of active slots that are populated.
|
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
# faf-python-sdk
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Persistent project context for Python.** Parse, validate, and score `.faf` files — the foundation other Python FAF tools build on.
|
|
4
|
+
|
|
5
|
+
If you're building MCP servers, CI validators, or any Python tool that needs to understand project context, start here.
|
|
4
6
|
|
|
5
7
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
6
8
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
@@ -20,6 +22,8 @@
|
|
|
20
22
|
|
|
21
23
|
**Why this matters:** If you're building on FAF in Python — MCP servers, Gemini extensions, CI pipelines — your scores now match every other FAF tool exactly. No more "it scored 85% in the CLI but 60% in Python." One engine, one truth.
|
|
22
24
|
|
|
25
|
+
> **v1.1.2** is a patch release — package description aligned with the canonical "Persistent project context for Python" framing. CHANGELOG.md added. No code changes.
|
|
26
|
+
|
|
23
27
|
## Installation
|
|
24
28
|
|
|
25
29
|
```bash
|
|
@@ -43,6 +47,8 @@ print(f"Score: {result.score}% {result.tier}")
|
|
|
43
47
|
print(f"Slots: {result.populated}/{result.total} populated")
|
|
44
48
|
```
|
|
45
49
|
|
|
50
|
+
> **FAF defines. MD instructs. AI codes.**
|
|
51
|
+
|
|
46
52
|
## Mk4 Scoring
|
|
47
53
|
|
|
48
54
|
The Mk4 engine scores `.faf` files by checking 21 universal slots (project metadata, human context, tech stack). Each slot is **Populated**, **Empty**, or **Slotignored**. The score is the percentage of active slots that are populated.
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
# Security Policy
|
|
2
|
+
|
|
3
|
+
## Supported Versions
|
|
4
|
+
|
|
5
|
+
We provide security updates for the latest released version. For older versions, please upgrade to the current release.
|
|
6
|
+
|
|
7
|
+
## Reporting a Vulnerability
|
|
8
|
+
|
|
9
|
+
If you discover a security vulnerability, please email **security@faf.one** directly.
|
|
10
|
+
|
|
11
|
+
**Please do not open a public GitHub issue for security reports.** Responsible disclosure gives us time to fix the issue before it's widely known.
|
|
12
|
+
|
|
13
|
+
When reporting, please include:
|
|
14
|
+
- A description of the vulnerability
|
|
15
|
+
- Steps to reproduce
|
|
16
|
+
- Affected versions (if known)
|
|
17
|
+
- Any suggested fix (optional)
|
|
18
|
+
|
|
19
|
+
We aim to respond within 7 days.
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
"""
|
|
2
|
-
|
|
2
|
+
faf-python-sdk — Persistent project context for Python.
|
|
3
3
|
|
|
4
|
-
|
|
5
|
-
|
|
4
|
+
Parse, validate, and score `.faf` files. The foundation other Python
|
|
5
|
+
FAF tools (gemini-faf-mcp, custom MCP servers, CI validators) build on.
|
|
6
|
+
|
|
7
|
+
IANA-registered media type: application/vnd.faf+yaml
|
|
8
|
+
Site: https://faf.one
|
|
6
9
|
|
|
7
10
|
Usage:
|
|
8
|
-
from faf_sdk import
|
|
11
|
+
from faf_sdk import parse_file, score_faf
|
|
9
12
|
|
|
10
|
-
|
|
11
|
-
faf
|
|
13
|
+
faf = parse_file("project.faf")
|
|
14
|
+
print(faf.data.project.name)
|
|
12
15
|
|
|
13
|
-
|
|
14
|
-
|
|
16
|
+
with open("project.faf") as f:
|
|
17
|
+
result = score_faf(f.read())
|
|
18
|
+
print(f"Score: {result.score}% {result.tier}")
|
|
15
19
|
|
|
16
|
-
|
|
17
|
-
path = find_faf_file("/path/to/project")
|
|
20
|
+
FAF defines. MD instructs. AI codes.
|
|
18
21
|
"""
|
|
19
22
|
|
|
20
23
|
from .parser import parse, parse_file, stringify, FafFile
|
|
@@ -31,7 +34,7 @@ from .types import (
|
|
|
31
34
|
AIScoring
|
|
32
35
|
)
|
|
33
36
|
|
|
34
|
-
__version__ = "1.1.
|
|
37
|
+
__version__ = "1.1.2"
|
|
35
38
|
__all__ = [
|
|
36
39
|
# Parser
|
|
37
40
|
"parse",
|
|
@@ -198,17 +198,19 @@ def _is_valid_populated(s: str) -> bool:
|
|
|
198
198
|
|
|
199
199
|
|
|
200
200
|
def _score_to_tier(score: int) -> str:
|
|
201
|
-
"""Mk4 official tier calculation
|
|
201
|
+
"""Mk4 official tier calculation — aligned with faf-cli v6 tiers.ts."""
|
|
202
202
|
if score >= 100:
|
|
203
|
-
return "
|
|
203
|
+
return "TROPHY"
|
|
204
204
|
if score >= 99:
|
|
205
|
-
return "
|
|
205
|
+
return "GOLD"
|
|
206
206
|
if score >= 95:
|
|
207
|
-
return "
|
|
207
|
+
return "SILVER"
|
|
208
208
|
if score >= 85:
|
|
209
|
-
return "
|
|
209
|
+
return "BRONZE"
|
|
210
210
|
if score >= 70:
|
|
211
|
-
return "
|
|
211
|
+
return "GREEN"
|
|
212
212
|
if score >= 55:
|
|
213
|
-
return "
|
|
214
|
-
|
|
213
|
+
return "YELLOW"
|
|
214
|
+
if score >= 1:
|
|
215
|
+
return "RED"
|
|
216
|
+
return "WHITE"
|
|
@@ -4,8 +4,8 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "faf-python-sdk"
|
|
7
|
-
version = "1.1.
|
|
8
|
-
description = "
|
|
7
|
+
version = "1.1.2"
|
|
8
|
+
description = "Persistent project context for Python — parse, validate, and score `.faf` files. The foundation other Python FAF tools (gemini-faf-mcp, custom MCP servers, CI validators) build on. IANA-registered application/vnd.faf+yaml."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = {text = "MIT"}
|
|
11
11
|
authors = [
|
|
@@ -89,7 +89,7 @@ stack:
|
|
|
89
89
|
assert result.populated == 21
|
|
90
90
|
assert result.total == 21
|
|
91
91
|
assert result.score == 100
|
|
92
|
-
assert result.tier == "
|
|
92
|
+
assert result.tier == "TROPHY"
|
|
93
93
|
|
|
94
94
|
def test_enterprise_33_slots_total(self):
|
|
95
95
|
result = score_faf("empty: true", LicenseTier.ENTERPRISE)
|
|
@@ -134,7 +134,7 @@ human_context:
|
|
|
134
134
|
assert result.active == 17 # 21 - 4 = 17
|
|
135
135
|
assert result.populated == 17
|
|
136
136
|
assert result.score == 100 # 17/17 = 100%
|
|
137
|
-
assert result.tier == "
|
|
137
|
+
assert result.tier == "TROPHY"
|
|
138
138
|
|
|
139
139
|
def test_all_slotignored_scores_zero(self):
|
|
140
140
|
yaml_content = """
|
|
@@ -350,44 +350,44 @@ project:
|
|
|
350
350
|
|
|
351
351
|
class TestTiers:
|
|
352
352
|
def test_tier_trophy_100(self):
|
|
353
|
-
assert _score_to_tier(100) == "
|
|
353
|
+
assert _score_to_tier(100) == "TROPHY"
|
|
354
354
|
|
|
355
355
|
def test_tier_gold_99(self):
|
|
356
|
-
assert _score_to_tier(99) == "
|
|
356
|
+
assert _score_to_tier(99) == "GOLD"
|
|
357
357
|
|
|
358
358
|
def test_tier_silver_95(self):
|
|
359
|
-
assert _score_to_tier(95) == "
|
|
359
|
+
assert _score_to_tier(95) == "SILVER"
|
|
360
360
|
|
|
361
361
|
def test_tier_bronze_85(self):
|
|
362
|
-
assert _score_to_tier(85) == "
|
|
362
|
+
assert _score_to_tier(85) == "BRONZE"
|
|
363
363
|
|
|
364
364
|
def test_tier_green_70(self):
|
|
365
|
-
assert _score_to_tier(70) == "
|
|
365
|
+
assert _score_to_tier(70) == "GREEN"
|
|
366
366
|
|
|
367
367
|
def test_tier_yellow_55(self):
|
|
368
|
-
assert _score_to_tier(55) == "
|
|
368
|
+
assert _score_to_tier(55) == "YELLOW"
|
|
369
369
|
|
|
370
370
|
def test_tier_red_54(self):
|
|
371
|
-
assert _score_to_tier(54) == "
|
|
371
|
+
assert _score_to_tier(54) == "RED"
|
|
372
372
|
|
|
373
373
|
def test_tier_red_0(self):
|
|
374
|
-
assert _score_to_tier(0) == "
|
|
374
|
+
assert _score_to_tier(0) == "WHITE"
|
|
375
375
|
|
|
376
376
|
# Off-by-one boundaries
|
|
377
377
|
def test_tier_boundary_98_is_silver(self):
|
|
378
|
-
assert _score_to_tier(98) == "
|
|
378
|
+
assert _score_to_tier(98) == "SILVER"
|
|
379
379
|
|
|
380
380
|
def test_tier_boundary_94_is_bronze(self):
|
|
381
|
-
assert _score_to_tier(94) == "
|
|
381
|
+
assert _score_to_tier(94) == "BRONZE"
|
|
382
382
|
|
|
383
383
|
def test_tier_boundary_84_is_green(self):
|
|
384
|
-
assert _score_to_tier(84) == "
|
|
384
|
+
assert _score_to_tier(84) == "GREEN"
|
|
385
385
|
|
|
386
386
|
def test_tier_boundary_69_is_yellow(self):
|
|
387
|
-
assert _score_to_tier(69) == "
|
|
387
|
+
assert _score_to_tier(69) == "YELLOW"
|
|
388
388
|
|
|
389
389
|
def test_tier_boundary_54_is_red(self):
|
|
390
|
-
assert _score_to_tier(54) == "
|
|
390
|
+
assert _score_to_tier(54) == "RED"
|
|
391
391
|
|
|
392
392
|
|
|
393
393
|
# =========================================================================
|
|
@@ -530,7 +530,7 @@ stack:
|
|
|
530
530
|
result = score_faf(yaml_content)
|
|
531
531
|
assert result.populated == 11
|
|
532
532
|
assert result.score == 52 # 11/21 = 52.38 -> 52
|
|
533
|
-
assert result.tier == "
|
|
533
|
+
assert result.tier == "RED"
|
|
534
534
|
|
|
535
535
|
def test_mixed_enterprise_base_plus_some_enterprise(self):
|
|
536
536
|
yaml_content = """
|
|
@@ -567,7 +567,7 @@ monorepo:
|
|
|
567
567
|
assert result.populated == 24 # 21 base + 3 monorepo
|
|
568
568
|
assert result.total == 33
|
|
569
569
|
assert result.score == 73 # 24/33 = 72.7 -> 73
|
|
570
|
-
assert result.tier == "
|
|
570
|
+
assert result.tier == "GREEN"
|
|
571
571
|
|
|
572
572
|
def test_enterprise_full_33_slots_populated(self):
|
|
573
573
|
yaml_content = """
|
|
@@ -613,4 +613,4 @@ monorepo:
|
|
|
613
613
|
assert result.populated == 33
|
|
614
614
|
assert result.total == 33
|
|
615
615
|
assert result.score == 100
|
|
616
|
-
assert result.tier == "
|
|
616
|
+
assert result.tier == "TROPHY"
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|