4a93c8b26244a1838ee99a5f906a30b8fc6f4720
5
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
4a93c8b262 |
fix(task-logs): a collaborator who may write a task may log on it (rule 78)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 11s
CI & Build / integration (push) Successful in 52s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / Python tests (push) Successful in 1m37s
CI & Build / Build & push image (push) Failing after 24s
create_log filtered on Note.user_id == user_id, a bare owner check, so a collaborator with write access to a shared task was told it did not exist — and, since a log now stamps the claim, could never be seen working it. It now asks can_write_note. Editing and deleting a log still require its author, which is authorship rather than access. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
b06b3a1d8a |
feat(tasks): a task can say a session is working it — the claim (milestone 381 step 2)
CI & Build / Python lint (push) Successful in 3s
CI & Build / integration (push) Successful in 53s
CI & Build / TypeScript typecheck (push) Successful in 54s
CI & Build / Python tests (push) Failing after 1m18s
CI & Build / Build & push image (push) Skipped
CI & Build / Plugin hooks (push) Successful in 14s
status is durable and nothing clears it, so in_progress cannot also mean "someone is on this now". The claim is that second meaning, stored as who and when so it dies on read rather than needing to be cleared. - notes.claimed_by / claimed_at / claim_touched_at / claim_session (0110). - The server stamps it on the write that is the work: reaching in_progress (update or create, via apply_status_transition) and a work log on an open task. done/cancelled/todo release it. Live while touched within CLAIM_LEASE (2h); dead on read past it, with no sweep. - The plugin's PostToolUse hook on update_task/add_task_log binds the harness's session_id (GET /api/plugin/claim-session). It binds only to a live claim the caller holds; it cannot create one. - to_dict carries `claim`. Plugin version minted. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> |
||
|
|
aa95c109ea |
feat(retrieval): a task's work logs join the document it is embedded as (#4251)
CI & Build / Python lint (push) Successful in 3s
CI & Build / Plugin hooks (push) Successful in 16s
CI & Build / TypeScript typecheck (push) Successful in 53s
CI & Build / integration (push) Successful in 59s
CI & Build / Python tests (push) Successful in 1m38s
CI & Build / Build & push image (push) Successful in 27s
Step 1 of #4251. A work log is the richest prose Scribe holds about WHY something is the way it is — written during the work, recording what was tried and ruled out. #4241 made it readable from the agent's door. It was still not findable, so "has anyone tried this approach?" — precisely the question a log answers — could not reach one. The cost is not hypothetical: #4208 was rebuilt in this session because its logs were unreachable. THE DESIGN QUESTION the issue left open was whether logs embed as part of their task's document or as rows of their own. As their own rows, a hit has to be resolved back to a task to be worth anything, and it needs a fourth search, a fourth result shape and a fourth arm. As part of the task, the objection is that a long log drowns a short title. That objection was true before #280 and is not true now. Chunking made one record into one vector per section, so each log becomes its own title-anchored chunk, scored separately, and the task's own prose keeps the chunk it always had — a task is as findable as its best-matching log rather than as the average of everything in it. The other half is this session's other build: a search hands back the chunk that won (#4243), so a hit earned by a log shows that log's passage under the task's title. Without that a reader would have got body[:240] of the task — the opening of a record whose relevance lives three hundred lines further down. So `task_document(title, body, logs)` sits beside `rule_document`: a synthesised embed-time shape, because the stored record is the task row and the logs live in their own table, so the document that should be searchable exists nowhere until it is built. A task with no logs is returned untouched — most notes are not tasks and most tasks carry no log, and their vectors are the corpus every tuned number here was measured against. CHUNKER_VERSION 1 → 2, and its comment now says what the version actually means. It used to read "whenever chunk_document's output can change for the same input", which this change would slip past: `chunk_document` is untouched and every task with a log now embeds differently while its title, body and the chunker all stand still. The invariant is the document a record is embedded as. The startup backfill re-embeds on that. Create, edit and delete of a log all refresh the task through `embed_note`, the one path every writer shares — an edited log whose vectors still carry its old wording keeps matching what it no longer says. No new kind enters the auto-inject menu: tasks were always in it, and this makes recall on them better rather than changing what the menu spans. The calibration stamp will now report shape_version 2 against numbers measured at 1, which is exactly the report #4104 built it to make. Also promoted `session_returning` into tests/helpers beside `make_mock_session` (#2834) — two files had spelled it out identically and a third was about to. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy |
||
|
|
4f2977b848 |
fix(tasks): add_task_log wrote to a surface no agent could read back (#4241)
CI & Build / Python lint (push) Successful in 2s
CI & Build / Plugin hooks (push) Successful in 13s
CI & Build / TypeScript typecheck (push) Successful in 55s
CI & Build / integration (push) Successful in 1m4s
CI & Build / Python tests (push) Failing after 1m14s
CI & Build / Build & push image (push) Skipped
The work log reached the web UI through routes/task_logs.py and nothing
else. get_task returned only the body — a claim written once, before the
work — with the record written during it invisible beside it. So a stale
body arrived with nothing to contradict it, and this session rebuilt work
that had already shipped, with the evidence sitting in the task's own logs.
Read side, scoped through the access layer (rule 78):
- logs_for_task / count_logs_for_task / log_counts_for_tasks in
services/task_logs.py. Scoped by who may read the TASK rather than by
who wrote the entry: list_logs filters TaskLog.user_id == user_id,
which hands a shared collaborator an empty list reading as "no work
has been done". The page query folds readable_notes_clause into the
same statement so the permission does not become an N+1.
- get_task returns work_log; list_tasks and get_milestone steps carry
log_count, zero-filled so "none" is a count and not a missing key.
Elision keeps both ends. The newest entry arrives whole to 4000 chars
because it answers "where does this stand"; older ones are shortened from
the MIDDLE, never the head. A head cut selects what a reader sees by
character position, which is uncorrelated with what matters — an entry
closing with "so this shipped in 04775c3" loses the one sentence that
answers the question, and a truncated flag says something went, never
whether it mattered. The gap states how many characters it covers.
conftest gains an autouse stub for the new read arm, same reasoning as
_no_rule_arm: three widely-called tools grew a database read, and the
existing call sites should not each have to learn about it.
Raised while reviewing this: search() has the same shape and worse —
body[:240] with no marker at all, while the chunk that actually matched
sits unused in the row that won. Filed as #4243, not fixed here.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01821k5B3Ysecp9fNYs92Kuy
|
||
|
|
b255a0f90e |
refactor: rename package fabledassistant -> scribe (code-only)
Renames src/fabledassistant -> src/scribe and all imports, plus the default DB name and DB user/password (fabled -> scribe) in config + compose. 952 refs / 154 files. Reverses the old 'internal name stays fabledassistant' convention. Code-only: live databases are still physically named 'fabledassistant'. Deployed environments must set POSTGRES_DB / POSTGRES_USER (or rename the DB) since the defaults now resolve to 'scribe'. Repo (FabledScribe), git host (fabledsword), MCP (fabled-git) and the image name (fabledscribe) are intentionally unchanged. ruff check src/ clean locally; CI (typecheck + pytest) is the gate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> |