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.
Files changed (27) hide show
  1. faf_python_sdk-1.1.2/CHANGELOG.md +39 -0
  2. faf_python_sdk-1.1.2/CODE_OF_CONDUCT.md +21 -0
  3. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/PKG-INFO +9 -3
  4. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/README.md +7 -1
  5. faf_python_sdk-1.1.2/SECURITY.md +19 -0
  6. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/__init__.py +14 -11
  7. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/mk4.py +10 -8
  8. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/pyproject.toml +2 -2
  9. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_mk4.py +18 -18
  10. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/.github/workflows/pypi.yml +0 -0
  11. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/.gitignore +0 -0
  12. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/LICENSE +0 -0
  13. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/docs/GROK-INTEGRATION.md +0 -0
  14. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/docs/TECHNICAL-SPEC.md +0 -0
  15. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/examples/basic_usage.py +0 -0
  16. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/examples/grok_integration.py +0 -0
  17. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/discovery.py +0 -0
  18. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/parser.py +0 -0
  19. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/types.py +0 -0
  20. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/faf_sdk/validator.py +0 -0
  21. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/project.faf +0 -0
  22. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/__init__.py +0 -0
  23. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/stress_test.py +0 -0
  24. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_discovery.py +0 -0
  25. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_parser.py +0 -0
  26. {faf_python_sdk-1.1.0 → faf_python_sdk-1.1.2}/tests/test_validator.py +0 -0
  27. {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.0
4
- Summary: Python SDK for FAF (Foundational AI-context Format) - IANA-registered application/vnd.faf+yaml
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
- > **Python SDK for FAF** — parse, validate, and score `.faf` files with the Mk4 Championship Scoring Engine. The foundation for [gemini-faf-mcp](https://pypi.org/project/gemini-faf-mcp/).
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
  [![PyPI](https://img.shields.io/pypi/v/faf-python-sdk?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/faf-python-sdk/)
38
40
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](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
- > **Python SDK for FAF** — parse, validate, and score `.faf` files with the Mk4 Championship Scoring Engine. The foundation for [gemini-faf-mcp](https://pypi.org/project/gemini-faf-mcp/).
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
  [![PyPI](https://img.shields.io/pypi/v/faf-python-sdk?style=for-the-badge&logo=pypi&logoColor=white)](https://pypi.org/project/faf-python-sdk/)
6
8
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](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
- FAF Python SDK - Foundational AI-context Format
2
+ faf-python-sdk Persistent project context for Python.
3
3
 
4
- IANA-registered: application/vnd.faf+yaml
5
- https://faf.one
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 parse, validate, find_faf_file, FafFile
11
+ from faf_sdk import parse_file, score_faf
9
12
 
10
- # Parse a .faf file
11
- faf = parse(content)
13
+ faf = parse_file("project.faf")
14
+ print(faf.data.project.name)
12
15
 
13
- # Validate structure
14
- errors, warnings = validate(faf)
16
+ with open("project.faf") as f:
17
+ result = score_faf(f.read())
18
+ print(f"Score: {result.score}% {result.tier}")
15
19
 
16
- # Find project.faf in directory tree
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.0"
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 (mk4.rs lines 239-247)."""
201
+ """Mk4 official tier calculation aligned with faf-cli v6 tiers.ts."""
202
202
  if score >= 100:
203
- return "\U0001f3c6" # Trophy
203
+ return "TROPHY"
204
204
  if score >= 99:
205
- return "\U0001f947" # Gold
205
+ return "GOLD"
206
206
  if score >= 95:
207
- return "\U0001f948" # Silver
207
+ return "SILVER"
208
208
  if score >= 85:
209
- return "\U0001f949" # Bronze
209
+ return "BRONZE"
210
210
  if score >= 70:
211
- return "\U0001f7e2" # Green
211
+ return "GREEN"
212
212
  if score >= 55:
213
- return "\U0001f7e1" # Yellow
214
- return "\U0001f534" # Red
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.0"
8
- description = "Python SDK for FAF (Foundational AI-context Format) - IANA-registered application/vnd.faf+yaml"
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 == "\U0001f3c6" # Trophy
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 == "\U0001f3c6" # Trophy
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) == "\U0001f3c6"
353
+ assert _score_to_tier(100) == "TROPHY"
354
354
 
355
355
  def test_tier_gold_99(self):
356
- assert _score_to_tier(99) == "\U0001f947"
356
+ assert _score_to_tier(99) == "GOLD"
357
357
 
358
358
  def test_tier_silver_95(self):
359
- assert _score_to_tier(95) == "\U0001f948"
359
+ assert _score_to_tier(95) == "SILVER"
360
360
 
361
361
  def test_tier_bronze_85(self):
362
- assert _score_to_tier(85) == "\U0001f949"
362
+ assert _score_to_tier(85) == "BRONZE"
363
363
 
364
364
  def test_tier_green_70(self):
365
- assert _score_to_tier(70) == "\U0001f7e2"
365
+ assert _score_to_tier(70) == "GREEN"
366
366
 
367
367
  def test_tier_yellow_55(self):
368
- assert _score_to_tier(55) == "\U0001f7e1"
368
+ assert _score_to_tier(55) == "YELLOW"
369
369
 
370
370
  def test_tier_red_54(self):
371
- assert _score_to_tier(54) == "\U0001f534"
371
+ assert _score_to_tier(54) == "RED"
372
372
 
373
373
  def test_tier_red_0(self):
374
- assert _score_to_tier(0) == "\U0001f534"
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) == "\U0001f948"
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) == "\U0001f949"
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) == "\U0001f7e2"
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) == "\U0001f7e1"
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) == "\U0001f534"
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 == "\U0001f534" # Red
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 == "\U0001f7e2" # Green
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 == "\U0001f3c6" # Trophy
616
+ assert result.tier == "TROPHY"
File without changes