faf-python-sdk 1.3.1__tar.gz → 1.4.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 (38) hide show
  1. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/CHANGELOG.md +25 -0
  2. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/PKG-INFO +8 -8
  3. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/README.md +7 -7
  4. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/__init__.py +13 -5
  5. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/interop.py +49 -5
  6. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/pyproject.toml +1 -1
  7. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_interop.py +48 -17
  8. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/.github/workflows/pypi.yml +0 -0
  9. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/.github/workflows/testpypi.yml +0 -0
  10. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/.gitignore +0 -0
  11. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/CLAUDE.md +0 -0
  12. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/CODE_OF_CONDUCT.md +0 -0
  13. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/CONTRIBUTING.md +0 -0
  14. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/LICENSE +0 -0
  15. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/SECURITY.md +0 -0
  16. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/docs/GROK-INTEGRATION.md +0 -0
  17. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/docs/TECHNICAL-SPEC.md +0 -0
  18. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/examples/basic_usage.py +0 -0
  19. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/examples/grok_integration.py +0 -0
  20. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/dart_detection.json +0 -0
  21. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/detect.py +0 -0
  22. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/discovery.py +0 -0
  23. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/mk4.py +0 -0
  24. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/parser.py +0 -0
  25. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/py.typed +0 -0
  26. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/types.py +0 -0
  27. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/faf_sdk/validator.py +0 -0
  28. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/project.faf +0 -0
  29. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/scripts/sync-dart-spec.sh +0 -0
  30. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/__init__.py +0 -0
  31. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/dart_parity_fixtures.json +0 -0
  32. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/stress_test.py +0 -0
  33. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_dart_parity.py +0 -0
  34. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_discovery.py +0 -0
  35. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_mk4.py +0 -0
  36. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_parser.py +0 -0
  37. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_validator.py +0 -0
  38. {faf_python_sdk-1.3.1 → faf_python_sdk-1.4.0}/tests/test_wjttc.py +0 -0
@@ -3,6 +3,31 @@
3
3
  All notable changes to faf-python-sdk are documented here.
4
4
  Format: [Keep a Changelog](https://keepachangelog.com/en/1.1.0/)
5
5
 
6
+ ## [1.4.0] - 2026-09-08 — The Interop Edition
7
+
8
+ 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).
9
+
10
+ 1.3.1 kept `generate_*` as "internal identifiers" — but a name that surfaces
11
+ reflexively when reasoning about the tool lands in `tools/list` descriptions,
12
+ host UIs, and docs. "Internal-only" holds only until someone documents it, so
13
+ the public name has to be right now.
14
+
15
+ ### Added
16
+ - `author_agents_md(faf)` / `author_gemini_md(faf)` — the public API names, in
17
+ the "faf authors" voice used on every documented surface. This is what
18
+ `faf_sdk.interop.__all__` advertises.
19
+ - `render_agents_md(faf)` / `render_gemini_md(faf)` — the impl names: pure
20
+ `dict -> str` projection, pairs with the `write_*` helpers, greps clean. Same
21
+ split the canonical faf-cli TS uses.
22
+
23
+ ### Deprecated
24
+ - `generate_agents_md` / `generate_gemini_md` — still work, now emit
25
+ `DeprecationWarning`, removed in 2.0. Point at `author_*`.
26
+
27
+ ### Notes
28
+ - Additive + back-compatible: existing `from faf_sdk import generate_agents_md`
29
+ keeps working. No output change — the authored Markdown is byte-identical.
30
+
6
31
  ## [1.3.1] - 2026-09-07 — The Interop Edition
7
32
 
8
33
  Docs / copy patch — no API change.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.5
2
2
  Name: faf-python-sdk
3
- Version: 1.3.1
3
+ Version: 1.4.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,24 +41,24 @@ 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-216%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 v1.4.0 — The Interop Edition
50
50
 
51
- The SDK can now author AI-context files, not just parse and score them.
51
+ 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).
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
+ 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`.
54
54
 
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.
55
+ `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
56
 
57
57
  ```python
58
- from faf_sdk import parse_file, generate_agents_md
58
+ from faf_sdk import parse_file, author_agents_md
59
59
 
60
60
  faf = parse_file("project.faf")
61
- print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
61
+ print(author_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
62
62
  ```
