faf-python-sdk 1.3.1__tar.gz → 2.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.
Files changed (165) hide show
  1. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.github/workflows/pypi.yml +8 -0
  2. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.github/workflows/testpypi.yml +7 -7
  3. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.gitignore +3 -0
  4. faf_python_sdk-2.0.0/CHANGELOG.md +174 -0
  5. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CLAUDE.md +2 -2
  6. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CONTRIBUTING.md +2 -1
  7. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/PKG-INFO +33 -22
  8. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/README.md +32 -21
  9. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/__init__.py +13 -5
  10. faf_python_sdk-2.0.0/faf_sdk/_kernel_yaml.py +493 -0
  11. faf_python_sdk-2.0.0/faf_sdk/_libyaml_scanner.py +874 -0
  12. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/interop.py +50 -5
  13. faf_python_sdk-2.0.0/faf_sdk/mk4.py +256 -0
  14. faf_python_sdk-2.0.0/project.faf +46 -0
  15. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/pyproject.toml +1 -1
  16. faf_python_sdk-2.0.0/scripts/gen_fuzz_cases.py +117 -0
  17. faf_python_sdk-2.0.0/scripts/kernel_oracle.js +27 -0
  18. faf_python_sdk-2.0.0/scripts/package-lock.json +23 -0
  19. faf_python_sdk-2.0.0/scripts/package.json +9 -0
  20. faf_python_sdk-2.0.0/scripts/record_kernel_expected.py +51 -0
  21. faf_python_sdk-2.0.0/tests/fixtures/corpus/agents-md-facts.faf +39 -0
  22. faf_python_sdk-2.0.0/tests/fixtures/corpus/bun-sticky-faf.faf +160 -0
  23. faf_python_sdk-2.0.0/tests/fixtures/corpus/bun-sticky-zig.faf +63 -0
  24. faf_python_sdk-2.0.0/tests/fixtures/corpus/claude-faf-mcp.faf +56 -0
  25. faf_python_sdk-2.0.0/tests/fixtures/corpus/claude-fafm-sdk.faf +46 -0
  26. faf_python_sdk-2.0.0/tests/fixtures/corpus/create-faf.faf +44 -0
  27. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-agent-public.faf +149 -0
  28. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-builder-public.faf +20 -0
  29. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-chrome-extension.faf +113 -0
  30. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-clawdbot.faf +41 -0
  31. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-cli.faf +101 -0
  32. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-crate.faf +65 -0
  33. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-enterprise-public.faf +340 -0
  34. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-foundation-site.faf +41 -0
  35. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-grid.faf +39 -0
  36. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-mcp-rig.faf +51 -0
  37. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-mcp.faf +59 -0
  38. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-memory-mcp.faf +53 -0
  39. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-moltbot.faf +60 -0
  40. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-one-svelte-new.faf +39 -0
  41. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-radio-public.faf +122 -0
  42. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-radio-rust.faf +54 -0
  43. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-rust.faf +48 -0
  44. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-server-card-ref.faf +54 -0
  45. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-skills-site.faf +39 -0
  46. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-skills.faf +38 -0
  47. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-taf-demo.faf +14 -0
  48. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-taf-git.faf +255 -0
  49. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-voice-public.faf +119 -0
  50. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-vscode.faf +57 -0
  51. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-wasm-core.faf +46 -0
  52. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-wasm-gen.faf +49 -0
  53. faf_python_sdk-2.0.0/tests/fixtures/corpus/faf.faf +46 -0
  54. faf_python_sdk-2.0.0/tests/fixtures/corpus/fafipedia.faf +41 -0
  55. faf_python_sdk-2.0.0/tests/fixtures/corpus/gemini-faf-mcp.faf +200 -0
  56. faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-elite.faf +119 -0
  57. faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-mcp.faf +57 -0
  58. faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-plugin.faf +41 -0
  59. faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-voice.faf +49 -0
  60. faf_python_sdk-2.0.0/tests/fixtures/corpus/hello-faf.faf +82 -0
  61. faf_python_sdk-2.0.0/tests/fixtures/corpus/homebrew-faf.faf +41 -0
  62. faf_python_sdk-2.0.0/tests/fixtures/corpus/mcp-context-card.faf +28 -0
  63. faf_python_sdk-2.0.0/tests/fixtures/corpus/mcp-project-context.faf +34 -0
  64. faf_python_sdk-2.0.0/tests/fixtures/corpus/mcpaas-cf.faf +45 -0
  65. faf_python_sdk-2.0.0/tests/fixtures/corpus/mcpaas-sdk.faf +39 -0
  66. faf_python_sdk-2.0.0/tests/fixtures/corpus/rust-faf-mcp.faf +75 -0
  67. faf_python_sdk-2.0.0/tests/fixtures/corpus/slash-nextjs.faf +85 -0
  68. faf_python_sdk-2.0.0/tests/fixtures/corpus/slash-tokens.faf +40 -0
  69. faf_python_sdk-2.0.0/tests/fixtures/corpus/v0-zero-faf-builder.faf +3 -0
  70. faf_python_sdk-2.0.0/tests/fixtures/corpus/wjttc-public.faf +159 -0
  71. faf_python_sdk-2.0.0/tests/fixtures/corpus/wolfejam-dev.faf +118 -0
  72. faf_python_sdk-2.0.0/tests/fixtures/corpus/wolfejames-com.faf +118 -0
  73. faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-rag-public.faf +21 -0
  74. faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-rust-public.faf +98 -0
  75. faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-zeph.faf +49 -0
  76. faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-zig-public.faf +74 -0
  77. faf_python_sdk-2.0.0/tests/fixtures/corpus/zero-faf-builder.faf +50 -0
  78. faf_python_sdk-2.0.0/tests/fixtures/edge/all21-base-slotignored.faf +25 -0
  79. faf_python_sdk-2.0.0/tests/fixtures/edge/all33-slotignored.faf +38 -0
  80. faf_python_sdk-2.0.0/tests/fixtures/edge/anchors-and-aliases.faf +5 -0
  81. faf_python_sdk-2.0.0/tests/fixtures/edge/app-type-is-not-read.faf +4 -0
  82. faf_python_sdk-2.0.0/tests/fixtures/edge/base21-legacy-keys-12-markers.faf +38 -0
  83. faf_python_sdk-2.0.0/tests/fixtures/edge/base21-legacy-keys-no-markers.faf +25 -0
  84. faf_python_sdk-2.0.0/tests/fixtures/edge/base21-short-keys-12-markers.faf +38 -0
  85. faf_python_sdk-2.0.0/tests/fixtures/edge/base21-short-keys-no-markers.faf +25 -0
  86. faf_python_sdk-2.0.0/tests/fixtures/edge/bom.faf +3 -0
  87. faf_python_sdk-2.0.0/tests/fixtures/edge/comments-only.faf +2 -0
  88. faf_python_sdk-2.0.0/tests/fixtures/edge/crlf.faf +3 -0
  89. faf_python_sdk-2.0.0/tests/fixtures/edge/document-markers.faf +5 -0
  90. faf_python_sdk-2.0.0/tests/fixtures/edge/empty-file.faf +0 -0
  91. faf_python_sdk-2.0.0/tests/fixtures/edge/empty-values.faf +12 -0
  92. faf_python_sdk-2.0.0/tests/fixtures/edge/enterprise-partial.faf +29 -0
  93. faf_python_sdk-2.0.0/tests/fixtures/edge/flow-style-full.faf +1 -0
  94. faf_python_sdk-2.0.0/tests/fixtures/edge/flow-style.faf +1 -0
  95. faf_python_sdk-2.0.0/tests/fixtures/edge/full33.faf +38 -0
  96. faf_python_sdk-2.0.0/tests/fixtures/edge/legacy-keys-only.faf +10 -0
  97. faf_python_sdk-2.0.0/tests/fixtures/edge/local-tag-is-empty.faf +7 -0
  98. faf_python_sdk-2.0.0/tests/fixtures/edge/merge-key-not-applied.faf +7 -0
  99. faf_python_sdk-2.0.0/tests/fixtures/edge/nested-values.faf +11 -0
  100. faf_python_sdk-2.0.0/tests/fixtures/edge/numbers-and-bools.faf +12 -0
  101. faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-all.faf +19 -0
  102. faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-tbd-todo.faf +12 -0
  103. faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-whitespace.faf +10 -0
  104. faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-1-of-8.faf +31 -0
  105. faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-3-of-8.faf +33 -0
  106. faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-5-of-8.faf +35 -0
  107. faf_python_sdk-2.0.0/tests/fixtures/edge/section-not-a-mapping.faf +4 -0
  108. faf_python_sdk-2.0.0/tests/fixtures/edge/short-keys.faf +10 -0
  109. faf_python_sdk-2.0.0/tests/fixtures/edge/short-vs-legacy-precedence.faf +16 -0
  110. faf_python_sdk-2.0.0/tests/fixtures/edge/slotignored-exact.faf +8 -0
  111. faf_python_sdk-2.0.0/tests/fixtures/edge/tabs-in-values.faf +4 -0
  112. faf_python_sdk-2.0.0/tests/fixtures/edge/tier-32-of-33.faf +37 -0
  113. faf_python_sdk-2.0.0/tests/fixtures/edge/tier-bronze.faf +35 -0
  114. faf_python_sdk-2.0.0/tests/fixtures/edge/tier-green.faf +32 -0
  115. faf_python_sdk-2.0.0/tests/fixtures/edge/tier-silver.faf +37 -0
  116. faf_python_sdk-2.0.0/tests/fixtures/edge/tier-yellow.faf +28 -0
  117. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-128bit-int.faf +2 -0
  118. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-alias-bomb.faf +7 -0
  119. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-bad-escape.faf +2 -0
  120. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-control-char.faf +2 -0
  121. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-duplicate-key.faf +3 -0
  122. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-duplicate-section.faf +4 -0
  123. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-flow-colon.faf +1 -0
  124. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-int-tag-mismatch.faf +2 -0
  125. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-null-tag-mismatch.faf +2 -0
  126. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-syntax.faf +2 -0
  127. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-tab-indent.faf +2 -0
  128. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-too-deep.faf +2 -0
  129. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-two-documents.faf +5 -0
  130. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-unknown-anchor.faf +2 -0
  131. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-unknown-directive.faf +4 -0
  132. faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-yaml-1.3.faf +4 -0
  133. faf_python_sdk-2.0.0/tests/fixtures/edge_cases.json +274 -0
  134. faf_python_sdk-2.0.0/tests/fixtures/fuzz_cases.json +462 -0
  135. faf_python_sdk-2.0.0/tests/fixtures/kernel_expected.json +848 -0
  136. faf_python_sdk-2.0.0/tests/parity_support.py +118 -0
  137. faf_python_sdk-2.0.0/tests/test_always33_parity.py +126 -0
  138. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_interop.py +48 -17
  139. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_mk4.py +92 -23
  140. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_wjttc.py +4 -4
  141. faf_python_sdk-1.3.1/CHANGELOG.md +0 -87
  142. faf_python_sdk-1.3.1/faf_sdk/mk4.py +0 -216
  143. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CODE_OF_CONDUCT.md +0 -0
  144. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/LICENSE +0 -0
  145. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/SECURITY.md +0 -0
  146. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/docs/GROK-INTEGRATION.md +0 -0
  147. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/docs/TECHNICAL-SPEC.md +0 -0
  148. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/examples/basic_usage.py +0 -0
  149. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/examples/grok_integration.py +0 -0
  150. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/dart_detection.json +0 -0
  151. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/detect.py +0 -0
  152. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/discovery.py +0 -0
  153. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/parser.py +0 -0
  154. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/py.typed +0 -0
  155. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/types.py +0 -0
  156. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/validator.py +0 -0
  157. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/scripts/sync-dart-spec.sh +0 -0
  158. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/__init__.py +0 -0
  159. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/dart_parity_fixtures.json +0 -0
  160. /faf_python_sdk-1.3.1/project.faf → /faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-python-sdk.faf +0 -0
  161. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/stress_test.py +0 -0
  162. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_dart_parity.py +0 -0
  163. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_discovery.py +0 -0
  164. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_parser.py +0 -0
  165. {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_validator.py +0 -0
@@ -10,5 +10,13 @@ jobs:
10
10
  - uses: actions/setup-python@v5
11
11
  with:
12
12
  python-version: "3.11"
13
+ # Gate: the full suite, with the live faf-kernel oracle required, passes
14
+ # before anything is built or published.
15
+ - uses: actions/setup-node@v4
16
+ with:
17
+ node-version: "22"
18
+ - run: npm install --prefix scripts
19
+ - run: pip install -e ".[dev]"
20
+ - run: FAF_REQUIRE_KERNEL=1 python -m pytest tests/ -q
13
21
  - run: pip install build && python -m build
14
22
  - uses: pypa/gh-action-pypi-publish@release/v1
@@ -1,13 +1,13 @@
1
1
  name: Publish to TestPyPI
2
2
 
3
- # Fires on every version tag push (e.g. `git push origin v0.1.5`).
4
- # Stage 1 of the publish flow: stage to TestPyPI for verification BEFORE
5
- # creating a GitHub Release (which then fires pypi.yml for production).
3
+ # Fires on every version tag push. Stage with a pre-release tag (e.g.
4
+ # `v2.1.0rc1` on a version-only commit), so the real version is never burned on
5
+ # TestPyPI before review.
6
6
  #
7
- # Flow:
8
- # 1. git tag v0.1.5 && git push origin v0.1.5 → this workflow runs
9
- # 2. verify install from TestPyPI → manual / via verify_install.py
10
- # 3. gh release create v0.1.5 → triggers pypi.yml (production)
7
+ # Flow (/pubpypi):
8
+ # 1. tag vX.Y.ZrcN + push → this workflow (TestPyPI)
9
+ # 2. verify install from TestPyPI, independent review
10
+ # 3. on GO: tag vX.Y.Z on main, then run pypi.yml (workflow_dispatch) from that tag
11
11
 
12
12
  on:
13
13
  push:
@@ -42,3 +42,6 @@ htmlcov/
42
42
  # OS
43
43
  .DS_Store
44
44
  Thumbs.db
45
+
46
+ # Node (kernel oracle for the parity harness)
47
+ node_modules/
@@ -0,0 +1,174 @@
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
+ ## [2.0.0] - 2026-09-28 — The Always33 Edition
7
+
8
+ One engine, one number: faf-python-sdk scores all 33 slots exactly like faf-kernel — the same score faf-cli 8, claude-faf-mcp 7, faf-mcp 4 and grok-faf-mcp 2 give.
9
+
10
+ `score_faf(yaml)` returns what faf-kernel (Wolfe-Jam/faf-rust `crates/faf-kernel`,
11
+ npm `faf-scoring-kernel@3.0.0`) returns, on every file. A parity harness
12
+ (`tests/test_always33_parity.py`) checks score, tier, populated, empty, ignored,
13
+ active, total and every slot state against the kernel's recorded answers: 845/845
14
+ fixtures (58 real `project.faf` files from public Wolfe-Jam repos, 55 rule
15
+ fixtures, 272 YAML-layer cases, 460 seeded fuzz documents).
16
+
17
+ ### Changed (breaking — scores move)
18
+ - **Always 33 slots.** The 12 enterprise slots count unless the file marks them
19
+ `slotignored`; `slotignored` slots drop out of the denominator
20
+ (`active = 33 − ignored`). A file with the 21 base slots filled and no markers
21
+ scores 64% (21/33); the same file plus the 12 markers scores 100% (21/21).
22
+ - `score_faf(yaml, tier=...)` still accepts `tier`, but it no longer changes the
23
+ slot count: `LicenseTier.BASE` and `LicenseTier.ENTERPRISE` give the same result.
24
+ - `Mk4Result.slots` (and `to_dict()["slots"]`) always lists 33 slots, in kernel
25
+ order, under the kernel's canonical names: `stack.framework`, `stack.css`,
26
+ `stack.state`, `stack.api`, `stack.db`, `stack.pkg_manager` (were
27
+ `stack.frontend`, `stack.css_framework`, `stack.state_management`,
28
+ `stack.api_type`, `stack.database`, `stack.package_manager`). `Mk4Result` keeps
29
+ its fields.
30
+ - `tbd` and `todo` (case-insensitive) are placeholders and score as empty, with
31
+ the existing list (`none`, `null`, `n/a`, `unknown`, `not applicable`, …).
32
+ - Short keys are read: `stack.framework`, `css`, `state`, `api`, `db`,
33
+ `pkg_manager`. The legacy key is read only when the short key is empty.
34
+ - Rounding is half away from zero, as the kernel does (1/8 = 12.5% → 13, where
35
+ Python's `round()` gave 12).
36
+ - YAML is read the way the kernel reads it (serde_yaml_ng on libyaml), not with
37
+ `yaml.safe_load`: flow-style YAML, a leading BOM and tabs inside values parse
38
+ as they do in the kernel; `<<` is an ordinary key (no YAML 1.1 merge); a
39
+ redefined anchor name resolves the way the kernel resolves it (an alias
40
+ follows the kernel's anchor ids, so it can land on a later anchor); duplicate
41
+ keys, a second document, an empty `?` key inside `[...]` (`[?, a]`, `[?]`),
42
+ alias expansion past the kernel's repetition limit and nesting deeper than
43
+ 128 are unreadable, and nesting past that limit (`[`/`{` or block) is rejected
44
+ in linear time; a local tag (`!x value`) scores the slot empty.
45
+ - Unreadable YAML scores 0 with every slot empty. `score_faf` does not raise for
46
+ any `str` or `bytes` input.
47
+
48
+ ### Added
49
+ - `faf_sdk.mk4.SLOTS` (the 33 slot paths, kernel order), `TOTAL_SLOTS`,
50
+ `LEGACY_ALIASES`.
51
+ - Parity harness and fixtures under `tests/fixtures/`; the kernel oracle
52
+ (`scripts/kernel_oracle.js`, pinned in `scripts/package.json`) and
53
+ `scripts/record_kernel_expected.py`, which records the kernel's answers.
54
+
55
+ ### Upgrading
56
+ - Files without the 12 enterprise markers score lower. `faf auto` (faf-cli)
57
+ writes the 12 markers; with them, a complete 21-slot file scores 100%.
58
+ - Code that reads `result.slots` by name: use the canonical short names above.
59
+ - YAML the kernel can't read now scores 0: a duplicate key (1.4.0 kept the last
60
+ value), a second document (`---`), an empty `?` key inside `[...]`, nesting
61
+ deeper than 128. `tbd` and `todo` now count as empty.
62
+ - Rounding is half away from zero (12.5% → 13), as the kernel rounds.
63
+
64
+ ### Notes
65
+ - `generate_agents_md` / `generate_gemini_md` remain deprecated aliases in 2.0.0.
66
+ - `validate()`'s completeness score is unchanged; it is not the Mk4 score.
67
+
68
+ ## [1.4.0] - 2026-09-08 — The Interop Edition
69
+
70
+ The interop functions get their real names: `author_agents_md` / `author_gemini_md` are public, `render_*` is the impl, `generate_*` is deprecated (removed in 2.0).
71
+
72
+ 1.3.1 kept `generate_*` as "internal identifiers" — but a name that surfaces
73
+ reflexively when reasoning about the tool lands in `tools/list` descriptions,
74
+ host UIs, and docs. "Internal-only" holds only until someone documents it, so
75
+ the public name has to be right now.
76
+
77
+ ### Added
78
+ - `author_agents_md(faf)` / `author_gemini_md(faf)` — the public API names, in
79
+ the "faf authors" voice used on every documented surface. This is what
80
+ `faf_sdk.interop.__all__` advertises.
81
+ - `render_agents_md(faf)` / `render_gemini_md(faf)` — the impl names: pure
82
+ `dict -> str` projection, pairs with the `write_*` helpers, greps clean. Same
83
+ split the canonical faf-cli TS uses.
84
+
85
+ ### Deprecated
86
+ - `generate_agents_md` / `generate_gemini_md` — still work, now emit
87
+ `DeprecationWarning`, removed in 2.0. Point at `author_*`.
88
+
89
+ ### Notes
90
+ - Additive + back-compatible: existing `from faf_sdk import generate_agents_md`
91
+ keeps working. No output change — the authored Markdown is byte-identical.
92
+
93
+ ## [1.3.1] - 2026-09-07 — The Interop Edition
94
+
95
+ Docs / copy patch — no API change.
96
+
97
+ ### Fixed
98
+ - "generate" removed from external copy: the `interop.py` module docstring, the
99
+ `## Setup & build` blockquote written into every AGENTS.md ("regenerated each
100
+ time" → "do not edit the managed block"), README, and CHANGELOG. FAF authors
101
+ and delivers facts. The `generate_agents_md` / `generate_gemini_md` function
102
+ names are unchanged (internal identifiers, TS↔Python parity).
103
+
104
+ ## [1.3.0] - 2026-09-06 — The Interop Edition
105
+
106
+ The SDK can now author AI-context files, not just parse and score them.
107
+
108
+ ### Added
109
+ - `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`,
110
+ Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, kept in
111
+ parity with the canonical TypeScript. Deterministic BETTER-shaped projection:
112
+ `## Setup & build` (install→build→dev ordered) · `## Run the tests` ·
113
+ `## Where things live` · `## Conventions` · three-tier `## Guardrails` ·
114
+ `## Definition of Done` · `## When stuck` · `## Security & secrets` ·
115
+ `## Commit & PR` · `## Stack`. Human Context (who/why marketing) is
116
+ intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA.
117
+ - `faf_meta_tag(faf)`, `title_label(key)`, `slot_label(path)` — the shared
118
+ label + metastamp helpers, also from `src/interop`.
119
+ - Both take the **raw parsed dict** (`FafFile.data.raw`) — the .faf
120
+ format carries top-level `commands` / `key_files` / `security` that the typed
121
+ model doesn't surface.
122
+ - 17 tests, including deterministic-output and human-context-omission guards.
123
+
124
+ ### Fixed
125
+ - `[tool.mypy] python_version` was `"3.9"` — rejected by modern mypy
126
+ (`must be 3.10 or higher`). Set to `"3.10"`.
127
+
128
+ ## [1.2.0] - 2026-06-16 — The Dart Edition
129
+
130
+ Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
131
+
132
+ ### Added
133
+ - `detect_dart_project(dir)` → `DartProject` — the SDK's first detection capability. Reads `pubspec.yaml` and classifies: Flutter app vs reusable package, Dart MCP server, Dart backend (Serverpod / Dart Frog / Shelf / …), Dart CLI, or library. Exported from `faf_sdk`.
134
+ - `faf_sdk/dart_detection.json` — the detection KNOWLEDGE spec, vendored byte-identical from faf-cli (the single source); ships in the wheel, loaded at runtime.
135
+ - `tests/test_dart_parity.py` — 20 shared fixtures run identically by faf-cli and this SDK; parity proven by test, not by eye.
136
+ - `scripts/sync-dart-spec.sh` — vendor + `--check` (byte-identity) the spec & fixtures from faf-cli.
137
+
138
+ ### Notes
139
+ - Mirrors faf-cli `src/detect/dart.ts` exactly (A+B hybrid). To bolster Dart support, edit the spec in faf-cli (the Truth) and re-sync. No new runtime dependencies.
140
+
141
+ ## [1.1.2] - 2026-04-26
142
+
143
+ ### Changed
144
+ - Package description aligned with the canonical "Persistent project context for Python" framing on PyPI catalog and GitHub repo metadata.
145
+ - README lede sharpened — leads with the value proposition and the audience (MCP server / CI validator / tool authors), no longer feature-list framing.
146
+
147
+ ### Added
148
+ - `CHANGELOG.md` (this file) — versioned release history, separate from the README's "What's New" section.
149
+ - Brand mantra `FAF defines. MD instructs. AI codes.` anchored in the README and `__init__.py` module docstring.
150
+
151
+ ### Notes
152
+ 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.
153
+
154
+ ## [1.1.1] - 2026-04-18
155
+
156
+ ### Fixed
157
+ - 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`).
158
+
159
+ ## [1.1.0] - 2026-03-29
160
+
161
+ ### Added
162
+ - **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.
163
+ - `score_faf()` — Mk4 scoring with 21-slot Base or 33-slot Enterprise tiers.
164
+ - 100% parity with `faf-wasm-sdk` (Rust) and `faf-cli` (TypeScript).
165
+ - 88 new WJTTC championship-grade tests (concurrency, adversarial input, security).
166
+ - Total test count: 175 (was 87).
167
+
168
+ ### Fixed
169
+ - 3 crash bugs in malformed YAML and null project field handling.
170
+
171
+ ## [1.0.2] - earlier
172
+
173
+ ### Added
174
+ - Initial public release with `parse`, `parse_file`, `stringify`, `validate`, `find_faf_file`, `find_project_root`, and the typed `FafData` model.
@@ -1,5 +1,5 @@
1
1
  <!-- faf:start -->
2
- <!-- faf: faf-python-sdk | Python | | Python SDK for parsing, validating, scoring, and authoring .faf project context — the Mk4 engine + interop generators other Python FAF tools build on -->
2
+ <!-- faf: faf-python-sdk | Python | library | Python SDK for parsing, validating, scoring, and authoring .faf project context — the Mk4 engine + interop generators other Python FAF tools build on -->
3
3
  <!-- faf: claim=project.faf | family=FAF -->
4
4
 
5
5
  # CLAUDE.md — faf-python-sdk
@@ -29,5 +29,5 @@ Python SDK for parsing, validating, scoring, and authoring .faf project context
29
29
 
30
30
  ---
31
31
 
32
- *STATUS: BI-SYNC ACTIVE — 2026-09-07T02:08:39.970Z*
32
+ *STATUS: SYNC ACTIVE — 2026-09-28T17:59:27.771Z*
33
33
  <!-- faf:end -->
@@ -26,7 +26,8 @@ That's it. You're ready to run tests and ship a fix.
26
26
  Run the full check pass:
27
27
 
28
28
  ```bash
29
- pytest tests/ -v # all tests must pass
29
+ npm install --prefix scripts # the faf-kernel oracle (once)
30
+ FAF_REQUIRE_KERNEL=1 pytest tests/ -v # all tests must pass, incl. the live-kernel check
30
31
  mypy faf_sdk/ # strict typing — no untyped defs
31
32
  ```
32
33
 
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: faf-python-sdk
3
- Version: 1.3.1
3
+ Version: 2.0.0
4
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
@@ -41,29 +41,42 @@ The foundation other Python FAF tools build on. If you're building MCP servers,
41
41
  [![FAF](https://mcpaas.live/badge/Wolfe-Jam/faf-python-sdk.svg)](https://builder.faf.one)
42
42
  [![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/)
43
43
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](https://pypi.org/project/faf-python-sdk/)
44
- [![Tests](https://img.shields.io/badge/tests-213%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
44
+ [![Tests](https://img.shields.io/badge/tests-1070%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
45
45
  [![IANA](https://img.shields.io/badge/IANA-registered-informational?style=for-the-badge)](https://www.iana.org/assignments/media-types/application/vnd.faf+yaml)
46
46
 
47
47
  **Media Type:** `application/vnd.faf+yaml` (IANA registered)
48
48
 
49
- ## What's New in v1.3.1 — The Interop Edition
49
+ ## What's New in v2.0.0 — The Always33 Edition
50
50
 
51
- The SDK can now author AI-context files, not just parse and score them.
51
+ One engine, one number: faf-python-sdk scores all 33 slots exactly like faf-kernel — the same score faf-cli 8, claude-faf-mcp 7, faf-mcp 4 and grok-faf-mcp 2 give.
52
52
 
53
- > **v1.3.1** is a copy patch — "generate" removed from external text (module docstring, the blockquote written into every AGENTS.md, README, CHANGELOG). No API change; the `faf_sdk.interop` functions below are unchanged.
53
+ - **Always 33 slots.** The 12 enterprise slots count unless marked `slotignored`. 21 base slots filled with no markers: 64% (21/33). The same file plus the 12 markers: 100% (21/21). `faf auto` (faf-cli) writes the markers.
54
+ - `tbd` / `todo` are placeholders; short keys (`framework`, `css`, `state`, `api`, `db`, `pkg_manager`) are read.
55
+ - YAML is read the way the kernel reads it; unreadable YAML scores 0 without raising.
56
+ - `score_faf(yaml, tier=...)` still accepts `tier`; it no longer changes the slot count.
57
+ - **Upgrading:** `result.slots` lists all 33 slots under the kernel's names (`stack.framework`, `css`, `state`, `api`, `db`, `pkg_manager`; were `frontend`, `css_framework`, `state_management`, `api_type`, `database`, `package_manager`). Code that reads slots by name needs the new names. YAML the kernel can't read now scores 0 (a duplicate key, a second document, nesting deeper than 128), and rounding is half away from zero (12.5% → 13), as the kernel does.
58
+ - Parity harness: 845/845 fixtures match faf-kernel (`faf-scoring-kernel@3.0.0`), including the `project.faf` of 58 public repos.
54
59
 
55
- `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
60
+ See [CHANGELOG.md](CHANGELOG.md) for the full list of changes.
61
+
62
+ ## v1.4.0 — The Interop Edition
63
+
64
+ The interop functions get their real names: `author_agents_md` / `author_gemini_md` are public, `render_*` is the impl, `generate_*` is deprecated (removed in 2.0).
65
+
66
+ Output is byte-identical — a naming change, not a behaviour change. Existing `from faf_sdk import generate_agents_md` keeps working, now with a `DeprecationWarning`.
67
+
68
+ `faf_sdk.interop` — `author_agents_md(faf)` and `author_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
56
69
 
57
70
  ```python
58
- from faf_sdk import parse_file, generate_agents_md
71
+ from faf_sdk import parse_file, author_agents_md
59
72
 
60
73
  faf = parse_file("project.faf")
61
- print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
74
+ print(author_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
62
75
  ```
63
76
 
64
77
  Any Python FAF tool that authors an AI-context file wraps this now — never hand-roll one. `gemini-faf-mcp` 2.7.0's `faf_agents` / `faf_gemini` are the reference wrappers.
65
78
 
66
- ## What's New in v1.2.0 — The Dart Edition
79
+ ## v1.2.0 — The Dart Edition
67
80
 
68
81
  Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
69
82
 
@@ -74,7 +87,7 @@ d = detect_dart_project(".")
74
87
  print(d.app_type, d.framework) # e.g. "mobile" "Flutter"
75
88
  ```
76
89
 
77
- ## What's New in v1.1.0
90
+ ## v1.1.0
78
91
 
79
92
  **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. Every FAF tool in every language now agrees on what 100% means.
80
93
 
@@ -114,26 +127,24 @@ print(f"Slots: {result.populated}/{result.total} populated")
114
127
 
115
128
  ## Mk4 Scoring
116
129
 
117
- 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.
130
+ The Mk4 engine scores `.faf` files against 33 slots (project metadata, human context, tech stack, and 12 enterprise slots), exactly as faf-kernel does. Each slot is **Populated**, **Empty**, or **Slotignored**. The score is populated ÷ active, where active = 33 − slotignored.
118
131
 
119
132
  ```python
120
- from faf_sdk import score_faf, LicenseTier
133
+ from faf_sdk import score_faf
121
134
 
122
- # Base scoring (21 slots)
123
135
  result = score_faf(yaml_content)
124
136
  print(result.score) # 0-100
125
- print(result.tier) # Trophy/Gold/Silver/Bronze/Green/Yellow/Red
137
+ print(result.tier) # TROPHY / GOLD / SILVER / BRONZE / GREEN / YELLOW / RED / WHITE
126
138
  print(result.populated) # slots with real data
127
- print(result.active) # total minus slotignored
128
- print(result.slots) # per-slot breakdown
129
-
130
- # Enterprise scoring (33 slots — adds monorepo/infra)
131
- result = score_faf(yaml_content, LicenseTier.ENTERPRISE)
139
+ print(result.active) # 33 minus slotignored
140
+ print(result.slots) # per-slot breakdown, 33 entries in kernel order
132
141
  ```
133
142
 
134
- **Placeholder rejection:** Values like `"null"`, `"unknown"`, `"n/a"`, `"Describe your project goal"` are detected and scored as Empty — not Populated.
143
+ **Placeholder rejection:** Values like `"null"`, `"none"`, `"unknown"`, `"n/a"`, `"tbd"`, `"todo"`, `"Describe your project goal"` (case-insensitive) are scored as Empty — not Populated.
144
+
145
+ **Slotignored:** Set any slot to `slotignored` to exclude it from scoring. A project that does not use the 12 enterprise slots marks them `slotignored` (`faf auto` writes them) and can still reach 100%.
135
146
 
136
- **Slotignored:** Set any slot to `slotignored` to exclude it from scoring. A backend-only project can mark `frontend: slotignored` and still reach 100%.
147
+ **Short keys:** `stack.framework`, `css`, `state`, `api`, `db`, `pkg_manager` are the canonical names; the legacy `frontend`, `css_framework`, `state_management`, `api_type`, `database`, `package_manager` are read when the short key is empty.
137
148
 
138
149
  ## Parsing
139
150
 
@@ -188,7 +199,7 @@ root = find_project_root()
188
199
 
189
200
  | Function | Returns | Description |
190
201
  |----------|---------|-------------|
191
- | `score_faf(yaml, tier?)` | `Mk4Result` | Mk4 score (21 or 33 slots) |
202
+ | `score_faf(yaml)` | `Mk4Result` | Mk4 score, always 33 slots (faf-kernel parity) |
192
203
  | `parse(content)` | `FafFile` | Parse YAML string |
193
204
  | `parse_file(path)` | `FafFile` | Parse from file path |
194
205
  | `validate(faf)` | `ValidationResult` | Structure validation + warnings |
@@ -9,29 +9,42 @@ The foundation other Python FAF tools build on. If you're building MCP servers,
9
9
  [![FAF](https://mcpaas.live/badge/Wolfe-Jam/faf-python-sdk.svg)](https://builder.faf.one)
10
10
  [![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/)
11
11
  [![Downloads](https://img.shields.io/pypi/dm/faf-python-sdk?style=for-the-badge&color=blue)](https://pypi.org/project/faf-python-sdk/)
12
- [![Tests](https://img.shields.io/badge/tests-213%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
12
+ [![Tests](https://img.shields.io/badge/tests-1070%20passing-brightgreen?style=for-the-badge)](https://github.com/Wolfe-Jam/faf-python-sdk)
13
13
  [![IANA](https://img.shields.io/badge/IANA-registered-informational?style=for-the-badge)](https://www.iana.org/assignments/media-types/application/vnd.faf+yaml)
14
14
 
15
15
  **Media Type:** `application/vnd.faf+yaml` (IANA registered)
16
16
 
17
- ## What's New in v1.3.1 — The Interop Edition
17
+ ## What's New in v2.0.0 — The Always33 Edition
18
18
 
19
- The SDK can now author AI-context files, not just parse and score them.
19
+ One engine, one number: faf-python-sdk scores all 33 slots exactly like faf-kernel — the same score faf-cli 8, claude-faf-mcp 7, faf-mcp 4 and grok-faf-mcp 2 give.
20
20
 
21
- > **v1.3.1** is a copy patch — "generate" removed from external text (module docstring, the blockquote written into every AGENTS.md, README, CHANGELOG). No API change; the `faf_sdk.interop` functions below are unchanged.
21
+ - **Always 33 slots.** The 12 enterprise slots count unless marked `slotignored`. 21 base slots filled with no markers: 64% (21/33). The same file plus the 12 markers: 100% (21/21). `faf auto` (faf-cli) writes the markers.
22
+ - `tbd` / `todo` are placeholders; short keys (`framework`, `css`, `state`, `api`, `db`, `pkg_manager`) are read.
23
+ - YAML is read the way the kernel reads it; unreadable YAML scores 0 without raising.
24
+ - `score_faf(yaml, tier=...)` still accepts `tier`; it no longer changes the slot count.
25
+ - **Upgrading:** `result.slots` lists all 33 slots under the kernel's names (`stack.framework`, `css`, `state`, `api`, `db`, `pkg_manager`; were `frontend`, `css_framework`, `state_management`, `api_type`, `database`, `package_manager`). Code that reads slots by name needs the new names. YAML the kernel can't read now scores 0 (a duplicate key, a second document, nesting deeper than 128), and rounding is half away from zero (12.5% → 13), as the kernel does.
26
+ - Parity harness: 845/845 fixtures match faf-kernel (`faf-scoring-kernel@3.0.0`), including the `project.faf` of 58 public repos.
22
27
 
23
- `faf_sdk.interop` — `generate_agents_md(faf)` and `generate_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
28
+ See [CHANGELOG.md](CHANGELOG.md) for the full list of changes.
29
+
30
+ ## v1.4.0 — The Interop Edition
31
+
32
+ The interop functions get their real names: `author_agents_md` / `author_gemini_md` are public, `render_*` is the impl, `generate_*` is deprecated (removed in 2.0).
33
+
34
+ Output is byte-identical — a naming change, not a behaviour change. Existing `from faf_sdk import generate_agents_md` keeps working, now with a `DeprecationWarning`.
35
+
36
+ `faf_sdk.interop` — `author_agents_md(faf)` and `author_gemini_md(faf)`, Python ports of faf-cli's `src/interop/agents.ts` + `gemini.ts`, in parity with the canonical TypeScript. Deterministic BETTER-shaped projection: setup (install→build→dev ordered) · tests · layout · conventions · three-tier guardrails · definition of done · security · commit · stack. Human Context (who/why marketing) is intentionally omitted from AGENTS.md — it belongs in the README / .faf DNA, not agent ops.
24
37
 
25
38
  ```python
26
- from faf_sdk import parse_file, generate_agents_md
39
+ from faf_sdk import parse_file, author_agents_md
27
40
 
28
41
  faf = parse_file("project.faf")
29
- print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
42
+ print(author_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
30
43
  ```
31
44
 
32
45
  Any Python FAF tool that authors an AI-context file wraps this now — never hand-roll one. `gemini-faf-mcp` 2.7.0's `faf_agents` / `faf_gemini` are the reference wrappers.
33
46
 
34
- ## What's New in v1.2.0 — The Dart Edition
47
+ ## v1.2.0 — The Dart Edition
35
48
 
36
49
  Adds `detect_dart_project()`: content-aware Dart/Flutter detection from a `pubspec.yaml` (Flutter app vs package · Dart MCP / backend / CLI / library), reproducing faf-cli's engine byte-for-byte — 20 shared fixtures, parity-tested.
37
50
 
@@ -42,7 +55,7 @@ d = detect_dart_project(".")
42
55
  print(d.app_type, d.framework) # e.g. "mobile" "Flutter"
43
56
  ```
44
57
 
45
- ## What's New in v1.1.0
58
+ ## v1.1.0
46
59
 
47
60
  **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. Every FAF tool in every language now agrees on what 100% means.
48
61
 
@@ -82,26 +95,24 @@ print(f"Slots: {result.populated}/{result.total} populated")
82
95
 
83
96
  ## Mk4 Scoring
84
97
 
85
- 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.
98
+ The Mk4 engine scores `.faf` files against 33 slots (project metadata, human context, tech stack, and 12 enterprise slots), exactly as faf-kernel does. Each slot is **Populated**, **Empty**, or **Slotignored**. The score is populated ÷ active, where active = 33 − slotignored.
86
99
 
87
100
  ```python
88
- from faf_sdk import score_faf, LicenseTier
101
+ from faf_sdk import score_faf
89
102
 
90
- # Base scoring (21 slots)
91
103
  result = score_faf(yaml_content)
92
104
  print(result.score) # 0-100
93
- print(result.tier) # Trophy/Gold/Silver/Bronze/Green/Yellow/Red
105
+ print(result.tier) # TROPHY / GOLD / SILVER / BRONZE / GREEN / YELLOW / RED / WHITE
94
106
  print(result.populated) # slots with real data
95
- print(result.active) # total minus slotignored
96
- print(result.slots) # per-slot breakdown
97
-
98
- # Enterprise scoring (33 slots — adds monorepo/infra)
99
- result = score_faf(yaml_content, LicenseTier.ENTERPRISE)
107
+ print(result.active) # 33 minus slotignored
108
+ print(result.slots) # per-slot breakdown, 33 entries in kernel order
100
109
  ```
101
110
 
102
- **Placeholder rejection:** Values like `"null"`, `"unknown"`, `"n/a"`, `"Describe your project goal"` are detected and scored as Empty — not Populated.
111
+ **Placeholder rejection:** Values like `"null"`, `"none"`, `"unknown"`, `"n/a"`, `"tbd"`, `"todo"`, `"Describe your project goal"` (case-insensitive) are scored as Empty — not Populated.
112
+
113
+ **Slotignored:** Set any slot to `slotignored` to exclude it from scoring. A project that does not use the 12 enterprise slots marks them `slotignored` (`faf auto` writes them) and can still reach 100%.
103
114
 
104
- **Slotignored:** Set any slot to `slotignored` to exclude it from scoring. A backend-only project can mark `frontend: slotignored` and still reach 100%.
115
+ **Short keys:** `stack.framework`, `css`, `state`, `api`, `db`, `pkg_manager` are the canonical names; the legacy `frontend`, `css_framework`, `state_management`, `api_type`, `database`, `package_manager` are read when the short key is empty.
105
116
 
106
117
  ## Parsing
107
118
 
@@ -156,7 +167,7 @@ root = find_project_root()
156
167
 
157
168
  | Function | Returns | Description |
158
169
  |----------|---------|-------------|
159
- | `score_faf(yaml, tier?)` | `Mk4Result` | Mk4 score (21 or 33 slots) |
170
+ | `score_faf(yaml)` | `Mk4Result` | Mk4 score, always 33 slots (faf-kernel parity) |
160
171
  | `parse(content)` | `FafFile` | Parse YAML string |
161
172
  | `parse_file(path)` | `FafFile` | Parse from file path |
162
173
  | `validate(faf)` | `ValidationResult` | Structure validation + warnings |
@@ -25,7 +25,15 @@ from .validator import validate, ValidationResult
25
25
  from .mk4 import score_faf, Mk4Result, SlotState, LicenseTier
26
26
  from .discovery import find_faf_file, find_project_root, load_fafignore
27
27
  from .detect import detect_dart_project, DartProject
28
- from .interop import generate_agents_md, generate_gemini_md, faf_meta_tag
28
+ from .interop import (
29
+ author_agents_md,
30
+ author_gemini_md,
31
+ render_agents_md,
32
+ render_gemini_md,
33
+ generate_agents_md, # deprecated alias — kept in 2.0, removal planned
34
+ generate_gemini_md, # deprecated alias — kept in 2.0, removal planned
35
+ faf_meta_tag,
36
+ )
29
37
  from .types import (
30
38
  FafData,
31
39
  ProjectInfo,
@@ -36,7 +44,7 @@ from .types import (
36
44
  AIScoring
37
45
  )
38
46
 
39
- __version__ = "1.3.1"
47
+ __version__ = "2.0.0"
40
48
  __all__ = [
41
49
  # Parser
42
50
  "parse",
@@ -58,9 +66,9 @@ __all__ = [
58
66
  # Detection (Dart/Flutter — A+B hybrid, parity with faf-cli)
59
67
  "detect_dart_project",
60
68
  "DartProject",
61
- # Interop — AGENTS.md / GEMINI.md generators (parity with faf-cli src/interop)
62
- "generate_agents_md",
63
- "generate_gemini_md",
69
+ # Interop — AGENTS.md / GEMINI.md authoring (parity with faf-cli src/interop)
70
+ "author_agents_md",
71
+ "author_gemini_md",
64
72
  "faf_meta_tag",
65
73
  # Types
66
74
  "FafData",