feat: update Mattermost integration to prefer local proxy mirror evidence and enhance context retrieval methods

This commit is contained in:
2026-05-20 07:11:06 -06:00
parent e081360a84
commit 3d4da1919a
19 changed files with 179 additions and 58 deletions

View File

@@ -26,11 +26,12 @@ Read:
Fresh communication evidence:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No communication evidence available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Instructions:
- if the sync command failed, stop and do not edit workspace memory
- prefer local proxy mirror evidence when present; legacy sync output is fallback evidence
- treat connector output as evidence, not automatically as project truth
- promote only explicit, project-relevant, high-confidence facts
- default destination is `project-knowledge/06-daily/$(date +%F).md`

View File

@@ -59,7 +59,7 @@ PY`
Latest Mattermost context, if available:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No Mattermost context available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Respond with:

View File

@@ -2,7 +2,7 @@
description: Force-sync Mattermost and answer from the latest matching message
---
Force-refresh Mattermost first, then answer the user's question from the refreshed inbox.
Refresh/read Mattermost first, then answer the user's question from the freshest evidence. Prefer the local proxy mirror when it is present; legacy sync output is fallback evidence.
Use this when the user asks for:
@@ -11,46 +11,13 @@ Use this when the user asks for:
- the latest Mattermost update
- the latest message in `fidelity-preguntas`
Run sync:
Run sync/fallback refresh:
!`start=$(date +%s); if [ -n "$AIW_MATTERMOST_SYNC_CMD" ]; then bash -lc "$AIW_MATTERMOST_SYNC_CMD"; elif [ -n "$FIDELITY_MATTERMOST_SYNC_CMD" ]; then bash -lc "$FIDELITY_MATTERMOST_SYNC_CMD"; elif [ -f scripts/mattermost/sync.sh ]; then bash scripts/mattermost/sync.sh; else echo "No Mattermost sync command is configured."; fi; status=$?; end=$(date +%s); echo "__MATTERMOST_SYNC_SECONDS__=$((end - start))"; exit "$status"`
Read a focused slice of refreshed Mattermost context:
Read a focused slice of refreshed Mattermost context, preferring the proxy mirror:
!`python3 - <<'PY'
import json
from pathlib import Path
paths = [
Path("ai/inbox/mattermost-latest.md"),
Path("scripts/mattermost/generated/mattermost_context.jsonl"),
]
source = next((path for path in paths if path.is_file() and path.stat().st_size > 0), None)
if not source:
print("No Mattermost context available after sync.")
raise SystemExit(0)
records = []
for line in source.read_text().splitlines():
line = line.strip()
if not line:
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError:
continue
manager_names = {"jeff", "jeff.dewitte"}
manager_records = [
record for record in records
if str(record.get("username", "")).lower() in manager_names
]
focused = manager_records[-10:] if manager_records else records[-15:]
for record in focused:
print(json.dumps(record, ensure_ascii=False))
PY`
!`python3 scripts/mattermost-proxy/read-context.py --mode focused`
User request:

View File

@@ -22,7 +22,7 @@ Today's log, if present:
Latest Mattermost context, if available:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No Mattermost context available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Detailed active work item files, if available:

View File

@@ -2,7 +2,7 @@
description: Sync Mattermost context and automatically promote high-confidence project memory
---
// turbo-all
Use the configured Mattermost sync command to fetch fresh communication context and maintain workspace memory automatically.
Use the configured Mattermost sync command and/or local proxy mirror evidence to fetch fresh communication context and maintain workspace memory automatically.
Preferred command sources:
@@ -14,12 +14,17 @@ Run the command and use its output as fresh communication context:
!`if [ -n "$AIW_MATTERMOST_SYNC_CMD" ]; then bash -lc "$AIW_MATTERMOST_SYNC_CMD"; elif [ -n "$FIDELITY_MATTERMOST_SYNC_CMD" ]; then bash -lc "$FIDELITY_MATTERMOST_SYNC_CMD"; elif [ -f scripts/mattermost/sync.sh ]; then bash scripts/mattermost/sync.sh; else echo "No Mattermost sync command is configured."; fi`
Fresh Mattermost evidence, preferring the proxy mirror:
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Use this command implicitly when the user asks for the latest or last Mattermost message, especially messages from Jeff or the current manager.
Then:
- if the command fails, stop there and do not edit any workspace files
- use `ai/inbox/mattermost-latest.md` if it exists and is non-empty
- prefer the local proxy mirror via `scripts/mattermost-proxy/read-context.py --mode latest` when it has evidence
- otherwise use `ai/inbox/mattermost-latest.md` if it exists and is non-empty
- otherwise use `scripts/mattermost/generated/mattermost_context.jsonl` if it exists and is non-empty
- apply the memory promotion rules from `agent-memory/memory/promotion-rules.md`
- treat Mattermost output as live communication evidence; the agent decides what becomes promoted memory

View File

@@ -22,11 +22,11 @@ Today's log, if present:
Current Mattermost inbox, if present:
!`if [ -f ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; else echo "No Mattermost inbox file is available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Generated Mattermost context, if present:
!`if [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No generated Mattermost context is available."; fi`
!`if [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No legacy generated Mattermost context is available."; fi`
User direction or facts to promote:

View File

@@ -79,7 +79,7 @@ Today's log, if present:
Latest refreshed Mattermost context, if present:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No refreshed Mattermost context available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode standup --today $(date +%F)`
Detailed active work item files, if available:

View File

@@ -36,7 +36,7 @@ Today's log, if present:
Latest Mattermost context, if available:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No Mattermost context available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Detailed active work item files, if available:

View File

@@ -66,7 +66,7 @@ PY`
Latest communication inbox, if available:
!`if [ -s ai/inbox/mattermost-latest.md ]; then cat ai/inbox/mattermost-latest.md; elif [ -s scripts/mattermost/generated/mattermost_context.jsonl ]; then cat scripts/mattermost/generated/mattermost_context.jsonl; else echo "No live communication context available."; fi`
!`python3 scripts/mattermost-proxy/read-context.py --mode latest`
Respond with:

View File

@@ -17,6 +17,7 @@ Behavior rules:
- Treat `scripts/memory/` as the project-agnostic access layer for note creation, project-knowledge search, Base queries, and health checks.
- Treat `scripts/obsidian/` as the current Obsidian adapter. Do not couple durable memory rules to Obsidian-specific behavior.
- Treat `ai/inbox/` and generated connector files as raw evidence only, not promoted memory.
- For Mattermost context, prefer the local proxy mirror in `ai/inbox/mattermost-mirror/` when present. Use `scripts/mattermost-proxy/read-context.py` or the mirror views (`latest.*`, `by-date/`, `channels/`, `threads/`) before falling back to legacy `ai/inbox/mattermost-latest.md` or `scripts/mattermost/generated/` artifacts.
- Keep Obsidian Bases clean: do not let templates in `project-knowledge/09-templates/` appear as real daily notes, work items, people, decisions, systems, or workstreams.
- Role mapping notes such as `project-knowledge/04-people/manager.md` are `type: role-map`; actual people profiles are `type: person`.
- When editing canonical project notes, update useful metadata at the same time: `updated`, `systems`, `workstreams`, `people`, `related`, `focus`, `work-items`, and `blockers` when applicable.
@@ -25,7 +26,7 @@ Behavior rules:
- Work item notes should preserve Jira ID/title and explicit relationships so standups, Bases, and graph navigation stay useful.
- Daily notes should include `focus`, `work-items`, and `blockers` when those values are clear.
- Before answering a prompt that depends on current state, verify the latest relevant files instead of relying only on conversation history.
- If the prompt asks for the latest Mattermost message, the last message from Jeff/current manager, or what someone just said, force a Mattermost refresh before answering and do not rely on stale inbox context.
- If the prompt asks for the latest Mattermost message, the last message from Jeff/current manager, or what someone just said, refresh or read the freshest Mattermost evidence before answering; the proxy mirror is the primary source when it is present, and legacy sync artifacts are fallback evidence.
- Treat latest-message prompts as read-first: answer from refreshed evidence and report memory update candidates instead of editing canonical memory by default.
- For learning-style questions, answer from known context and verified facts only; explicitly label unknowns, assumptions, and inferences.
- For learning sessions, prioritize durable architecture, process, ownership, debugging strategy, release mechanics, domain concepts, and decision rules over transient ticket status.

View File

@@ -17,6 +17,7 @@ Behavior rules:
- Treat `scripts/memory/` as the stable memory access layer.
- Treat tool-specific integrations such as Obsidian as replaceable adapters.
- Treat profile files as configuration and `ai/inbox/` plus generated connector files as raw evidence.
- For live communication context, prefer project-local mirror evidence under `ai/inbox/*-mirror/` through its reader script when available, then fall back to legacy inbox/generated connector artifacts.
- Keep Obsidian Bases clean by excluding templates and typing role maps separately from people.
- When updating canonical project notes, maintain relationship metadata and `updated` fields so project knowledge remains useful to both humans and agents.
- Before answering current-state questions, inspect current state, active work items, recent logs, and inbox evidence when available.

View File

@@ -43,9 +43,9 @@ Do not preemptively load broad context sets, all work-item files, or all process
- Maintain useful project-note properties when editing canonical notes, especially work-item relationships (`systems`, `workstreams`, `people`, `related`) and daily note fields (`focus`, `work-items`, `blockers`).
- Before answering questions that depend on current work state, inspect `project-knowledge/01-current/current-work.md` and the latest relevant daily note under `project-knowledge/06-daily/`.
- Prefer lazy loading over eager loading. Pull in only the smallest relevant files for the active task.
- If `ai/inbox/mattermost-latest.md` exists, inspect it for fresher communication context before answering standup, status, or manager-message prompts.
- If the user asks for the latest/last/recent Mattermost message, the latest message from Jeff/current manager, or what someone just said, synchronize Mattermost first instead of relying on existing inbox context.
- If automatic refresh is uncertain, use the explicit latest-message flow: run the Mattermost sync command, then answer from the refreshed inbox only.
- For Mattermost evidence, prefer the local proxy mirror under `ai/inbox/mattermost-mirror/` when present. Use `scripts/mattermost-proxy/read-context.py` or mirror views (`latest.*`, `by-date/`, `channels/`, `threads/`) before falling back to legacy `ai/inbox/mattermost-latest.md` or `scripts/mattermost/generated/` artifacts.
- If the user asks for the latest/last/recent Mattermost message, the latest message from Jeff/current manager, or what someone just said, use the explicit latest-message flow and answer from the freshest refreshed evidence; the proxy mirror is primary when available.
- If automatic refresh is uncertain and the proxy mirror is not available, run the Mattermost sync command, then answer from the refreshed inbox only.
- Treat latest-message flows as read-first. Report memory update candidates, but do not edit canonical memory from that command unless the user explicitly asks to promote the fact.
- For learning-style questions, answer from known context and verified facts only; label unknowns, assumptions, and inferences instead of inventing missing details.
- For learning sessions, prioritize durable architecture, process, ownership, debugging strategy, release mechanics, domain concepts, and decision rules over transient ticket status.

View File

@@ -1,7 +1,7 @@
---
type: agent-integration
status: active
updated: 2026-05-08
updated: 2026-05-19
tags:
- communication
- evidence
@@ -17,12 +17,16 @@ Communication connectors extract evidence. The agent decides what to promote.
Mattermost is the current live communication connector.
- Fresh output goes to `ai/inbox/mattermost-latest.md`.
- Generated extraction artifacts stay under `scripts/mattermost/generated/`.
- Primary local evidence is the Mattermost proxy mirror under `ai/inbox/mattermost-mirror/` when present.
- Prefer `ai/inbox/mattermost-mirror/latest.md` / `latest.jsonl` for latest-message context, `by-date/YYYY/MM/YYYY-MM-DD.jsonl` for daily/standup context, `channels/<channel>/YYYY/MM/YYYY-MM-DD.jsonl` for channel-specific context, and `threads/<root-or-post-id>.jsonl` for thread-specific context.
- Use `scripts/mattermost-proxy/read-context.py` from commands/workflows instead of reading ad hoc files; it prefers the proxy mirror and falls back to legacy sync artifacts.
- Legacy fresh output may still go to `ai/inbox/mattermost-latest.md`.
- Legacy generated extraction artifacts stay under `scripts/mattermost/generated/`.
- Failed syncs must not update project knowledge.
- Latest-message requests must refresh Mattermost before answering.
- Latest-message requests are read-first. The agent may identify a memory update candidate, but should not edit `project-knowledge/` from the latest-message command unless the user explicitly asks to promote the fact.
- Standup generation is a separate required-refresh flow: it must fetch Mattermost before drafting, even though general prompts should not sync automatically.
- If the proxy mirror is running, treat it as fresher than legacy `mattermost-latest.md` / generated JSONL. Do not ignore mirror evidence merely because a legacy sync command also ran.
- Do not refresh Mattermost just because a prompt mentions a manager or stakeholder.
- Treat document review, message polishing, translation, and "does this align with Jeff's expectations?" prompts as normal drafting tasks unless the user explicitly asks for the latest message or fresh Mattermost evidence.
- The OpenCode plugin syncs automatically only for explicit latest-message requests by default.

View File

@@ -4,8 +4,9 @@ This directory stores raw or semi-processed communication captured from Mattermo
Recommended usage:
- `mattermost-latest.md` contains the most recent capture
- `mattermost-mirror/` is the preferred local Mattermost evidence source when present. It stores `latest.*`, `by-date/`, `channels/`, `threads/`, and `refs/` views from the local proxy mirror.
- `mattermost-latest.md` contains the legacy most recent sync capture
- timestamped snapshots can be stored here if needed
- durable facts should be promoted into `vault/06-daily/`, `vault/01-current/`, `vault/02-work-items/`, `vault/03-context/`, `vault/04-people/`, or `vault/05-decisions/`
- durable facts should be promoted into `project-knowledge/06-daily/`, `project-knowledge/01-current/`, `project-knowledge/02-work-items/`, `project-knowledge/03-context/`, `project-knowledge/04-people/`, or `project-knowledge/05-decisions/`
This directory is intentionally treated as an inbox, not as the final source of truth.

View File

@@ -20,6 +20,7 @@ It keeps Fidelity-specific context, integrations, commands, and skills separate
## Communication Sources
- Live communication: Mattermost
- Preferred local Mattermost evidence source: proxy mirror under `ai/inbox/mattermost-mirror/` when present; legacy `ai/inbox/mattermost-latest.md` and `scripts/mattermost/generated/` are fallback evidence.
- Historical archive: Slack export
- Preferred channel naming: readable channel names instead of raw IDs
- Current high-signal channel: `fidelity-preguntas`

View File

@@ -8,7 +8,7 @@ Generate a standup update for the active project profile.
## Required refresh
- At the start of the day, fetch Mattermost before drafting.
- At the start of the day, fetch or read refreshed Mattermost evidence before drafting. Prefer the local proxy mirror through `scripts/mattermost-proxy/read-context.py` when it exists; legacy sync output is fallback evidence.
- Fetch both latest available messages and previous-workday activity when the connector supports both modes.
- If Mattermost refresh fails, say so internally and use only saved workspace memory with clear caution; do not invent fresher context.
- Do not skip communication refresh for standup just to reduce latency, because stale standups cost more time to correct later.

View File

@@ -59,7 +59,7 @@ Expected behavior:
- avoid interactive prompts
- return a non-zero exit code on failure
OpenCode can then use that output to refresh `ai/inbox/mattermost-latest.md` proactively.
OpenCode can then use that output to refresh `ai/inbox/mattermost-latest.md` proactively. When the local Mattermost proxy mirror is running, commands should prefer `ai/inbox/mattermost-mirror/` through `scripts/mattermost-proxy/read-context.py` and use legacy sync output as fallback evidence.
Historical Slack exports can also be imported through:

View File

@@ -0,0 +1,135 @@
#!/usr/bin/env python3
"""Read local Mattermost mirror evidence for agent prompts.
The proxy mirror under ai/inbox/mattermost-mirror is the preferred Mattermost
evidence source when present. This reader gives commands a stable way to load a
small focused view and fall back to the older sync artifacts when the mirror is
not available.
"""
from __future__ import annotations
import argparse
import json
from datetime import date, datetime, timedelta
from pathlib import Path
ROOT = Path(__file__).resolve().parents[2]
MIRROR_DIR = ROOT / "ai" / "inbox" / "mattermost-mirror"
LEGACY_LATEST = ROOT / "ai" / "inbox" / "mattermost-latest.md"
LEGACY_GENERATED = ROOT / "scripts" / "mattermost" / "generated" / "mattermost_context.jsonl"
def previous_workday(today: date) -> date:
day = today - timedelta(days=1)
while day.weekday() >= 5:
day -= timedelta(days=1)
return day
def daily_by_date_path(day: date) -> Path:
return MIRROR_DIR / "by-date" / f"{day:%Y}" / f"{day:%m}" / f"{day:%Y-%m-%d}.jsonl"
def print_file(path: Path) -> bool:
if path.is_file() and path.stat().st_size > 0:
print(path.read_text(encoding="utf-8"))
return True
return False
def read_jsonl(path: Path) -> list[dict]:
records = []
if not path.is_file():
return records
for line in path.read_text(encoding="utf-8").splitlines():
line = line.strip()
if not line:
continue
try:
records.append(json.loads(line))
except json.JSONDecodeError:
continue
return records
def print_jsonl(records: list[dict]) -> bool:
if not records:
return False
for record in records:
print(json.dumps(record, ensure_ascii=False, sort_keys=True))
return True
def fallback() -> None:
if print_file(LEGACY_LATEST):
return
if print_file(LEGACY_GENERATED):
return
print("No Mattermost context available.")
def mode_latest() -> None:
print("## Mattermost mirror latest context")
if print_file(MIRROR_DIR / "latest.md"):
return
records = read_jsonl(MIRROR_DIR / "latest.jsonl")
if print_jsonl(records):
return
print("No proxy mirror latest context available; falling back to legacy sync artifacts.")
fallback()
def mode_previous_workday(today_raw: str | None) -> None:
today = datetime.strptime(today_raw, "%Y-%m-%d").date() if today_raw else date.today()
day = previous_workday(today)
path = daily_by_date_path(day)
print(f"## Mattermost mirror previous-workday context ({day.isoformat()})")
records = read_jsonl(path)
if print_jsonl(records):
return
print("No proxy mirror previous-workday context available; falling back to latest context.")
mode_latest()
def mode_standup(today_raw: str | None) -> None:
mode_previous_workday(today_raw)
print("\n## Mattermost mirror latest context")
latest_md = MIRROR_DIR / "latest.md"
if latest_md.is_file() and latest_md.stat().st_size > 0:
print(latest_md.read_text(encoding="utf-8"))
def mode_focused() -> None:
records = read_jsonl(MIRROR_DIR / "latest.jsonl")
if not records:
records = read_jsonl(LEGACY_GENERATED)
if not records:
print("No Mattermost context available.")
return
manager_names = {"jeff", "jeff.dewitte"}
manager_records = [record for record in records if str(record.get("username", "")).lower() in manager_names]
focused = manager_records[-10:] if manager_records else records[-15:]
print_jsonl(focused)
def main() -> None:
parser = argparse.ArgumentParser()
parser.add_argument("--mode", choices=["latest", "previous-workday", "standup", "focused"], default="latest")
parser.add_argument("--today", default="")
args = parser.parse_args()
if args.mode == "latest":
mode_latest()
elif args.mode == "previous-workday":
mode_previous_workday(args.today or None)
elif args.mode == "standup":
mode_standup(args.today or None)
elif args.mode == "focused":
mode_focused()
if __name__ == "__main__":
main()

View File

@@ -2,6 +2,11 @@
This directory contains the workspace-local Mattermost extractor used by OpenCode to refresh communication context.
The preferred live Mattermost evidence source is now the proxy mirror under
`ai/inbox/mattermost-mirror/` when it is running. This legacy extractor remains
the fallback and explicit refresh path for commands that need a fresh pull from
the Mattermost API.
## Files
- `mattermost_context.py`