file-observer 1.2.2__tar.gz → 1.2.3__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 (26) hide show
  1. {file_observer-1.2.2/src/file_observer.egg-info → file_observer-1.2.3}/PKG-INFO +1 -1
  2. {file_observer-1.2.2 → file_observer-1.2.3}/pyproject.toml +1 -1
  3. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer/scanner.py +12 -5
  4. {file_observer-1.2.2 → file_observer-1.2.3/src/file_observer.egg-info}/PKG-INFO +1 -1
  5. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer.egg-info/SOURCES.txt +2 -1
  6. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_packaging.py +3 -3
  7. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_unit.py +6 -6
  8. file_observer-1.2.3/tests/test_v1_2_3.py +41 -0
  9. {file_observer-1.2.2 → file_observer-1.2.3}/LICENSE +0 -0
  10. {file_observer-1.2.2 → file_observer-1.2.3}/LICENSE-AGPL +0 -0
  11. {file_observer-1.2.2 → file_observer-1.2.3}/docs/README.md +0 -0
  12. {file_observer-1.2.2 → file_observer-1.2.3}/setup.cfg +0 -0
  13. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer/__init__.py +0 -0
  14. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer.egg-info/dependency_links.txt +0 -0
  15. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer.egg-info/entry_points.txt +0 -0
  16. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer.egg-info/requires.txt +0 -0
  17. {file_observer-1.2.2 → file_observer-1.2.3}/src/file_observer.egg-info/top_level.txt +0 -0
  18. {file_observer-1.2.2 → file_observer-1.2.3}/src/scanner/__init__.py +0 -0
  19. {file_observer-1.2.2 → file_observer-1.2.3}/src/scanner/scanner.py +0 -0
  20. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_edge_cases.py +0 -0
  21. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_golden.py +0 -0
  22. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_integration.py +0 -0
  23. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_v1_1.py +0 -0
  24. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_v1_2.py +0 -0
  25. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_v1_2_1.py +0 -0
  26. {file_observer-1.2.2 → file_observer-1.2.3}/tests/test_v1_2_2.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: file-observer
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Not a file watcher — a one-shot, read-only scan that emits a deterministic JSON manifest of what's in a directory before you ingest it.
5
5
  Author-email: Russell Pfister <russalo@russalo.com>
6
6
  License: AGPL-3.0-or-later
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "file-observer"
7
- version = "1.2.2"
7
+ version = "1.2.3"
8
8
  description = "Not a file watcher — a one-shot, read-only scan that emits a deterministic JSON manifest of what's in a directory before you ingest it."
9
9
  readme = "docs/README.md"
10
10
  requires-python = ">=3.12"
@@ -71,8 +71,8 @@ except ImportError:
71
71
  _defusedxml_available = False
72
72
 
73
73
 
74
- SCANNER_VERSION = "1.2.2"
75
- LOGIC_VERSION = "1.1.2"
74
+ SCANNER_VERSION = "1.2.3"
75
+ LOGIC_VERSION = "1.1.3"
76
76
  SCHEMA_VERSION = "1.2"
77
77
 
78
78
 
@@ -546,7 +546,7 @@ CHATLOG_TOOL = "chatlog_signals"
546
546
  # any detection regex or extraction algorithm requires bumping METHOD_VERSION
547
547
  # and updating the rules definition string.
548
548
  CHATLOG_VECTOR_ID = "chatlog"
