FAQ harvesting: Teams -> Coda review -> FAQ-<Domain>.md - #100
Merged
Merged
Conversation
vijay-tylertech
enabled auto-merge (squash)
September 9, 2026 16:37
Recurring questions get answered in Teams by the people who own the answer, and then the answer is lost. This adds the loop that catches them, with a human approval gate in Coda so nothing unreviewed reaches Foundry. Half agent, half script, because reading a Teams *channel* needs the Microsoft 365 MCP tool teams_list_channel_messages, which is available to Claude in a session but not to a plain Python process - there are no Graph app credentials on this machine. So extraction and dedupe are agent work; push, pull and delete are deterministic. scripts/coda_faq_review.py status / push / pull / delete scripts/faq-harvest/HARVEST.md the loop, the sources, the rules extraction follows scripts/faq-harvest/TABLE-SPEC.md column spec and what the CSV import actually produced team-config/faq-harvest/ the first batch, the import CSV, the ledger The Aligned Releases queue is live: 21 candidates in Coda grid-4mdZMRDPAE, awaiting review. Two of them contradict entries the agent is answering from today - cohort unassignment and the feature-flag metadata field - and are flagged as such rather than applied. Three Coda behaviours the script exists to absorb, all verified rather than assumed: - The REST API cannot create a table, and cannot add or remove a column. A markdown table in page canvasContent becomes static text, not a grid. Tables are made by CSV import, which is why the CSV filename has to be the intended table name. - Writes are asynchronous. A probe page took ~20s to disappear. Every mutation polls /mutationStatus to completion, so "delete the processed row" cannot half-happen. - CSV import coerces types: Source link became a link column (safe, full URL preserved) and the date column reads back as ...T00:00:00.000-05:00, so it must never be string-compared. A checkbox is the only gate, and there is deliberately no Status column: rows do not survive processing, so a status written on one would never be read again. Decisions live in team-config/faq-harvest/ledger-<domain>.json instead, which outlives the row. Rejection is expressed by deleting the row - push notices a key it previously sent has gone and records it as rejected. The ledger tracks table_id for that reason too, since rebuilding the table is the normal way a column change happens and would otherwise look like the reviewer having deleted every row at once. No credentials: CODA_API_TOKEN comes from the environment and the token is not in any committed file. Claude-Session: https://claude.ai/code/session_01KxA7f912exehyUjjgVBgf4
vijay-tylertech
force-pushed
the
chore/faq-harvest-pipeline
branch
from
September 9, 2026 18:00
bbeba84 to
eee0511
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Builds the queued FAQ-harvesting loop: read the source Teams channels, extract FAQ candidates
not already in that domain's
FAQ-*.md, post them to a Coda review table, and when a reviewerticks Ready for Processing, pull the row, index it into the FAQ, and delete it — so Coda
only ever holds outstanding items.
Why it is half agent, half script
Reading a Teams channel needs the Microsoft 365 MCP tool
teams_list_channel_messages,available to Claude in a session but not to a plain Python process (no Graph app credentials on
this machine). Extraction, dedupe and conflict classification are agent judgement;
push,pullanddeleteare deterministic — they are the steps that must not half-happen.Status
The Aligned Releases queue is live — 21 candidates in Coda
grid-4mdZMRDPAE, all awaitingreview. Nothing has been indexed and nothing has reached Foundry. Two candidates contradict
entries the agent answers from today (cohort unassignment; the feature-flag
metadatafield)and are flagged, not applied.
Status Pages/SLAs is scoped but not built, and has nowhere to deploy yet —
Knowledge-StatusPageAndSLA/is a scaffolded corpus with no agent and no collection.Design notes worth reviewing
processing, so a status written on a row would never be read again. Decisions live in
ledger-<domain>.json, which outlives the row.pushnotices that a key it previously sent has gone andrecords it as rejected, so it is never re-proposed.
table_id. Rebuilding the table is the normal way a column changehappens, since the Coda API cannot rename or delete a column — without this, pointing at a
fresh table would record every previously-pushed key as a rejection in one go.
Verified Coda behaviour, not assumed
canvasContentbecomes static text/mutationStatusto completionSource link→ link (safe, full URL kept); the date column reads back as...T00:00:00.000-05:00, so never string-compare itPaths
Touches
scripts/andteam-config/, both admin-only. No credentials committed —CODA_API_TOKENcomes from the environment.https://claude.ai/code/session_01KxA7f912exehyUjjgVBgf4