docs(6204): shipped instruction text describes the product, not this instance's records
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 39s
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 15s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 1m10s
CI & Build / Python tests (push) Successful in 1m55s
CI & Build / Build & push image (push) Successful in 39s
MCP tool docstrings and comments, the server's orientation notes, the plugin skills, every hook script and the plugin README/PACKAGING drop record ids, milestone numbers, dates and incident stories, and state each non-obvious why as a present-tense fact. History stays in the task logs. Corrections found on the way: - plugin README/PACKAGING said every hook is a GET; moment, rule-opened and reply-rules POST, so the plugin's key needs write scope. Both now say so, name Settings -> MCP Access, and list every wired hook and endpoint. - the contract-change hint printed "(lesson #4207)", this instance's record, to every install. It no longer cites a record. - tasks.py claimed there is no delete_task; there is. - rulebooks.py cited delete_event and a design spec that no longer exist. - scribe_after_write.sh carried an always-empty etag_q; removed. - test_write_path_trigger pinned "| tac | scribe_defs | head -1", text that existed only in a comment about the old code; it now pins the real tail -1 pipeline. - docs/development.md: CI runs on Gitea Actions. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
@@ -4,15 +4,9 @@ Tasks are notes with a non-null `status` — same model, different filter.
|
||||
Wrappers call services/notes.py for CRUD with is_task=True and add the
|
||||
task-specific fields (status, priority, due_date, parent_id), plus
|
||||
services/task_logs.py for add_task_log AND for reading those logs back —
|
||||
`get_task` returns them and `list_tasks` counts them. For most of this
|
||||
module's life `add_task_log` wrote to a surface no agent could read: the
|
||||
entries reached the web UI and nothing else, so a session opening a task
|
||||
saw only the body — a claim written before the work — with the record
|
||||
written during it invisible beside it. A stale body then had nothing to
|
||||
contradict it, and shipped work got rebuilt (#4241).
|
||||
|
||||
There is no delete_task — matches the existing fable-mcp surface.
|
||||
Cancel by updating status to "cancelled".
|
||||
`get_task` returns them and `list_tasks` counts them. The log is the record
|
||||
written during the work; a body is a claim written before it, so a session
|
||||
opening a task needs both.
|
||||
|
||||
Sentinels (preserved from existing fable-mcp):
|
||||
- status="" / priority="" / title="" / body="" → "leave unchanged" on update
|
||||
@@ -181,8 +175,8 @@ async def get_task(
|
||||
written once, at the start, when the least is known; the log is written
|
||||
during the work and after it. A task whose body says "not started" and
|
||||
whose log records a partial ship is not a contradiction to resolve — the
|
||||
log is simply later. This block exists because its absence cost a session
|
||||
a day of rebuilding work that had already shipped (#4241).
|
||||
log is simply later; trusting the body alone is how finished work gets
|
||||
rebuilt.
|
||||
|
||||
The NEWEST entry arrives whole (to 4000 characters); older ones are
|
||||
shortened to a headline. Anything shortened is cut from the MIDDLE, so
|
||||
@@ -228,10 +222,9 @@ async def get_task(
|
||||
# Same reasoning as get_note's record_pulled, and this is the tool where it
|
||||
# matters MOST: auto-inject ranks kind-blind over a corpus that is
|
||||
# overwhelmingly tasks and issues, so tasks dominate what it surfaces. Without
|
||||
# this the surfaced→pulled loop was open exactly where the volume is — every
|
||||
# surfaced task counted as never-pulled because the tool that opens one didn't
|
||||
# say so, driving auto-inject's measured pull-through toward zero for its own
|
||||
# dominant kind. #1038 and #2085 are explicitly gated on that number (#2245).
|
||||
# this every surfaced task would count as never-pulled, driving
|
||||
# auto-inject's measured pull-through toward zero for its own dominant
|
||||
# kind.
|
||||
await systems_tools.attach_systems(
|
||||
uid, getattr(note, "user_id", uid) or uid, data, note.id, note.project_id
|
||||
)
|
||||
@@ -365,7 +358,7 @@ async def create_task(
|
||||
await systems_tools.attach_systems(uid, uid, data, note.id, project_id or None)
|
||||
data.update(dedup_svc.note_overlap_response(overlaps, "task"))
|
||||
# A task that says it is "matching" another project's work names the
|
||||
# source of a pattern — family canon's citation trigger (milestone 463).
|
||||
# source of a pattern — family canon's citation trigger.
|
||||
await family_svc.attach_family_hint(uid, data, note, created=True)
|
||||
return await placement_svc.attach_placement(uid, data, note)
|
||||
|
||||
@@ -467,8 +460,8 @@ async def update_task(
|
||||
await family_svc.attach_family_hint(uid, data, note, created=False)
|
||||
if status in _CLOSING_STATUSES:
|
||||
data["report_back"] = REPORT_BACK_CUE
|
||||
# Owed family adoptions filed while the task was open (milestone 463
|
||||
# step 6) — work now waiting in some project, which the report names.
|
||||
# Owed family adoptions filed while the task was open — work now
|
||||
# waiting in some project, which the report names.
|
||||
await family_adoption_svc.attach_owed_adoptions(uid, data, note)
|
||||
return await moment_delivery.attach_moment_rules(
|
||||
uid, "update_task", {"status": status, "project_id": project_id}, data,
|
||||
@@ -490,7 +483,7 @@ async def add_task_log(task_id: int, content: str) -> dict:
|
||||
out, and where it actually stands.
|
||||
|
||||
A log on an open task also marks it as being worked by this session — its
|
||||
`claim` (milestone 381). That is what brings the task and its newest
|
||||
`claim`. That is what brings the task and its newest
|
||||
entries back to you after a compaction; it lapses by itself once the
|
||||
session stops writing, and a status of done, cancelled or todo clears it.
|
||||
|
||||
@@ -520,8 +513,8 @@ async def add_task_log(task_id: int, content: str) -> dict:
|
||||
return data
|
||||
|
||||
|
||||
# The in-band half of milestone 409 step 3. The completion shape itself rides
|
||||
# the closing response as `reply_shape` (milestone 500); this is the one line
|
||||
# The in-band completion cue. The completion shape itself rides the closing
|
||||
# response as `reply_shape`; this is the one line
|
||||
# that says what the reply covers, for every MCP client, at the moment a piece
|
||||
# of work closes. One line on purpose: a template here would be read as the
|
||||
# reply itself.
|
||||
@@ -537,7 +530,7 @@ _ITEM_KEYS = {"title", "body", "type", "status", "priority", "kind", "tags", "sy
|
||||
def _batch_items(records: list[dict], *, what: str = "record") -> list[batch_svc.BatchItem]:
|
||||
"""Parse the door's plain dicts into BatchItems, refusing unknown keys.
|
||||
|
||||
Strict for the same reason StrictArgsMCPServer is (#2709): a misspelt key
|
||||
Strict for the same reason StrictArgsMCPServer is: a misspelt key
|
||||
silently dropped — `milestone` for a per-record milestone, `desc` for body —
|
||||
creates a record that looks right and is missing what the caller sent.
|
||||
"""
|
||||
@@ -574,7 +567,7 @@ async def _first_duplicate(
|
||||
"""The duplicate gate over a whole batch — the first hit blocks all of it.
|
||||
|
||||
`overlaps`, when given, collects each record's near matches below the copy
|
||||
band by its 1-based position (#4306), for the reply once the batch is
|
||||
band by its 1-based position, for the reply once the batch is
|
||||
created."""
|
||||
for i, item in enumerate(items, start=1):
|
||||
found: list = []
|
||||
|
||||
Reference in New Issue
Block a user