memleaf 0.2.12__tar.gz → 0.2.14__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.
- {memleaf-0.2.12 → memleaf-0.2.14}/CHANGELOG.md +22 -0
- {memleaf-0.2.12/src/memleaf.egg-info → memleaf-0.2.14}/PKG-INFO +3 -3
- {memleaf-0.2.12 → memleaf-0.2.14}/README.en.md +2 -2
- {memleaf-0.2.12 → memleaf-0.2.14}/README.md +2 -2
- {memleaf-0.2.12 → memleaf-0.2.14}/pyproject.toml +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/__init__.py +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/hermes_provider/__init__.py +1 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/hermes_provider/plugin.yaml +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/processing.py +67 -19
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/prompts.py +45 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/validation.py +305 -0
- {memleaf-0.2.12 → memleaf-0.2.14/src/memleaf.egg-info}/PKG-INFO +3 -3
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf.egg-info/SOURCES.txt +1 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_admission_noise.py +89 -0
- memleaf-0.2.14/tests/test_extraction_quality_regressions.py +680 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_hermes_provider.py +42 -4
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_pypi_install.py +5 -5
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b1.py +33 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b2a.py +87 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b2b.py +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_c1_mcp.py +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_c3_packaging.py +1 -1
- {memleaf-0.2.12 → memleaf-0.2.14}/IMPLEMENTATION_PLAN.md +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/LICENSE +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/MANIFEST.in +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/RELEASE_CHECKLIST.md +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/examples/README.md +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/examples/basic_usage.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/examples/mcp_stdio.ndjson +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/install.ps1 +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/install.sh +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/setup.cfg +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/__main__.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/adapters/__init__.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/adapters/antigravity.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/adapters/base.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/adapters/codex.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/adapters/hermes.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/budget.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/capture.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/cli.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/compaction.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/config.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/credentials.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/frontmatter.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/hermes_provider/README.md +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/host_events.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/host_runtime.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/inbox.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/index.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/installer.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/__init__.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/base.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/claude_compatible.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/gemini.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/openai_compatible.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/llm/router.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/locking.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/mcp_server.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/memory_writer.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/model_discovery.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/models.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/native_index.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/redaction.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/retrieval.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/retrieval_gate.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/scope_maintenance.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/scope_state.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/service.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf/vault.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf.egg-info/dependency_links.txt +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf.egg-info/entry_points.txt +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/src/memleaf.egg-info/top_level.txt +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/__init__.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_codex_install.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_codex_native_cli.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_context_budget.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_credential_safety.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_cross_host_acceptance.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_hermes_stdio_transport.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_host_events.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_host_runtime_contract.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_install.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_maintenance_v2.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_model_discovery.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_retrieval_gate.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_retrieval_v2.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_session_lineage.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_a.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3a_commit.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3a_contract.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3b_native_context.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3b_native_index.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3b_scope.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3c_retrieval.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_b3d_scope_maintenance.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_stage_c2_init.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_upgrade_preserves_vault.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_v2_gate_limits.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_v2_host_flow.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_v2_mcp_flow.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_v2_nomatch_semantics.py +0 -0
- {memleaf-0.2.12 → memleaf-0.2.14}/tests/test_v2_search_gate_acceptance.py +0 -0
|
@@ -2,6 +2,28 @@
|
|
|
2
2
|
|
|
3
3
|
All notable changes to memleaf are documented here.
|
|
4
4
|
|
|
5
|
+
## 0.2.14 — 2026-09-02
|
|
6
|
+
|
|
7
|
+
- Reject a permanent-memory candidate that combines a durable project rule or
|
|
8
|
+
implementation-plan change with an independent dated todo, while preserving
|
|
9
|
+
multiple dated milestones that belong to one project plan.
|
|
10
|
+
- Normalize explicit implementation plans and plan adjustments to `project`
|
|
11
|
+
without promoting adjacent mail, attachment, meeting, or archive records.
|
|
12
|
+
- Keep the gate-selected type immutable through summarization for both CREATE
|
|
13
|
+
and UPDATE, preserve multiple legal update candidates, and expose the new
|
|
14
|
+
`mixed_future_use` diagnostic through the Hermes provider.
|
|
15
|
+
|
|
16
|
+
## 0.2.13 — 2026-09-02
|
|
17
|
+
|
|
18
|
+
- Preserve the full active project-plan body, stable title, and retrieval
|
|
19
|
+
metadata when a customer suggestion only adds constraints; explicit
|
|
20
|
+
replacements continue to replace conflicting state normally.
|
|
21
|
+
- Reject attachment, document, presentation, material, and issue-list items
|
|
22
|
+
described only by transport metadata or a generic follow-up at both the gate
|
|
23
|
+
and summary boundaries.
|
|
24
|
+
- Keep concrete attachment-backed actions eligible when they include a
|
|
25
|
+
remediation, owner, deadline, delivery rule, deployment, or migration.
|
|
26
|
+
|
|
5
27
|
## 0.2.12 — 2026-09-02
|
|
6
28
|
|
|
7
29
|
- Remove empty ASCII or Chinese parentheses left after an absolute date is
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: memleaf
|
|
3
|
-
Version: 0.2.
|
|
3
|
+
Version: 0.2.14
|
|
4
4
|
Summary: A local-first Markdown memory core for AI agents
|
|
5
5
|
Author: memleaf contributors
|
|
6
6
|
License-Expression: MIT
|
|
@@ -23,8 +23,8 @@ Dynamic: license-file
|
|
|
23
23
|
|
|
24
24
|
[English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
25
25
|
|
|
26
|
-
> **当前版本:0.2.
|
|
27
|
-
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.
|
|
26
|
+
> **当前版本:0.2.14。**
|
|
27
|
+
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.14 通过 PyPI 分发。
|
|
28
28
|
> **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
|
|
29
29
|
|
|
30
30
|
## 项目定位
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
[中文](README.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
6
6
|
|
|
7
|
-
> **Version: 0.2.
|
|
8
|
-
> The core library, Vault, stdio MCP server, initialization CLI, model routing, memory extraction, controlled retrieval protocol, and host adapters are implemented. memleaf 0.2.
|
|
7
|
+
> **Version: 0.2.14.**
|
|
8
|
+
> The core library, Vault, stdio MCP server, initialization CLI, model routing, memory extraction, controlled retrieval protocol, and host adapters are implemented. memleaf 0.2.14 is distributed through PyPI.
|
|
9
9
|
> **The current release supports Hermes and Codex.** Antigravity is not detected, installed, or configured.
|
|
10
10
|
|
|
11
11
|
## Project scope
|
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
|
|
5
5
|
[English](README.en.md) · [PyPI](https://pypi.org/project/memleaf/) · [GitHub](https://github.com/miffyblueboo/memleaf)
|
|
6
6
|
|
|
7
|
-
> **当前版本:0.2.
|
|
8
|
-
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.
|
|
7
|
+
> **当前版本:0.2.14。**
|
|
8
|
+
> 核心库、Vault、stdio MCP Server、初始化 CLI、模型路由、提炼流程、受控检索协议和宿主适配器已经实现。memleaf 0.2.14 通过 PyPI 分发。
|
|
9
9
|
> **当前版本支持 Hermes 和 Codex。** Antigravity(反重力)不检测、不安装、不配置。
|
|
10
10
|
|
|
11
11
|
## 项目定位
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"""Local-first Markdown memory core for AI agents."""
|
|
2
2
|
|
|
3
|
-
__version__ = "0.2.
|
|
3
|
+
__version__ = "0.2.14"
|
|
4
4
|
|
|
5
5
|
from .config import DEFAULT_CONFIG, default_config, load_config, save_config
|
|
6
6
|
from .frontmatter import FrontmatterError, dump_frontmatter, dump_yaml, load_yaml, parse_frontmatter
|
|
@@ -34,6 +34,7 @@ from .prompts import (
|
|
|
34
34
|
DUPLICATE_TARGET_CORRECTION,
|
|
35
35
|
GATE_TYPE_CORRECTION,
|
|
36
36
|
JSON_CORRECTION,
|
|
37
|
+
MIXED_FUTURE_USE_CORRECTION,
|
|
37
38
|
MIXED_PROJECT_SCOPES_CORRECTION,
|
|
38
39
|
RELATIVE_TIME_CORRECTION,
|
|
39
40
|
GATE_SYSTEM,
|
|
@@ -65,6 +66,8 @@ from .validation import (
|
|
|
65
66
|
parse_summarize_output,
|
|
66
67
|
normalize_relative_calendar_text,
|
|
67
68
|
is_aggregate_operational_text,
|
|
69
|
+
is_attachment_followup_only_text,
|
|
70
|
+
is_project_plan_text,
|
|
68
71
|
)
|
|
69
72
|
from .vault import safe_component
|
|
70
73
|
|
|
@@ -598,6 +601,8 @@ class Processor:
|
|
|
598
601
|
return DUPLICATE_TARGET_CORRECTION
|
|
599
602
|
if hint == "mixed_project_scopes":
|
|
600
603
|
return MIXED_PROJECT_SCOPES_CORRECTION
|
|
604
|
+
if hint == "mixed_future_use":
|
|
605
|
+
return MIXED_FUTURE_USE_CORRECTION
|
|
601
606
|
if stage == "gate" and hint == "update_target_type_mismatch":
|
|
602
607
|
return UPDATE_TARGET_TYPE_CORRECTION
|
|
603
608
|
if stage == "gate" and hint == "invalid_type":
|
|
@@ -1695,22 +1700,64 @@ class Processor:
|
|
|
1695
1700
|
|
|
1696
1701
|
@staticmethod
|
|
1697
1702
|
def _is_project_plan_title(value: Any) -> bool:
|
|
1698
|
-
|
|
1699
|
-
return any(
|
|
1700
|
-
marker in text
|
|
1701
|
-
for marker in ("实施计划", "项目计划", "implementation plan", "project plan")
|
|
1702
|
-
)
|
|
1703
|
+
return is_project_plan_text(value)
|
|
1703
1704
|
|
|
1704
1705
|
@staticmethod
|
|
1705
1706
|
def _is_adjacent_plan_record(value: Any) -> bool:
|
|
1706
1707
|
text = normalize_term(value) if isinstance(value, str) else ""
|
|
1707
|
-
return any(
|
|
1708
|
+
return bool(text) and any(
|
|
1708
1709
|
marker in text
|
|
1709
1710
|
for marker in (
|
|
1710
1711
|
"已发送", "发送", "邮件", "附件", "存档", "会议", "启动会", "纪要",
|
|
1711
1712
|
"sent", "email", "mail", "attachment", "archive", "meeting", "minutes",
|
|
1712
1713
|
)
|
|
1714
|
+
) and not is_project_plan_text(value)
|
|
1715
|
+
|
|
1716
|
+
@classmethod
|
|
1717
|
+
def _merge_additive_project_plan_update(
|
|
1718
|
+
cls,
|
|
1719
|
+
candidate: Mapping[str, Any],
|
|
1720
|
+
summary: Mapping[str, Any],
|
|
1721
|
+
target: Memory | None,
|
|
1722
|
+
) -> dict[str, Any]:
|
|
1723
|
+
"""Preserve a full active plan when current evidence only adds constraints."""
|
|
1724
|
+
|
|
1725
|
+
result = dict(summary)
|
|
1726
|
+
if (
|
|
1727
|
+
target is None
|
|
1728
|
+
or target.type != "project"
|
|
1729
|
+
or not cls._is_project_plan_title(target.title)
|
|
1730
|
+
):
|
|
1731
|
+
return result
|
|
1732
|
+
candidate_text = str(candidate.get("memory", "")).casefold()
|
|
1733
|
+
additive_markers = (
|
|
1734
|
+
"建议", "补充", "增加", "新增", "追加", "还需", "同时", "纳入", "完善",
|
|
1735
|
+
"suggest", "propose", "additional", "add ", "include",
|
|
1736
|
+
)
|
|
1737
|
+
replacement_markers = (
|
|
1738
|
+
"改为", "变更为", "替换", "取消", "删除", "不再", "更新为", "调整为", "推迟至", "提前至",
|
|
1739
|
+
"replace", "changed to", "no longer", "cancel", "remove", "instead", "moved to",
|
|
1713
1740
|
)
|
|
1741
|
+
if not any(marker in candidate_text for marker in additive_markers):
|
|
1742
|
+
return result
|
|
1743
|
+
if any(marker in candidate_text for marker in replacement_markers):
|
|
1744
|
+
return result
|
|
1745
|
+
|
|
1746
|
+
old_body = target.body.strip()
|
|
1747
|
+
new_body = str(result.get("body", "")).strip()
|
|
1748
|
+
normalized_old = " ".join(old_body.casefold().split())
|
|
1749
|
+
normalized_new = " ".join(new_body.casefold().split())
|
|
1750
|
+
if old_body and normalized_old not in normalized_new:
|
|
1751
|
+
result["body"] = f"{old_body}\n\n{new_body}" if new_body else old_body
|
|
1752
|
+
result["title"] = target.title
|
|
1753
|
+
for field in ("tags", "aliases", "keywords"):
|
|
1754
|
+
merged: list[str] = []
|
|
1755
|
+
for value in list(getattr(target, field)) + list(result.get(field, [])):
|
|
1756
|
+
if isinstance(value, str) and value not in merged:
|
|
1757
|
+
merged.append(value)
|
|
1758
|
+
if merged or field in result:
|
|
1759
|
+
result[field] = merged
|
|
1760
|
+
return result
|
|
1714
1761
|
|
|
1715
1762
|
def _infer_update_target(
|
|
1716
1763
|
self,
|
|
@@ -2140,6 +2187,8 @@ class Processor:
|
|
|
2140
2187
|
# its own candidate; the aggregate shell itself is NO_CHANGE.
|
|
2141
2188
|
if candidate.get("worth") and is_aggregate_operational_text(candidate.get("memory")):
|
|
2142
2189
|
continue
|
|
2190
|
+
if candidate.get("worth") and is_attachment_followup_only_text(candidate.get("memory")):
|
|
2191
|
+
continue
|
|
2143
2192
|
candidate_scopes = list(candidate["scopes"])
|
|
2144
2193
|
# An automatic candidate with no reliable project attribution is
|
|
2145
2194
|
# retained as a retryable inbox turn, never silently promoted to
|
|
@@ -2286,20 +2335,13 @@ class Processor:
|
|
|
2286
2335
|
expected_scopes=candidate["scopes"],
|
|
2287
2336
|
expected_scope_source=candidate["scope_source"],
|
|
2288
2337
|
allow_no_change=True,
|
|
2289
|
-
|
|
2290
|
-
|
|
2291
|
-
|
|
2292
|
-
|
|
2293
|
-
|
|
2294
|
-
)
|
|
2295
|
-
else None
|
|
2296
|
-
),
|
|
2338
|
+
# The summarize stage may not reinterpret a gate
|
|
2339
|
+
# candidate, including CREATE candidates. Updates
|
|
2340
|
+
# additionally retain the active target's immutable
|
|
2341
|
+
# type below.
|
|
2342
|
+
expected_type=candidate.get("type"),
|
|
2297
2343
|
expected_update_memory_id=gate_update_target,
|
|
2298
|
-
expected_target_type=
|
|
2299
|
-
gate_target_type
|
|
2300
|
-
if gate_target_type == candidate.get("type")
|
|
2301
|
-
else None
|
|
2302
|
-
),
|
|
2344
|
+
expected_target_type=gate_target_type,
|
|
2303
2345
|
),
|
|
2304
2346
|
diagnostic_context={
|
|
2305
2347
|
"source": turn.source,
|
|
@@ -2322,6 +2364,10 @@ class Processor:
|
|
|
2322
2364
|
continue
|
|
2323
2365
|
if summary.get("decision") == NO_CHANGE_DECISION:
|
|
2324
2366
|
continue
|
|
2367
|
+
if is_attachment_followup_only_text(
|
|
2368
|
+
f"{summary.get('title', '')}\n{summary.get('body', '')}"
|
|
2369
|
+
):
|
|
2370
|
+
continue
|
|
2325
2371
|
if gate_update_target is not None:
|
|
2326
2372
|
summary_update_target = summary.get("update_memory_id")
|
|
2327
2373
|
if summary_update_target is None:
|
|
@@ -2338,6 +2384,8 @@ class Processor:
|
|
|
2338
2384
|
else:
|
|
2339
2385
|
summary = dict(summary)
|
|
2340
2386
|
summary["update_memory_id"] = gate_update_target
|
|
2387
|
+
target = self.service.read(gate_update_target, include_history=False)
|
|
2388
|
+
summary = self._merge_additive_project_plan_update(candidate, summary, target)
|
|
2341
2389
|
if summary["scopes"] == ["unscoped"] or summary.get("scope_source") == "insufficient_context":
|
|
2342
2390
|
self._defer_candidate(
|
|
2343
2391
|
turn_ref,
|
|
@@ -108,6 +108,13 @@ be independently retrieved and updated. Prefer one coherent current-state memory
|
|
|
108
108
|
over adjacent overlapping snapshots; do not use a score or fixed category rule.
|
|
109
109
|
Most ordinary turns should produce zero or one candidate; multiple require
|
|
110
110
|
genuinely independent future questions or actions.
|
|
111
|
+
Never combine a durable project rule or implementation-plan change with an
|
|
112
|
+
independent dated todo in one candidate. For example, a project plan/rule and
|
|
113
|
+
"reply or investigate by YYYY-MM-DD" are two future uses: emit separate
|
|
114
|
+
candidates, preserving each candidate's own evidence and project scope. A
|
|
115
|
+
date that is an intrinsic milestone of the same implementation plan may remain
|
|
116
|
+
inside that one project candidate. A candidate that combines these independent
|
|
117
|
+
uses is invalid and must be split before summarization.
|
|
111
118
|
Never persist an aggregate mailbox sweep, daily report, or "items to watch"
|
|
112
119
|
digest as one cross-project memory. Inspect each item and split genuinely
|
|
113
120
|
independent project risks or actionable todo items; if an item has no
|
|
@@ -115,6 +122,10 @@ independent future use, set worth=false. A one-off meeting with no durable
|
|
|
115
122
|
decision or commitment, a request awaiting PM acceptance, and pending feedback
|
|
116
123
|
or another intermediate status normally have worth=false. Keep an independent
|
|
117
124
|
project risk or concrete future action even when it came from a digest.
|
|
125
|
+
A PPT, attachment, material, document, or issue-list item described only as
|
|
126
|
+
"needs follow-up/review/processing" has no independent action and normally has
|
|
127
|
+
worth=false; keep it only when the evidence supplies a concrete remediation,
|
|
128
|
+
owner, deadline, delivery rule, migration, or durable conclusion.
|
|
118
129
|
An intermediate state such as a draft awaiting confirmation, preparation/
|
|
119
130
|
processing status, temporary error, repeated confirmation, or unconfirmed
|
|
120
131
|
suggestion normally has no independent future use and is worth=false. Keep one
|
|
@@ -151,6 +162,16 @@ same-use project targets remain, do not guess an update target. A candidate
|
|
|
151
162
|
that still combines a mailbox or daily digest shell with counts/statuses is not
|
|
152
163
|
atomic: set worth=false for the aggregate, and emit any independent concrete
|
|
153
164
|
action as a separate candidate.
|
|
165
|
+
If the same confirmed change applies to multiple same-project active memories,
|
|
166
|
+
emit one candidate per independently changed memory and set each exact
|
|
167
|
+
update_memory_id; never select one target and silently omit the others. Do not
|
|
168
|
+
target adjacent sent-mail, attachment, meeting, or archive records unless the
|
|
169
|
+
current evidence changes their own future-use topic.
|
|
170
|
+
An explicit implementation plan, project implementation plan, or plan
|
|
171
|
+
adjustment is type project. Do not label it fact merely because it came from
|
|
172
|
+
an email or because no existing project memory was retrieved. Keep an actual
|
|
173
|
+
todo as todo only when its future use is the action itself, not the durable
|
|
174
|
+
plan it mentions.
|
|
154
175
|
Return no prose, markdown fences, comments, or trailing text."""
|
|
155
176
|
|
|
156
177
|
|
|
@@ -182,7 +203,8 @@ still has a concrete future-use fact or action. If not, return exactly
|
|
|
182
203
|
{"decision":"NO_CHANGE"}; this is the only no-write summary response. Use it
|
|
183
204
|
for temporary delegation, waiting for feedback, a one-off meeting arrangement with
|
|
184
205
|
no durable outcome/decision/project constraint, or a subject/attachment-only item
|
|
185
|
-
without a concrete impact or conclusion.
|
|
206
|
+
without a concrete impact or conclusion. Attachment sizes, mail/message IDs,
|
|
207
|
+
and a generic "needs follow-up" phrase do not create such an impact.
|
|
186
208
|
Explicit remember mode never permits NO_CHANGE and must return the normal summary
|
|
187
209
|
object. When producing a normal automatic summary, copy the gate candidate's
|
|
188
210
|
scopes exactly. scope_source may be omitted to inherit the gate value, but an
|
|
@@ -260,6 +282,16 @@ plan target over same-topic sent-mail, attachment/archive, or meeting records;
|
|
|
260
282
|
the target type remains immutable. Never summarize a combined mailbox/daily
|
|
261
283
|
digest shell with counts or statuses; return NO_CHANGE for that aggregate,
|
|
262
284
|
while a separate atomic future-use action may still be summarized.
|
|
285
|
+
If the current confirmed change applies to multiple same-project active
|
|
286
|
+
memories, the gate will provide separate candidates and exact update targets;
|
|
287
|
+
preserve each candidate's target and do not collapse them into one summary.
|
|
288
|
+
Never combine a durable project rule or implementation-plan change with an
|
|
289
|
+
independent dated todo in one summary. A plan/rule and a dated reply,
|
|
290
|
+
investigation, submission, or follow-up are separate future uses; such output
|
|
291
|
+
is invalid and must be retried as separate gate candidates. A date that is an
|
|
292
|
+
intrinsic milestone of the same project plan may remain in its project summary.
|
|
293
|
+
An explicit implementation plan or plan adjustment must use type project;
|
|
294
|
+
preserve an existing update target's type when it is the same project plan.
|
|
263
295
|
Return no prose, markdown fences, comments, or trailing text."""
|
|
264
296
|
|
|
265
297
|
|
|
@@ -320,6 +352,18 @@ MIXED_PROJECT_SCOPES_CORRECTION = (
|
|
|
320
352
|
)
|
|
321
353
|
|
|
322
354
|
|
|
355
|
+
MIXED_FUTURE_USE_CORRECTION = (
|
|
356
|
+
"Previous output violated: mixed_future_use. Keep every candidate and "
|
|
357
|
+
"summary atomic. Split a durable project rule or implementation-plan "
|
|
358
|
+
"change from an independent dated todo such as a reply, investigation, "
|
|
359
|
+
"submission, or follow-up; preserve each part's own evidence and project "
|
|
360
|
+
"scope. A date intrinsic to the same implementation plan may remain in "
|
|
361
|
+
"that project candidate. Return separate gate candidates or return "
|
|
362
|
+
"NO_CHANGE for a part with no independent future use. Return only the "
|
|
363
|
+
"strict JSON object."
|
|
364
|
+
)
|
|
365
|
+
|
|
366
|
+
|
|
323
367
|
DUPLICATE_TARGET_CORRECTION = (
|
|
324
368
|
"Previous output violated: duplicate_update_target. Merge every candidate "
|
|
325
369
|
"that refers to the same active memory target into one candidate for one "
|