63
63
 
64
64
  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.
@@ -9,24 +9,24 @@ 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-216%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 v1.4.0 — The Interop Edition
18
18
 
19
- The SDK can now author AI-context files, not just parse and score them.
19
+ 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).
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
+ 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`.
22
22
 
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.
23
+ `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
24
 
25
25
  ```python
26
- from faf_sdk import parse_file, generate_agents_md
26
+ from faf_sdk import parse_file, author_agents_md
27
27
 
28
28
  faf = parse_file("project.faf")
29
- print(generate_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
29
+ print(author_agents_md(faf.data.raw)) # takes the raw dict — carries top-level commands / key_files / security
30
30
  ```
31
31
 
32
32
  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.
@@ -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 — removed in 2.0
34
+ generate_gemini_md, # deprecated alias — removed in 2.0
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__ = "1.4.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",
@@ -10,13 +10,23 @@ not agent ops.
10
10
  Both take the raw parsed .faf dict (`FafFile.data.raw`) — the .faf format
11
11
  carries top-level `commands` / `key_files` / `security` that the typed model
12
12
  doesn't surface.
13
+
14
+ Naming: the impl functions are ``render_agents_md`` / ``render_gemini_md``
15
+ (pure ``dict -> str`` projection, pairs with the writers). The public API
16
+ name is ``author_agents_md`` / ``author_gemini_md`` — the surface consumers
17
+ import, in the "faf authors" voice used on every documented surface.
18
+ ``generate_*`` is a deprecated alias, removed in 2.0.
13
19
  """
14
20
 
15
21
  from __future__ import annotations
16
22
 
23
+ import warnings
17
24
  from typing import Any
18
25
 
19
- __all__ = ["generate_agents_md", "generate_gemini_md", "faf_meta_tag", "title_label", "slot_label"]
26
+ __all__ = [
27
+ "author_agents_md", "author_gemini_md",
28
+ "faf_meta_tag", "title_label", "slot_label",
29
+ ]
20
30
 
21
31
  # --- shared helpers ----------------------------------------------------------
22
32
 
@@ -146,8 +156,11 @@ def _key_files(faf: dict) -> list:
146
156
 
147
157
  # --- AGENTS.md -------------------------------------------------------------
148
158
 
149
- def generate_agents_md(faf: dict) -> str:
150
- """Author a BETTER-shaped AGENTS.md from raw .faf data. Deterministic."""
159
+ def render_agents_md(faf: dict) -> str:
160
+ """Author a BETTER-shaped AGENTS.md from raw .faf data. Deterministic.
161
+
162
+ Public alias: :func:`author_agents_md`.
163
+ """
151
164
  lines: list[str] = []
152
165
 
153
166
  def push(s: str = "") -> None:
@@ -344,8 +357,11 @@ def generate_agents_md(faf: dict) -> str:
344
357
 
345
358
  # --- GEMINI.md ------------------------------------------------------------
346
359
 
347
- def generate_gemini_md(faf: dict) -> str:
348
- """GEMINI.md — Gemini CLI's own convention (hierarchical, @file-importable)."""
360
+ def render_gemini_md(faf: dict) -> str:
361
+ """GEMINI.md — Gemini CLI's own convention (hierarchical, @file-importable).
362
+
363
+ Public alias: :func:`author_gemini_md`.
364
+ """
349
365
  lines: list[str] = []
350
366
  proj = faf.get("project") or {}
351
367
  cmds = _classify_commands(faf)
@@ -401,3 +417,31 @@ def generate_gemini_md(faf: dict) -> str:
401
417
  "",
402
418
  ]
403
419
  return "\n".join(lines)
420
+
421
+
422
+ # --- public + deprecated names --------------------------------------------
423
+
424
+ #: Public API name for :func:`render_agents_md` — "faf authors" voice.
425
+ author_agents_md = render_agents_md
426
+ #: Public API name for :func:`render_gemini_md`.
427
+ author_gemini_md = render_gemini_md
428
+
429
+
430
+ def generate_agents_md(faf: dict) -> str:
431
+ """Deprecated alias for :func:`author_agents_md`. Removed in 2.0."""
432
+ warnings.warn(
433
+ "generate_agents_md is deprecated; use author_agents_md",
434
+ DeprecationWarning,
435
+ stacklevel=2,
436
+ )
437
+ return render_agents_md(faf)
438
+
439
+
440
+ def generate_gemini_md(faf: dict) -> str:
441
+ """Deprecated alias for :func:`author_gemini_md`. Removed in 2.0."""
442
+ warnings.warn(
443
+ "generate_gemini_md is deprecated; use author_gemini_md",
444
+ DeprecationWarning,
445
+ stacklevel=2,
446
+ )
447
+ return render_gemini_md(faf)
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
4
4
 
