Compare commits
3 Commits
e081360a84
...
fdbf52f811
| Author | SHA1 | Date | |
|---|---|---|---|
| fdbf52f811 | |||
| ee35f70c3e | |||
| 3d4da1919a |
@@ -26,11 +26,12 @@ Read:
|
|||||||
|
|
||||||
Fresh communication evidence:
|
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:
|
Instructions:
|
||||||
|
|
||||||
- if the sync command failed, stop and do not edit workspace memory
|
- 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
|
- treat connector output as evidence, not automatically as project truth
|
||||||
- promote only explicit, project-relevant, high-confidence facts
|
- promote only explicit, project-relevant, high-confidence facts
|
||||||
- default destination is `project-knowledge/06-daily/$(date +%F).md`
|
- default destination is `project-knowledge/06-daily/$(date +%F).md`
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ PY`
|
|||||||
|
|
||||||
Latest Mattermost context, if available:
|
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:
|
Respond with:
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
description: Force-sync Mattermost and answer from the latest matching message
|
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:
|
Use this when the user asks for:
|
||||||
|
|
||||||
@@ -11,46 +11,13 @@ Use this when the user asks for:
|
|||||||
- the latest Mattermost update
|
- the latest Mattermost update
|
||||||
- the latest message in `fidelity-preguntas`
|
- 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"`
|
!`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'
|
!`python3 scripts/mattermost-proxy/read-context.py --mode focused`
|
||||||
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`
|
|
||||||
|
|
||||||
User request:
|
User request:
|
||||||
|
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ Today's log, if present:
|
|||||||
|
|
||||||
Latest Mattermost context, if available:
|
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:
|
Detailed active work item files, if available:
|
||||||
|
|
||||||
|
|||||||
@@ -2,7 +2,7 @@
|
|||||||
description: Sync Mattermost context and automatically promote high-confidence project memory
|
description: Sync Mattermost context and automatically promote high-confidence project memory
|
||||||
---
|
---
|
||||||
// turbo-all
|
// 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:
|
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`
|
!`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.
|
Use this command implicitly when the user asks for the latest or last Mattermost message, especially messages from Jeff or the current manager.
|
||||||
|
|
||||||
Then:
|
Then:
|
||||||
|
|
||||||
- if the command fails, stop there and do not edit any workspace files
|
- 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
|
- 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`
|
- 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
|
- treat Mattermost output as live communication evidence; the agent decides what becomes promoted memory
|
||||||
|
|||||||
@@ -22,11 +22,11 @@ Today's log, if present:
|
|||||||
|
|
||||||
Current Mattermost inbox, 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:
|
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:
|
User direction or facts to promote:
|
||||||
|
|
||||||
|
|||||||
@@ -79,7 +79,7 @@ Today's log, if present:
|
|||||||
|
|
||||||
Latest refreshed Mattermost context, 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:
|
Detailed active work item files, if available:
|
||||||
|
|
||||||
|
|||||||
@@ -36,7 +36,7 @@ Today's log, if present:
|
|||||||
|
|
||||||
Latest Mattermost context, if available:
|
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:
|
Detailed active work item files, if available:
|
||||||
|
|
||||||
|
|||||||
@@ -66,7 +66,7 @@ PY`
|
|||||||
|
|
||||||
Latest communication inbox, if available:
|
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:
|
Respond with:
|
||||||
|
|
||||||
|
|||||||
@@ -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/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 `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.
|
- 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.
|
- 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`.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- 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-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.
|
- For learning sessions, prioritize durable architecture, process, ownership, debugging strategy, release mechanics, domain concepts, and decision rules over transient ticket status.
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ Behavior rules:
|
|||||||
- Treat `scripts/memory/` as the stable memory access layer.
|
- Treat `scripts/memory/` as the stable memory access layer.
|
||||||
- Treat tool-specific integrations such as Obsidian as replaceable adapters.
|
- 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.
|
- 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.
|
- 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.
|
- 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.
|
- Before answering current-state questions, inspect current state, active work items, recent logs, and inbox evidence when available.
|
||||||
|
|||||||
@@ -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`).
|
- 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/`.
|
- 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.
|
- 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.
|
- 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, synchronize Mattermost first instead of relying on existing inbox context.
|
- 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, use the explicit latest-message flow: run the Mattermost sync command, then answer from the refreshed inbox only.
|
- 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.
|
- 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-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.
|
- For learning sessions, prioritize durable architecture, process, ownership, debugging strategy, release mechanics, domain concepts, and decision rules over transient ticket status.
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
---
|
---
|
||||||
type: agent-integration
|
type: agent-integration
|
||||||
status: active
|
status: active
|
||||||
updated: 2026-05-08
|
updated: 2026-05-19
|
||||||
tags:
|
tags:
|
||||||
- communication
|
- communication
|
||||||
- evidence
|
- evidence
|
||||||
@@ -17,12 +17,17 @@ Communication connectors extract evidence. The agent decides what to promote.
|
|||||||
|
|
||||||
Mattermost is the current live communication connector.
|
Mattermost is the current live communication connector.
|
||||||
|
|
||||||
- Fresh output goes to `ai/inbox/mattermost-latest.md`.
|
- Primary local evidence is the Mattermost proxy mirror under `ai/inbox/mattermost-mirror/` when present.
|
||||||
- Generated extraction artifacts stay under `scripts/mattermost/generated/`.
|
- 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.
|
- Failed syncs must not update project knowledge.
|
||||||
- Latest-message requests must refresh Mattermost before answering.
|
- 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.
|
- 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.
|
- Standup generation is a separate required-refresh flow: it must fetch Mattermost before drafting, even though general prompts should not sync automatically.
|
||||||
|
- Standup reads should use the focused reader mode, `scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD`, which reads date-bucketed previous-workday/today records and should use the active profile's configured `AIW_MATTERMOST_CONTEXT_CHANNELS` when available. Avoid loading broad mirror `latest.md` into standup prompts because it may include stale or unrelated channels and waste tokens. Keep project-specific channel names out of reusable connector code.
|
||||||
|
- 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.
|
- 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.
|
- 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.
|
- The OpenCode plugin syncs automatically only for explicit latest-message requests by default.
|
||||||
|
|||||||
@@ -4,8 +4,9 @@ This directory stores raw or semi-processed communication captured from Mattermo
|
|||||||
|
|
||||||
Recommended usage:
|
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
|
- 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.
|
This directory is intentionally treated as an inbox, not as the final source of truth.
|
||||||
|
|||||||
@@ -20,9 +20,11 @@ It keeps Fidelity-specific context, integrations, commands, and skills separate
|
|||||||
## Communication Sources
|
## Communication Sources
|
||||||
|
|
||||||
- Live communication: Mattermost
|
- 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
|
- Historical archive: Slack export
|
||||||
- Preferred channel naming: readable channel names instead of raw IDs
|
- Preferred channel naming: readable channel names instead of raw IDs
|
||||||
- Current high-signal channel: `fidelity-preguntas`
|
- Current high-signal channel: `fidelity-preguntas`
|
||||||
|
- Focused Mattermost context for standups/latest project reads should come from configured profile/environment channels, not hardcoded connector defaults. For this profile, the useful context-channel set is currently `fidelity-preguntas`, `fidelity-standup`, `fidelity-code-review`, `fidelity-interface-meetings-on-calendar-outlook-team-etc`, and `dm-david--jeff`; keep that list in local `.env` as `AIW_MATTERMOST_CONTEXT_CHANNELS` or an equivalent profile setup when using the reusable Mattermost reader.
|
||||||
|
|
||||||
Compatibility environment variables:
|
Compatibility environment variables:
|
||||||
|
|
||||||
@@ -38,6 +40,7 @@ Generic variables should be preferred for new setup:
|
|||||||
- `AIW_SLACK_EXPORT_PATH`
|
- `AIW_SLACK_EXPORT_PATH`
|
||||||
- `AIW_CHANNEL_PREFIX=fidelity`
|
- `AIW_CHANNEL_PREFIX=fidelity`
|
||||||
- `AIW_PROJECT_PROFILE=fidelity`
|
- `AIW_PROJECT_PROFILE=fidelity`
|
||||||
|
- `AIW_MATTERMOST_CONTEXT_CHANNELS=fidelity-preguntas,fidelity-standup,fidelity-code-review,fidelity-interface-meetings-on-calendar-outlook-team-etc,dm-david--jeff`
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ tags:
|
|||||||
- SampleApp should support explicit validation of both UIKit-host and SwiftUI-host scenarios. XFlowSDK should remain self-aware of which dismissal mechanism to use based on the active integration path, so SampleApp can exercise both paths without relying on stale singleton/default host-mode state.
|
- SampleApp should support explicit validation of both UIKit-host and SwiftUI-host scenarios. XFlowSDK should remain self-aware of which dismissal mechanism to use based on the active integration path, so SampleApp can exercise both paths without relying on stale singleton/default host-mode state.
|
||||||
- May 14 SampleApp iteration: SampleApp was updated to choose between UIKit-host (`initialViewController(...)`) and SwiftUI-host (`makeInitialFlowView(...)`) paths from the existing `Use SwiftUI` setting / feature-toggle path. XFlowSDK entrypoints now prepare the matching dismissal mechanism, and the SampleApp SwiftUI-host rendering was refined to use a `ViewBuilder`/`flowContent` approach rather than storing an `AnyView` in state. Next validation is SampleApp back-to-back host-mode switching plus Fid4 AccountLink revalidation in both host modes.
|
- May 14 SampleApp iteration: SampleApp was updated to choose between UIKit-host (`initialViewController(...)`) and SwiftUI-host (`makeInitialFlowView(...)`) paths from the existing `Use SwiftUI` setting / feature-toggle path. XFlowSDK entrypoints now prepare the matching dismissal mechanism, and the SampleApp SwiftUI-host rendering was refined to use a `ViewBuilder`/`flowContent` approach rather than storing an `AnyView` in state. Next validation is SampleApp back-to-back host-mode switching plus Fid4 AccountLink revalidation in both host modes.
|
||||||
- Jeff recommended expanding validation beyond AccountLink before PR review: test as many current Fid4 XFlow entry and dismissal points as possible in both default SwiftUI-host and forced UIKit-host modes, with particular attention to AO flows. Use the existing Confluence entry-point list as a starting point, but verify it against current code and temporary logs because some entries may be stale.
|
- Jeff recommended expanding validation beyond AccountLink before PR review: test as many current Fid4 XFlow entry and dismissal points as possible in both default SwiftUI-host and forced UIKit-host modes, with particular attention to AO flows. Use the existing Confluence entry-point list as a starting point, but verify it against current code and temporary logs because some entries may be stale.
|
||||||
|
- May 20 validation: sessions 004 and 005 show SwiftUI-host load, dismissal, and delegate cleanup coverage for HybridBrokerageAccountOpening and accountlink, but HybridYouthAccountOpening has load/start coverage only and needs complete dismissal coverage.
|
||||||
|
- Remaining validation gaps on the combined PDIAP-12284 / PDIAP-15836 branch include AO deep-link coverage, Fid4 surface attribution for launch wrappers, common-launch flows, HybridBloomAccountOpening, and explicit UIKit-host mode validation. Plans are to open a draft PR today while validation continues.
|
||||||
- Keep the separate `HybridBrokerageAccountOpening` / `JointIdentityCheck` scenario out of `PDIAP-15765` scope unless later evidence proves it belongs there
|
- Keep the separate `HybridBrokerageAccountOpening` / `JointIdentityCheck` scenario out of `PDIAP-15765` scope unless later evidence proves it belongs there
|
||||||
- Include feature-flag planning for the broader UIKit-removal spike, including dismissal sequencing changes that affect consumers
|
- Include feature-flag planning for the broader UIKit-removal spike, including dismissal sequencing changes that affect consumers
|
||||||
- Thoroughly verify current `ApexBridgingAddressComponent` / rule-loading usage before describing it as inactive or dead code
|
- Thoroughly verify current `ApexBridgingAddressComponent` / rule-loading usage before describing it as inactive or dead code
|
||||||
|
|||||||
@@ -28,7 +28,7 @@ Update the per-ticket files first when scope, status, sequencing, or communicati
|
|||||||
|
|
||||||
- `PDIAP-12284` - Remove UIKit wrapping from XFlow
|
- `PDIAP-12284` - Remove UIKit wrapping from XFlow
|
||||||
Detail: `project-knowledge/02-work-items/pdiap-12284.md`
|
Detail: `project-knowledge/02-work-items/pdiap-12284.md`
|
||||||
Current note: moved to In Progress on May 12 and should be handled with `PDIAP-15836` in the same branch. Quy confirmed both stories can remain In Progress together, so no immediate Jira restructuring is required. Current implementation direction is to avoid Fid4 per-flow host-mode mapping and instead evaluate XFlowViewMaker-owned global host-mode resolution, with SwiftUI as default and `UIHostingController` only as an explicit temporary fallback. May 12 implementation pass resolved earlier shape concerns with neutral enum names, `iOS-XflowUIKitHostEnabled`, SwiftUI default semantics, and `AnyView` removed from builder internals. Fid4 now compiles after manual dependency/build handling. May 13 AccountLink runtime validation looks good for both SwiftUI-default and forced UIKit-host paths. May 14 SampleApp work added explicit UIKit-host vs SwiftUI-host validation paths and refined SwiftUI-host rendering to avoid local `AnyView` state, using a `ViewBuilder`/`flowContent` path instead. Next validation is SampleApp back-to-back mode switching and broader Fid4 entry/dismissal validation in both host modes, especially AO flows, per Jeff's review guidance.
|
Current note: moved to In Progress on May 12 and should be handled with `PDIAP-15836` in the same branch. Quy confirmed both stories can remain In Progress together, so no immediate Jira restructuring is required. Current implementation direction is to avoid Fid4 per-flow host-mode mapping and instead evaluate XFlowViewMaker-owned global host-mode resolution, with SwiftUI as default and `UIHostingController` only as an explicit temporary fallback. May 12 implementation pass resolved earlier shape concerns with neutral enum names, `iOS-XflowUIKitHostEnabled`, SwiftUI default semantics, and `AnyView` removed from builder internals. Fid4 now compiles after manual dependency/build handling. May 13 AccountLink runtime validation looks good for both SwiftUI-default and forced UIKit-host paths. May 14 SampleApp work added explicit UIKit-host vs SwiftUI-host validation paths. May 20 status: sessions 004 and 005 validated SwiftUI-host load, dismissal, and delegate cleanup for HybridBrokerageAccountOpening and accountlink, but HybridYouthAccountOpening needs complete dismissal coverage. Remaining gaps include AO deep-link coverage, launch wrappers, common-launch, HybridBloomAccountOpening, and explicit UIKit-host mode. Plan to open a draft PR today while validation continues.
|
||||||
|
|
||||||
## Backlog / Future Reference
|
## Backlog / Future Reference
|
||||||
|
|
||||||
|
|||||||
@@ -36,6 +36,7 @@ tags:
|
|||||||
- May 14 SampleApp iteration updated the sample to exercise both host scenarios explicitly: UIKit-host through `initialViewController(...)` and SwiftUI-host through `makeInitialFlowView(...)`, selected by the existing `Use SwiftUI` / feature-toggle path. XFlowSDK entrypoints should prepare their own matching dismissal mechanism so `endActivity` cannot route to the SwiftUI subject unless the SwiftUI host is actually active.
|
- May 14 SampleApp iteration updated the sample to exercise both host scenarios explicitly: UIKit-host through `initialViewController(...)` and SwiftUI-host through `makeInitialFlowView(...)`, selected by the existing `Use SwiftUI` / feature-toggle path. XFlowSDK entrypoints should prepare their own matching dismissal mechanism so `endActivity` cannot route to the SwiftUI subject unless the SwiftUI host is actually active.
|
||||||
- The SampleApp SwiftUI-host rendering was refined to avoid storing `AnyView` in state; SwiftUI-host content now lives behind a `ViewBuilder` / `flowContent` rendering path. This keeps local SampleApp code aligned with the branch goal of avoiding unnecessary `AnyView`, while preserving type erasure only at true public boundaries such as the existing XFlowViewMaker boundary if still required.
|
- The SampleApp SwiftUI-host rendering was refined to avoid storing `AnyView` in state; SwiftUI-host content now lives behind a `ViewBuilder` / `flowContent` rendering path. This keeps local SampleApp code aligned with the branch goal of avoiding unnecessary `AnyView`, while preserving type erasure only at true public boundaries such as the existing XFlowViewMaker boundary if still required.
|
||||||
- Jeff recommended expanding validation before review: test as many current Fid4 XFlow entry and dismissal points as possible in both default SwiftUI-host and forced UIKit-host modes, with particular attention to AO flows. Use the prior Confluence entry-point list as a starting point, but verify it against current code and temporary runtime logs before treating it as complete.
|
- Jeff recommended expanding validation before review: test as many current Fid4 XFlow entry and dismissal points as possible in both default SwiftUI-host and forced UIKit-host modes, with particular attention to AO flows. Use the prior Confluence entry-point list as a starting point, but verify it against current code and temporary runtime logs before treating it as complete.
|
||||||
|
- May 20 status: validation sessions 004 and 005 confirmed SwiftUI-host load, dismissal, and delegate cleanup for HybridBrokerageAccountOpening and accountlink, but HybridYouthAccountOpening needs complete dismissal coverage. Gaps remain in AO deep-link coverage, launch wrappers, common-launch, HybridBloomAccountOpening, and explicit UIKit-host validation. Plan to open a draft PR today while continuing validation.
|
||||||
|
|
||||||
---
|
---
|
||||||
|
|
||||||
|
|||||||
@@ -27,6 +27,7 @@ tags:
|
|||||||
- May 13 AccountLink runtime validation looks good in both SwiftUI-default and forced UIKit-host modes. Both paths preserved the intended dismissal sequencing, with delegate callbacks and session cleanup after confirmed dismissal.
|
- May 13 AccountLink runtime validation looks good in both SwiftUI-default and forced UIKit-host modes. Both paths preserved the intended dismissal sequencing, with delegate callbacks and session cleanup after confirmed dismissal.
|
||||||
- May 14 SampleApp work refined dismissal validation coverage: SampleApp can exercise both UIKit-host and SwiftUI-host paths, while XFlowSDK entrypoints prepare the matching dismissal mechanism for the active integration path. This supports validating that UIKit dismiss completion and SwiftUI `XFlowDismissalHost` lifecycle sequencing both converge on the canonical delegate/session teardown path.
|
- May 14 SampleApp work refined dismissal validation coverage: SampleApp can exercise both UIKit-host and SwiftUI-host paths, while XFlowSDK entrypoints prepare the matching dismissal mechanism for the active integration path. This supports validating that UIKit dismiss completion and SwiftUI `XFlowDismissalHost` lifecycle sequencing both converge on the canonical delegate/session teardown path.
|
||||||
- Jeff recommended broad Fid4 validation before PR review: test as many current XFlow entry and dismissal points as possible in both host modes, especially AO flows, and use temporary logs to confirm the active entry path, host mode, dismissal mechanism, delegate callbacks, and session cleanup.
|
- Jeff recommended broad Fid4 validation before PR review: test as many current XFlow entry and dismissal points as possible in both host modes, especially AO flows, and use temporary logs to confirm the active entry path, host mode, dismissal mechanism, delegate callbacks, and session cleanup.
|
||||||
|
- May 20 status: validation sessions 004 and 005 confirmed SwiftUI-host load, dismissal, and delegate cleanup for HybridBrokerageAccountOpening and accountlink, but HybridYouthAccountOpening needs complete dismissal coverage. Gaps remain in AO deep-link coverage, launch wrappers, common-launch, HybridBloomAccountOpening, and explicit UIKit-host validation. Plan to open a draft PR today while continuing validation.
|
||||||
- Quy confirmed on May 13 that this story and `PDIAP-12284` can both remain In Progress together, so no immediate Jira restructuring is required.
|
- Quy confirmed on May 13 that this story and `PDIAP-12284` can both remain In Progress together, so no immediate Jira restructuring is required.
|
||||||
- Sequenced after `PDIAP-15838` source work, but merge/release is delayed until after REST-transition consumer validation
|
- Sequenced after `PDIAP-15838` source work, but merge/release is delayed until after REST-transition consumer validation
|
||||||
- Sized at `8` points
|
- Sized at `8` points
|
||||||
|
|||||||
26
project-knowledge/06-daily/2026-05-20.md
Normal file
26
project-knowledge/06-daily/2026-05-20.md
Normal file
@@ -0,0 +1,26 @@
|
|||||||
|
---
|
||||||
|
type: daily
|
||||||
|
project: fidelity
|
||||||
|
date: 2026-05-20
|
||||||
|
status: active
|
||||||
|
focus: [pdiap-12284, pdiap-15836, duplicate-ao-report]
|
||||||
|
work-items: [PDIAP-12284, PDIAP-15836]
|
||||||
|
blockers: [duplicate-ao-report-waiting-on-adam-discourse-details]
|
||||||
|
tags:
|
||||||
|
- daily
|
||||||
|
- fidelity
|
||||||
|
updated: 2026-05-20
|
||||||
|
---
|
||||||
|
|
||||||
|
# 2026-05-20
|
||||||
|
|
||||||
|
## Work Planned / Current State
|
||||||
|
|
||||||
|
- Continue `PDIAP-12284` / `PDIAP-15836` validation across current Fid4 XFlow entry points and dismissal paths.
|
||||||
|
- Current validation evidence from sessions `004` and `005` shows SwiftUI-host load/dismiss/delegate-cleanup coverage for `HybridBrokerageAccountOpening` and `accountlink`; `HybridYouthAccountOpening` has load/start coverage only and still needs complete dismissal coverage.
|
||||||
|
- Remaining validation gaps include AO deep-link coverage, Fid4 surface attribution for launch wrappers, common-launch flows, `HybridBloomAccountOpening`, and explicit UIKit-host mode validation.
|
||||||
|
- Plan to open a draft PR today for the combined `PDIAP-12284` / `PDIAP-15836` branch while validation continues.
|
||||||
|
|
||||||
|
## Work Done
|
||||||
|
|
||||||
|
- Previous-day project evidence confirms the duplicate account-opening investigation is still waiting on Adam's Discourse details; no new `xflog` Discourse post had been seen yet.
|
||||||
@@ -8,8 +8,8 @@ Generate a standup update for the active project profile.
|
|||||||
|
|
||||||
## Required refresh
|
## 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.
|
- Fetch focused standup evidence with `python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD`; this reads previous-workday and today records from date-bucketed mirror files and should filter through the active profile's configured `AIW_MATTERMOST_CONTEXT_CHANNELS` when available. Do not read broad `latest.md` for standups unless the focused date-bucketed view is unavailable and you explicitly label the fallback as broad/noisy.
|
||||||
- If Mattermost refresh fails, say so internally and use only saved workspace memory with clear caution; do not invent fresher context.
|
- 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.
|
- Do not skip communication refresh for standup just to reduce latency, because stale standups cost more time to correct later.
|
||||||
|
|
||||||
|
|||||||
@@ -59,7 +59,7 @@ Expected behavior:
|
|||||||
- avoid interactive prompts
|
- avoid interactive prompts
|
||||||
- return a non-zero exit code on failure
|
- 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:
|
Historical Slack exports can also be imported through:
|
||||||
|
|
||||||
|
|||||||
@@ -19,6 +19,13 @@ MATTERMOST_MIRROR_LATEST_LIMIT=200
|
|||||||
# Optional channel allowlist. Comma-separated channel IDs. Empty means all captured channels.
|
# Optional channel allowlist. Comma-separated channel IDs. Empty means all captured channels.
|
||||||
MATTERMOST_MIRROR_CHANNEL_IDS=
|
MATTERMOST_MIRROR_CHANNEL_IDS=
|
||||||
|
|
||||||
|
# Optional AI context channel filter for reader commands such as:
|
||||||
|
# python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD
|
||||||
|
# Use readable channel names or channel IDs. Keep project-specific values in your local .env
|
||||||
|
# or active profile setup, not in reusable scripts.
|
||||||
|
# Example: project-main,project-standup,dm-you--manager
|
||||||
|
AIW_MATTERMOST_CONTEXT_CHANNELS=
|
||||||
|
|
||||||
# Write compact raw REST/WebSocket evidence in addition to normalized messages.
|
# Write compact raw REST/WebSocket evidence in addition to normalized messages.
|
||||||
# Keep disabled by default to avoid large files.
|
# Keep disabled by default to avoid large files.
|
||||||
MATTERMOST_MIRROR_WRITE_RAW=0
|
MATTERMOST_MIRROR_WRITE_RAW=0
|
||||||
|
|||||||
@@ -69,6 +69,30 @@ and `threads/...` when a single discussion thread is the relevant evidence.
|
|||||||
This mirrors Slack's export pattern of one folder per conversation with one file
|
This mirrors Slack's export pattern of one folder per conversation with one file
|
||||||
per date, while adding Mattermost-specific thread views.
|
per date, while adding Mattermost-specific thread views.
|
||||||
|
|
||||||
|
For standup generation, prefer the focused reader instead of loading broad
|
||||||
|
`latest.md` directly:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD
|
||||||
|
```
|
||||||
|
|
||||||
|
`standup` mode reads only date-bucketed records for the previous workday and
|
||||||
|
today. To avoid spending tokens on unrelated channels or stale global `latest.md`
|
||||||
|
content, configure project-specific context channels in the connector-local
|
||||||
|
`.env` or pass them explicitly. Keep those channel values out of reusable scripts.
|
||||||
|
|
||||||
|
```bash
|
||||||
|
AIW_MATTERMOST_CONTEXT_CHANNELS="project-main,project-standup,dm-you--manager" \
|
||||||
|
python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD
|
||||||
|
|
||||||
|
python3 scripts/mattermost-proxy/read-context.py --mode standup --today YYYY-MM-DD \
|
||||||
|
--channels "project-main,project-standup,dm-you--manager"
|
||||||
|
```
|
||||||
|
|
||||||
|
If no context channel filter is configured, `standup` mode still avoids
|
||||||
|
`latest.md` and reads date-bucketed records only, but it will include all mirrored
|
||||||
|
channels for those dates.
|
||||||
|
|
||||||
Direct-message channels are labeled as `dm-<user-a>--<user-b>` when the mirror
|
Direct-message channels are labeled as `dm-<user-a>--<user-b>` when the mirror
|
||||||
has seen enough user metadata to resolve the Mattermost channel ID. Group DMs
|
has seen enough user metadata to resolve the Mattermost channel ID. Group DMs
|
||||||
use `group-...`. If a DM was first captured before the relevant user metadata
|
use `group-...`. If a DM was first captured before the relevant user metadata
|
||||||
@@ -119,6 +143,7 @@ Each line in the normalized JSONL contains:
|
|||||||
- `MATTERMOST_MIRROR_CHANNEL_IDS`: optional comma-separated channel ID allowlist.
|
- `MATTERMOST_MIRROR_CHANNEL_IDS`: optional comma-separated channel ID allowlist.
|
||||||
- `MATTERMOST_MIRROR_WRITE_RAW`: set to `1` to save compact raw REST/WebSocket evidence.
|
- `MATTERMOST_MIRROR_WRITE_RAW`: set to `1` to save compact raw REST/WebSocket evidence.
|
||||||
- `MATTERMOST_APP_PATH`: Mattermost Desktop `.app` bundle path.
|
- `MATTERMOST_APP_PATH`: Mattermost Desktop `.app` bundle path.
|
||||||
|
- `AIW_MATTERMOST_PROJECT_CHANNELS`: optional comma-separated channel names or IDs for focused standup reads.
|
||||||
|
|
||||||
## Troubleshooting
|
## Troubleshooting
|
||||||
|
|
||||||
|
|||||||
227
scripts/mattermost-proxy/read-context.py
Executable file
227
scripts/mattermost-proxy/read-context.py
Executable file
@@ -0,0 +1,227 @@
|
|||||||
|
#!/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
|
||||||
|
import os
|
||||||
|
import shlex
|
||||||
|
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"
|
||||||
|
LOCAL_ENV = Path(__file__).resolve().parent / ".env"
|
||||||
|
|
||||||
|
|
||||||
|
def load_local_env(path: Path = LOCAL_ENV) -> None:
|
||||||
|
"""Load simple KEY=VALUE pairs from the connector-local .env.
|
||||||
|
|
||||||
|
Existing process environment values win. This keeps the reusable reader
|
||||||
|
project-agnostic while allowing each workspace/profile to provide its own
|
||||||
|
channel filters without hardcoding them in Python.
|
||||||
|
"""
|
||||||
|
if not path.is_file():
|
||||||
|
return
|
||||||
|
for raw_line in path.read_text(encoding="utf-8").splitlines():
|
||||||
|
line = raw_line.strip()
|
||||||
|
if not line or line.startswith("#") or "=" not in line:
|
||||||
|
continue
|
||||||
|
if line.startswith("export "):
|
||||||
|
line = line[len("export ") :].strip()
|
||||||
|
key, value = line.split("=", 1)
|
||||||
|
key = key.strip()
|
||||||
|
value = value.strip()
|
||||||
|
if not key or key in os.environ:
|
||||||
|
continue
|
||||||
|
try:
|
||||||
|
parsed = shlex.split(value, comments=False, posix=True)
|
||||||
|
value = parsed[0] if parsed else ""
|
||||||
|
except ValueError:
|
||||||
|
value = value.strip('"\'')
|
||||||
|
os.environ[key] = value
|
||||||
|
|
||||||
|
|
||||||
|
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 parse_channels(raw: str | None) -> set[str]:
|
||||||
|
if not raw:
|
||||||
|
env_raw = os.getenv("AIW_MATTERMOST_CONTEXT_CHANNELS", "") or os.getenv(
|
||||||
|
"AIW_MATTERMOST_PROJECT_CHANNELS", ""
|
||||||
|
)
|
||||||
|
raw = env_raw
|
||||||
|
if not raw:
|
||||||
|
return set()
|
||||||
|
return {item.strip() for item in raw.split(",") if item.strip()}
|
||||||
|
|
||||||
|
|
||||||
|
def filter_channels(records: list[dict], channels: set[str] | None) -> list[dict]:
|
||||||
|
if not channels:
|
||||||
|
return records
|
||||||
|
lowered = {channel.lower() for channel in channels}
|
||||||
|
return [
|
||||||
|
record
|
||||||
|
for record in records
|
||||||
|
if str(record.get("channel_name", "")).lower() in lowered
|
||||||
|
or str(record.get("channel_id", "")).lower() in lowered
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
|
def trim_records(records: list[dict], limit: int | None) -> list[dict]:
|
||||||
|
if limit is None or limit <= 0 or len(records) <= limit:
|
||||||
|
return records
|
||||||
|
return records[-limit:]
|
||||||
|
|
||||||
|
|
||||||
|
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 print_records_section(title: str, records: list[dict], limit: int | None = None) -> bool:
|
||||||
|
print(title)
|
||||||
|
records = trim_records(records, limit)
|
||||||
|
if print_jsonl(records):
|
||||||
|
return True
|
||||||
|
print("No matching Mattermost mirror records.")
|
||||||
|
return False
|
||||||
|
|
||||||
|
|
||||||
|
def mode_previous_workday(today_raw: str | None, channels: set[str] | None = None, limit: int | None = 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)
|
||||||
|
records = filter_channels(read_jsonl(path), channels)
|
||||||
|
if print_records_section(f"## Mattermost mirror previous-workday context ({day.isoformat()})", records, limit):
|
||||||
|
return
|
||||||
|
if channels:
|
||||||
|
print("Filtered to project channels: " + ", ".join(sorted(channels)))
|
||||||
|
print("No proxy mirror previous-workday project context available; not falling back to broad latest context.")
|
||||||
|
|
||||||
|
|
||||||
|
def mode_standup(today_raw: str | None, channels: set[str], limit: int | None) -> None:
|
||||||
|
"""Print a compact standup-focused view.
|
||||||
|
|
||||||
|
Standup mode intentionally avoids latest.md because latest.md is a bounded
|
||||||
|
global window and can contain stale or unrelated channels. Use date-bucketed
|
||||||
|
mirror files filtered to known project channels instead.
|
||||||
|
"""
|
||||||
|
today = datetime.strptime(today_raw, "%Y-%m-%d").date() if today_raw else date.today()
|
||||||
|
day = previous_workday(today)
|
||||||
|
previous_records = filter_channels(read_jsonl(daily_by_date_path(day)), channels)
|
||||||
|
today_records = filter_channels(read_jsonl(daily_by_date_path(today)), channels)
|
||||||
|
|
||||||
|
print("## Mattermost mirror standup context")
|
||||||
|
if channels:
|
||||||
|
print("Filtered to configured context channels: " + ", ".join(sorted(channels)))
|
||||||
|
else:
|
||||||
|
print("No context channel filter configured; using all mirrored date-bucket records.")
|
||||||
|
print_records_section(f"\n### Previous workday ({day.isoformat()})", previous_records, limit)
|
||||||
|
print_records_section(f"\n### Today so far ({today.isoformat()})", today_records, limit)
|
||||||
|
|
||||||
|
|
||||||
|
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:
|
||||||
|
load_local_env()
|
||||||
|
|
||||||
|
parser = argparse.ArgumentParser()
|
||||||
|
parser.add_argument("--mode", choices=["latest", "previous-workday", "standup", "focused"], default="latest")
|
||||||
|
parser.add_argument("--today", default="")
|
||||||
|
parser.add_argument(
|
||||||
|
"--channels",
|
||||||
|
default="",
|
||||||
|
help="Comma-separated channel names or IDs. Defaults to AIW_MATTERMOST_CONTEXT_CHANNELS from environment/.env when set.",
|
||||||
|
)
|
||||||
|
parser.add_argument("--limit", type=int, default=80, help="Max records per section; use 0 for no limit.")
|
||||||
|
args = parser.parse_args()
|
||||||
|
channels = parse_channels(args.channels or None)
|
||||||
|
limit = args.limit if args.limit > 0 else None
|
||||||
|
|
||||||
|
if args.mode == "latest":
|
||||||
|
mode_latest()
|
||||||
|
elif args.mode == "previous-workday":
|
||||||
|
mode_previous_workday(args.today or None, channels=None, limit=limit)
|
||||||
|
elif args.mode == "standup":
|
||||||
|
mode_standup(args.today or None, channels, limit)
|
||||||
|
elif args.mode == "focused":
|
||||||
|
mode_focused()
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == "__main__":
|
||||||
|
main()
|
||||||
@@ -2,6 +2,11 @@
|
|||||||
|
|
||||||
This directory contains the workspace-local Mattermost extractor used by OpenCode to refresh communication context.
|
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
|
## Files
|
||||||
|
|
||||||
- `mattermost_context.py`
|
- `mattermost_context.py`
|
||||||
|
|||||||
Reference in New Issue
Block a user