549
- CHATLOG_METHOD_VERSION = 6 # v1.2.2: prose Rule 1 requires a recurring speaker; expanded stop-list (FP fixes)
549
+ CHATLOG_METHOD_VERSION = 7 # v1.2.3: + Question/Answer stop-list (FAQ FP stopgap)
550
550
  CHATLOG_RULES_DEFINITION = (
551
551
  "detect:speaker_label_re(distinct>=2,total>=3,recurring>=1,stop_list),"
552
552
  "h3_header_re(5+)|section_divider_re(3+)[require speaker_cosignal_distinct>=2],"
@@ -556,10 +556,10 @@ CHATLOG_RULES_DEFINITION = (
556
556
  "turn_char_stats,speaker_turn_counts,speaker_turn_chars,alternation,"
557
557
  "reference_tokens(at_mentions,wiki_links,code_fence_blocks,url_count),"
558
558
  "top_capitalized_tokens(freq>=3,top20),vocabulary_size_estimate;"
559
- "stop_list:Allow,Arguments,Authorization,Bcc,CAUTION,Cc,Command,Commands,"
559
+ "stop_list:ANSWER,Allow,Answer,Arguments,Authorization,Bcc,CAUTION,Cc,Command,Commands,"
560
560
  "Copyright,Date,Description,Disallow,Distribution,Documentation,Error,Example,"
561
561
  "Examples,FIXME,Format,From,IMPORTANT,License,Lines,Message,NOTE,Newsgroups,"
562
- "Note,Options,Organization,Parameters,Password,Path,References,Result,Returns,"
562
+ "Note,Options,Organization,Parameters,Password,Path,QUESTION,Question,References,Result,Returns,"
563
563
  "Sender,Subject,Summary,Synopsis,TIP,TODO,To,Usage,Version,Warning"
564
564
  )
565
565
  CHATLOG_STATIC_TUNING = {
@@ -624,6 +624,13 @@ CHATLOG_SPEAKER_STOP_LIST: set[str] = {
624
624
  "Version", "Usage", "Options", "Command", "Commands", "Format", "Synopsis",
625
625
  "Description", "Arguments", "Parameters", "Returns", "Summary",
626
626
  "Authorization", "Documentation", "License", "Copyright", "Password",
627
+ # v1.2.3: FAQ labels — `Question:`/`Answer:` recur like speakers but aren't
628
+ # (stopgap for the most common recurring-Key:value FP; the real fix is a
629
+ # non-count signal — see scratch/review/v1_2_2_fp_findings.md). Deliberately
630
+ # NOT adding single letters `Q`/`A`: `A:`/`B:` is legitimate anonymized dialogue.
631
+ # Membership is case-sensitive and the label regex matches all-caps, so the
632
+ # common ALL-CAPS FAQ style needs its own entries (PR #30 review).
633
+ "Question", "Answer", "QUESTION", "ANSWER",
627
634
  }
628
635
 
629
636
  # v0.9: Reference tokens vector identity constants.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.4
2
2
  Name: file-observer
3
- Version: 1.2.2
3
+ Version: 1.2.3
4
4
  Summary: Not a file watcher — a one-shot, read-only scan that emits a deterministic JSON manifest of what's in a directory before you ingest it.
5
5
  Author-email: Russell Pfister <russalo@russalo.com>
6
6
  License: AGPL-3.0-or-later
@@ -20,4 +20,5 @@ tests/test_unit.py
20
20
  tests/test_v1_1.py
21
21
  tests/test_v1_2.py
22
22
  tests/test_v1_2_1.py
23
- tests/test_v1_2_2.py
23
+ tests/test_v1_2_2.py
24
+ tests/test_v1_2_3.py
@@ -17,7 +17,7 @@ def test_canonical_top_level_api():
17
17
 
18
18
  import file_observer
19
19
 
20
- assert file_observer.__version__ == "1.2.2"
20
+ assert file_observer.__version__ == "1.2.3"
21
21
  assert "Scanner" in file_observer.__all__
22
22
 
23
23
 
@@ -49,9 +49,9 @@ def test_canonical_submodule_constants_unchanged():
49
49
  """Constants and the manifest field they feed stay stable across the 1.0.x → 1.1 line (the 1.0.1 import-package rename left them intact)."""
50
50
  from file_observer.scanner import SCANNER_VERSION, LOGIC_VERSION, SCHEMA_VERSION
51
51
 
52
- assert SCANNER_VERSION == "1.2.2"
52
+ assert SCANNER_VERSION == "1.2.3"
53
53
  assert SCHEMA_VERSION == "1.2" # additive minor bump (v1.1 corpus-intelligence fields)
54
- assert LOGIC_VERSION == "1.1.2" # v1.2.2: prose chatlog detection tightened (recurrence + stop-list)
54
+ assert LOGIC_VERSION == "1.1.3" # v1.2.3: + Question/Answer stop-list (FAQ FP stopgap)
55
55
 
56
56
 
57
57
  def test_legacy_scanner_import_warns():
@@ -811,8 +811,8 @@ class TestScanContext:
811
811
  (tmp_path / "a.txt").write_text("hello")
812
812
  manifest = Scanner(source_dir=tmp_path).scan()
813
813
  ctx = manifest.context
814
- assert ctx.scanner_version == "1.2.2"
815
- assert ctx.logic_version == "1.1.2"
814
+ assert ctx.scanner_version == "1.2.3"
815
+ assert ctx.logic_version == "1.1.3"
816
816
  assert ctx.python_version # non-empty
817
817
  assert ctx.platform # non-empty
818
818
 
@@ -850,7 +850,7 @@ class TestScanContext:
850
850
  manifest = Scanner(source_dir=tmp_path).scan()
851
851
  data = json_mod.loads(manifest_to_json(manifest))
852
852
  assert "context" in data
853
- assert data["context"]["scanner_version"] == "1.2.2"
853
+ assert data["context"]["scanner_version"] == "1.2.3"
854
854
 
855
855
 
856
856
  # ---------------------------------------------------------------------------
@@ -2042,8 +2042,8 @@ class TestSemanticToolNames:
2042
2042
 
2043
2043
  def test_version_is_current(self) -> None:
2044
2044
  from file_observer.scanner import SCANNER_VERSION, LOGIC_VERSION
2045
- assert SCANNER_VERSION == "1.2.2"
2046
- assert LOGIC_VERSION == "1.1.2"
2045
+ assert SCANNER_VERSION == "1.2.3"
2046
+ assert LOGIC_VERSION == "1.1.3"
2047
2047
 
2048
2048
 
2049
2049
  # ---------------------------------------------------------------------------
@@ -3530,4 +3530,4 @@ class TestMarkdownReport:
3530
3530
  from file_observer.scanner import manifest_to_markdown
3531
3531
  (tmp_path / "a.txt").write_text("hello")
3532
3532
  md = manifest_to_markdown(Scanner(source_dir=tmp_path).scan())
3533
- assert "1.2.2" in md
3533
+ assert "1.2.3" in md
@@ -0,0 +1,41 @@
1
+ """v1.2.3 — FAQ false-positive stopgap.
2
+
3
+ Pro + flash both flagged that recurring `Question:`/`Answer:` labels (a FAQ doc)
4
+ satisfy the v1.2.2 prose rule (≥2 distinct, ≥3 total, ≥1 recurring) and
5
+ false-positive as a chatlog. v1.2.3 adds `Question`/`Answer` to the stop-list.
6
+
7
+ This is a STOPGAP for the most common single case — NOT the real fix. The root
8
+ issue (prose `Key:value` is ambiguous with dialogue) needs a non-count signal;
9
+ see scratch/review/v1_2_2_fp_findings.md. We deliberately do NOT stop-list single
10
+ letters like `Q`/`A` — `A:`/`B:` is a legitimate anonymized-dialogue pattern.
11
+
12
+ Falsify-first: the FAQ test FAILS on v1.2.2, passes after the stop-list add.
13
+ """
14
+
15
+ from file_observer.scanner import Scanner, ScannerConfig
16
+
17
+
18
+ def _is_chatlog(tmp_path, name, content):
19
+ (tmp_path / name).write_text(content)
20
+ return Scanner(source_dir=tmp_path, config=ScannerConfig()).scan().files[0].is_chatlog
21
+
22
+
23
+ def test_faq_question_answer_not_flagged(tmp_path):
24
+ c = ("Question: How do I install?\nAnswer: Use pip.\n"
25
+ "Question: Where are the docs?\nAnswer: In docs/.\n")
26
+ assert _is_chatlog(tmp_path, "FAQ.md", c) is False
27
+
28
+
29
+ def test_faq_allcaps_not_flagged(tmp_path):
30
+ # case-sensitive stop-list + the label regex matches all-caps → ALL-CAPS FAQ
31
+ # style needs explicit coverage (PR #30 review: flash/codex/copilot flagged it).
32
+ c = ("QUESTION: How do I install?\nANSWER: Use pip.\n"
33
+ "QUESTION: Where are the docs?\nANSWER: In docs/.\n")
34
+ assert _is_chatlog(tmp_path, "FAQ.md", c) is False
35
+
36
+
37
+ def test_anonymized_ab_dialogue_still_detected(tmp_path):
38
+ # `A:`/`B:` anonymized speakers are a LEGIT conversation — must stay detected
39
+ # (guards against over-stop-listing single letters while fixing FAQ).
40
+ c = "A: hey did you see this\nB: yeah just now\nA: what do you think\n"
41
+ assert _is_chatlog(tmp_path, "dialogue.txt", c) is True
File without changes
File without changes