5
5
  [project]
6
6
  name = "faf-python-sdk"
7
- version = "1.3.1"
7
+ version = "1.4.0"
8
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"}
@@ -1,6 +1,8 @@
1
- """Tests for faf_sdk.interop — AGENTS.md / GEMINI.md generators."""
1
+ """Tests for faf_sdk.interop — AGENTS.md / GEMINI.md authoring."""
2
2
 
3
- from faf_sdk import faf_meta_tag, generate_agents_md, generate_gemini_md
3
+ import pytest
4
+
5
+ from faf_sdk import faf_meta_tag, render_agents_md, render_gemini_md
4
6
  from faf_sdk.interop import slot_label, title_label
5
7
 
6
8
  # A representative .faf as the raw parsed dict.
@@ -64,7 +66,7 @@ def test_faf_meta_tag_two_lines():
64
66
  # --- AGENTS.md -----------------------------------------------------------
65
67
 
66
68
  def test_agents_md_sections_present():
67
- md = generate_agents_md(SAMPLE)
69
+ md = render_agents_md(SAMPLE)
68
70
  for section in (
69
71
  "# AGENTS.md — widget-api",
70
72
  "## Setup & build",
@@ -82,14 +84,14 @@ def test_agents_md_sections_present():
82
84
 
83
85
 
84
86
  def test_agents_md_omits_human_context():
85
- md = generate_agents_md({**SAMPLE, "human_context": {"who": "devs", "why": "money"}})
87
+ md = render_agents_md({**SAMPLE, "human_context": {"who": "devs", "why": "money"}})
86
88
  assert "## Human Context" not in md
87
89
  assert "## Context" not in md
88
90
  assert "money" not in md
89
91
 
90
92
 
91
93
  def test_agents_md_setup_ordered_install_build_dev():
92
- md = generate_agents_md(SAMPLE)
94
+ md = render_agents_md(SAMPLE)
93
95
  block = md.split("## Setup & build")[1].split("```")[1]
94
96
  # first line is the ```bash language tag
95
97
  lines = [ln for ln in block.strip().splitlines()][1:]
@@ -99,7 +101,7 @@ def test_agents_md_setup_ordered_install_build_dev():
99
101
 
100
102
 
101
103
  def test_agents_md_guardrails_always_render_even_with_no_data():
102
- md = generate_agents_md({"faf_version": "3.0", "project": {"name": "bare"}})
104
+ md = render_agents_md({"faf_version": "3.0", "project": {"name": "bare"}})
103
105
  assert "## Guardrails" in md
104
106
  assert "## Definition of Done" in md
105
107
  assert "## Commit & PR" in md
@@ -107,28 +109,28 @@ def test_agents_md_guardrails_always_render_even_with_no_data():
107
109
 
108
110
 
109
111
  def test_agents_md_key_files_table_when_roles_present():
110
- md = generate_agents_md(SAMPLE)
112
+ md = render_agents_md(SAMPLE)
111
113
  assert "| Path | Role |" in md
112
114
  assert "| `src/server.ts` | the entry point |" in md
113
115
 
114
116
 
115
117
  def test_agents_md_security_never_leaks_values():
116
- md = generate_agents_md(SAMPLE)
118
+ md = render_agents_md(SAMPLE)
117
119
  assert "Secrets live in `.env`" in md
118
120
  assert "see `.env.example`" in md
119
121
 
120
122
 
121
123
  def test_agents_md_stack_drops_non_stack_marketing_keys():
122
- md = generate_agents_md(SAMPLE)
124
+ md = render_agents_md(SAMPLE)
123
125
  assert "developers" not in md.split("## Stack")[1] # target_user filtered
124
126
 
125
127
 
126
128
  def test_agents_md_deterministic():
127
- assert generate_agents_md(SAMPLE) == generate_agents_md(dict(SAMPLE))
129
+ assert render_agents_md(SAMPLE) == render_agents_md(dict(SAMPLE))
128
130
 
129
131
 
130
132
  def test_agents_md_human_prefs_excluded_from_conventions():
131
- md = generate_agents_md(SAMPLE)
133
+ md = render_agents_md(SAMPLE)
132
134
  conv = md.split("## Conventions")[1].split("##")[0]
133
135
  assert "communication" not in conv.lower()
134
136
  assert "Testing" in conv
@@ -137,7 +139,7 @@ def test_agents_md_human_prefs_excluded_from_conventions():
137
139
  # --- GEMINI.md ----------------------------------------------------------
138
140
 
139
141
  def test_gemini_md_structure():
140
- md = generate_gemini_md(SAMPLE)
142
+ md = render_gemini_md(SAMPLE)
141
143
  assert md.startswith("<!-- faf:")
142
144
  assert "# GEMINI.md — widget-api" in md
143
145
  assert "Project: widget-api" in md
@@ -148,7 +150,7 @@ def test_gemini_md_structure():
148
150
 
149
151
  def test_gemini_md_no_guardrail_ladder():
150
152
  # GEMINI.md is lighter — no Definition of Done / When stuck
151
- md = generate_gemini_md(SAMPLE)
153
+ md = render_gemini_md(SAMPLE)
152
154
  assert "## Definition of Done" not in md
153
155
  assert "## Guardrails" not in md
154
156
 
@@ -157,8 +159,8 @@ def test_gemini_md_no_guardrail_ladder():
157
159
 
158
160
  def test_minimal_faf_does_not_crash():
159
161
  minimal = {"faf_version": "3.0", "project": {"name": "x"}}
160
- assert "# AGENTS.md — x" in generate_agents_md(minimal)
161
- assert "# GEMINI.md — x" in generate_gemini_md(minimal)
162
+ assert "# AGENTS.md — x" in render_agents_md(minimal)
163
+ assert "# GEMINI.md — x" in render_gemini_md(minimal)
162
164
 
163
165
 
164
166
  def test_key_files_from_instant_context_fallback():
@@ -167,12 +169,41 @@ def test_key_files_from_instant_context_fallback():
167
169
  "project": {"name": "y"},
168
170
  "instant_context": {"key_files": ["a.py", "b.py"]},
169
171
  }
