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.
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.github/workflows/pypi.yml +8 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.github/workflows/testpypi.yml +7 -7
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/.gitignore +3 -0
- faf_python_sdk-2.0.0/CHANGELOG.md +174 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CLAUDE.md +2 -2
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CONTRIBUTING.md +2 -1
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/PKG-INFO +33 -22
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/README.md +32 -21
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/__init__.py +13 -5
- faf_python_sdk-2.0.0/faf_sdk/_kernel_yaml.py +493 -0
- faf_python_sdk-2.0.0/faf_sdk/_libyaml_scanner.py +874 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/interop.py +50 -5
- faf_python_sdk-2.0.0/faf_sdk/mk4.py +256 -0
- faf_python_sdk-2.0.0/project.faf +46 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/pyproject.toml +1 -1
- faf_python_sdk-2.0.0/scripts/gen_fuzz_cases.py +117 -0
- faf_python_sdk-2.0.0/scripts/kernel_oracle.js +27 -0
- faf_python_sdk-2.0.0/scripts/package-lock.json +23 -0
- faf_python_sdk-2.0.0/scripts/package.json +9 -0
- faf_python_sdk-2.0.0/scripts/record_kernel_expected.py +51 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/agents-md-facts.faf +39 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/bun-sticky-faf.faf +160 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/bun-sticky-zig.faf +63 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/claude-faf-mcp.faf +56 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/claude-fafm-sdk.faf +46 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/create-faf.faf +44 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-agent-public.faf +149 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-builder-public.faf +20 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-chrome-extension.faf +113 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-clawdbot.faf +41 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-cli.faf +101 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-crate.faf +65 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-enterprise-public.faf +340 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-foundation-site.faf +41 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-grid.faf +39 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-mcp-rig.faf +51 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-mcp.faf +59 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-memory-mcp.faf +53 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-moltbot.faf +60 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-one-svelte-new.faf +39 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-radio-public.faf +122 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-radio-rust.faf +54 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-rust.faf +48 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-server-card-ref.faf +54 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-skills-site.faf +39 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-skills.faf +38 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-taf-demo.faf +14 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-taf-git.faf +255 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-voice-public.faf +119 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-vscode.faf +57 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-wasm-core.faf +46 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-wasm-gen.faf +49 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/faf.faf +46 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/fafipedia.faf +41 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/gemini-faf-mcp.faf +200 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-elite.faf +119 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-mcp.faf +57 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-plugin.faf +41 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/grok-faf-voice.faf +49 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/hello-faf.faf +82 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/homebrew-faf.faf +41 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/mcp-context-card.faf +28 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/mcp-project-context.faf +34 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/mcpaas-cf.faf +45 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/mcpaas-sdk.faf +39 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/rust-faf-mcp.faf +75 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/slash-nextjs.faf +85 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/slash-tokens.faf +40 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/v0-zero-faf-builder.faf +3 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/wjttc-public.faf +159 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/wolfejam-dev.faf +118 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/wolfejames-com.faf +118 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-rag-public.faf +21 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-rust-public.faf +98 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-zeph.faf +49 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/xai-faf-zig-public.faf +74 -0
- faf_python_sdk-2.0.0/tests/fixtures/corpus/zero-faf-builder.faf +50 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/all21-base-slotignored.faf +25 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/all33-slotignored.faf +38 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/anchors-and-aliases.faf +5 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/app-type-is-not-read.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/base21-legacy-keys-12-markers.faf +38 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/base21-legacy-keys-no-markers.faf +25 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/base21-short-keys-12-markers.faf +38 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/base21-short-keys-no-markers.faf +25 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/bom.faf +3 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/comments-only.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/crlf.faf +3 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/document-markers.faf +5 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/empty-file.faf +0 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/empty-values.faf +12 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/enterprise-partial.faf +29 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/flow-style-full.faf +1 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/flow-style.faf +1 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/full33.faf +38 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/legacy-keys-only.faf +10 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/local-tag-is-empty.faf +7 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/merge-key-not-applied.faf +7 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/nested-values.faf +11 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/numbers-and-bools.faf +12 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-all.faf +19 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-tbd-todo.faf +12 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/placeholders-whitespace.faf +10 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-1-of-8.faf +31 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-3-of-8.faf +33 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/rounding-5-of-8.faf +35 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/section-not-a-mapping.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/short-keys.faf +10 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/short-vs-legacy-precedence.faf +16 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/slotignored-exact.faf +8 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tabs-in-values.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tier-32-of-33.faf +37 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tier-bronze.faf +35 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tier-green.faf +32 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tier-silver.faf +37 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/tier-yellow.faf +28 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-128bit-int.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-alias-bomb.faf +7 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-bad-escape.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-control-char.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-duplicate-key.faf +3 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-duplicate-section.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-flow-colon.faf +1 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-int-tag-mismatch.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-null-tag-mismatch.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-syntax.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-tab-indent.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-too-deep.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-two-documents.faf +5 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-unknown-anchor.faf +2 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-unknown-directive.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge/unreadable-yaml-1.3.faf +4 -0
- faf_python_sdk-2.0.0/tests/fixtures/edge_cases.json +274 -0
- faf_python_sdk-2.0.0/tests/fixtures/fuzz_cases.json +462 -0
- faf_python_sdk-2.0.0/tests/fixtures/kernel_expected.json +848 -0
- faf_python_sdk-2.0.0/tests/parity_support.py +118 -0
- faf_python_sdk-2.0.0/tests/test_always33_parity.py +126 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_interop.py +48 -17
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_mk4.py +92 -23
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_wjttc.py +4 -4
- faf_python_sdk-1.3.1/CHANGELOG.md +0 -87
- faf_python_sdk-1.3.1/faf_sdk/mk4.py +0 -216
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/CODE_OF_CONDUCT.md +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/LICENSE +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/SECURITY.md +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/docs/GROK-INTEGRATION.md +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/docs/TECHNICAL-SPEC.md +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/examples/basic_usage.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/examples/grok_integration.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/dart_detection.json +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/detect.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/discovery.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/parser.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/py.typed +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/types.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/faf_sdk/validator.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/scripts/sync-dart-spec.sh +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/__init__.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/dart_parity_fixtures.json +0 -0
- /faf_python_sdk-1.3.1/project.faf → /faf_python_sdk-2.0.0/tests/fixtures/corpus/faf-python-sdk.faf +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/stress_test.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_dart_parity.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_discovery.py +0 -0
- {faf_python_sdk-1.3.1 → faf_python_sdk-2.0.0}/tests/test_parser.py +0 -0
- {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
|
|
4
|
-
#
|
|
5
|
-
#
|
|
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.
|
|
9
|
-
# 2. verify install from TestPyPI
|
|
10
|
-
# 3.
|
|
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:
|
|
@@ -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 |
|
|
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:
|
|
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
|
-
|
|
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:
|
|
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
|
[](https://builder.faf.one)
|
|
42
42
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
43
43
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
44
|
-
[](https://github.com/Wolfe-Jam/faf-python-sdk)
|
|
45
45
|
[](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
|
|
49
|
+
## What's New in v2.0.0 — The Always33 Edition
|
|
50
50
|
|
|
51
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
71
|
+
from faf_sdk import parse_file, author_agents_md
|
|
59
72
|
|
|
60
73
|
faf = parse_file("project.faf")
|
|
61
|
-
print(
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
|
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
|
|
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) #
|
|
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) #
|
|
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
|
|
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
|
-
**
|
|
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
|
|
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
|
[](https://builder.faf.one)
|
|
10
10
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
11
11
|
[](https://pypi.org/project/faf-python-sdk/)
|
|
12
|
-
[](https://github.com/Wolfe-Jam/faf-python-sdk)
|
|
13
13
|
[](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
|
|
17
|
+
## What's New in v2.0.0 — The Always33 Edition
|
|
18
18
|
|
|
19
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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,
|
|
39
|
+
from faf_sdk import parse_file, author_agents_md
|
|
27
40
|
|
|
28
41
|
faf = parse_file("project.faf")
|
|
29
|
-
print(
|
|
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
|
-
##
|
|
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
|
-
##
|
|
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
|
|
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
|
|
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) #
|
|
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) #
|
|
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
|
|
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
|
-
**
|
|
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
|
|
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
|
|
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__ = "
|
|
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
|
|
62
|
-
"
|
|
63
|
-
"
|
|
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",
|