170
- md = generate_agents_md(faf)
172
+ md = render_agents_md(faf)
171
173
  assert "- `a.py`" in md
172
174
 
173
175
 
174
176
  def test_commands_from_instant_context_are_not_used_at_toplevel():
175
177
  # top-level `commands` is the source; instant_context.commands is separate
176
178
  faf = {"faf_version": "3.0", "project": {"name": "z"}, "commands": {"test": "go test ./..."}}
177
- md = generate_agents_md(faf)
179
+ md = render_agents_md(faf)
178
180
  assert "go test ./..." in md
181
+
182
+
183
+ # --- naming: public alias + deprecated shim ------------------------------
184
+
185
+ def test_author_names_are_the_public_alias():
186
+ from faf_sdk import author_agents_md, author_gemini_md
187
+
188
+ assert author_agents_md is render_agents_md
189
+ assert author_gemini_md is render_gemini_md
190
+
191
+
192
+ def test_generate_names_still_work_but_warn():
193
+ from faf_sdk import generate_agents_md, generate_gemini_md
194
+
195
+ with pytest.warns(DeprecationWarning, match="author_agents_md"):
196
+ md = generate_agents_md(SAMPLE)
197
+ assert md == render_agents_md(SAMPLE)
198
+
199
+ with pytest.warns(DeprecationWarning, match="author_gemini_md"):
200
+ assert generate_gemini_md(SAMPLE) == render_gemini_md(SAMPLE)
201
+
202
+
203
+ def test_interop_all_advertises_author_not_generate():
204
+ from faf_sdk import interop
205
+
206
+ assert "author_agents_md" in interop.__all__
207
+ assert "author_gemini_md" in interop.__all__
208
+ assert "generate_agents_md" not in interop.__all__
209
+ assert "render_agents_md" not in interop.__all__ # importable, not advertised
File without changes
File without changes