d356k24tkniemc8dab9ref6iu49qs5i4 · 205 rows
| id | issue_id | event_type | actor | old_value | new_value | comment | created_at |
|---|---|---|---|---|---|---|---|
| 019f75fc-fa59-73c0-9b78-8f13c2fb7d69 | ah-25e | updated | Eugene Blikh | {"id":"ah-25e","title":"Yonote publish lane: .task/publish.json → bot-authored docs at finalize","description":"Why: the operator wants agents to publish specs/reports/answers INTO Yonote instead of committing them to the project repo — with the write credential never entering the agent's environment. What: a third finalize-time file channel in the SPEC §14.4 family (tasks.json, question.json): the agent writes markdown under .task/publish/ and declares it in .task/publish.json {documents:[{title, file, collection_id?, parent_url?}]}; on successful finalize the daemon creates the docs via the internal/yonote client under the BOT token (publish:true, target = yonote.publish_collection_id or per-entry override, optional nesting under a referenced doc), comments the resulting URLs on the card, emits a yonote_published event. Publish failures never change the run outcome. wave 2.5 — implement after feat/wave2-archive-links merges. Depends on ah-gxa (client: CreateDocument/DocumentInfo) and ah-2lh (yonote config block + prepareTaskDir channel-clearing site).","design":"# Yonote publish lane: .task/publish.json → bot-authored docs at finalize\n\nAgents publish specs/reports/answers INTO Yonote instead of committing them to the project repo.\nThe agent declares intent via a file channel; the daemon executes with ITS bot token at finalize\nand links results on the card.\n\n## Why file-channel, not direct API access from the agent (DECISION)\n\nThe agent must never hold the Yonote token: worktrees run arbitrary generated code; `.task/` is\narchived to tar.gz at Done (a token in env or files would fossilize into archives); per-run env\nplumbing would put a write-capable credential into every zellij session. The daemon already owns a\nfinalize-time file-channel protocol with exactly the right semantics — `.task/tasks.json`\n(delegation) and `.task/question.json` (ask-user), SPEC §14.4: agent writes declarative intent,\ndaemon acts with its own credentials, results land as card comments. Publishing is the third\nchannel of that family. Attribution lands on the BOT user — uniform and auditable.\n\n## Channel format\n\n`.task/publish.json` (attempt-scoped; cleared in prepareTaskDir alongside summary/tasks/question):\n\n {\"documents\": [\n {\"title\": \"Design: frobnicator\", \"file\": \"publish/design.md\",\n \"collection_id\": \"\", \"parent_url\": \"\"}\n ]}\n\n- title: required, non-empty, ≤255 chars (Yonote bot/user name limits suggest 255 conventions).\n- file: required; RELATIVE path resolved under `.task/` (convention: agents put content in\n `.task/publish/\u003cname\u003e.md`). Guard rigor as repo-slug F8: reject absolute paths, filepath.Clean\n must stay under .task/, and the opened file must not escape via symlink (os.Root / EvalSymlinks\n containment check).\n- collection_id: optional uuid overriding the config default target.\n- parent_url: optional Yonote doc URL → resolve via yonote.ParseDocRefs(single) + DocumentInfo →\n parentDocumentId, so a report can nest under the spec it answers (documents.create\n parentDocumentId is v1-spec'd; child creation itself NOT live-verified — verify in tests against\n httptest only, live during rollout).\n- Caps: maxPublishDocs = 3 per run; maxPublishBytes = 512 KiB per file. No server-side text limit\n was verified — cap defensively; an oversize entry FAILS (listed in the comment), never truncated.\n\nRead at SUCCESSFUL finalize only, same site and semantics as readTasksFile: absent → nil (common\ncase); present-but-unparseable → same bounce treatment as a malformed tasks.json (do not silently\ndrop agent intent).\n\n## Config\n\nThe `yonote` block (introduced by the materialization bead) gains:\n\n publish_collection_id: \"…\" # uuid; the default target collection\n\nValidation: when set, must be a UUID shape; lane is ACTIVE only when the yonote client is\nconfigured AND publish_collection_id is set. When the client is configured but the collection is\nnot, publish.json entries all fail visibly with \"publishing not configured\" in the card comment —\nnever silently ignored. Operational prerequisite (document next to the config): the BOT must have\nread_write on the target collection (collections.add_user — client-bead runbook step 3).\n\n## Daemon flow (finalize, success path, after summary/tasks/question handling)\n\nPer entry (≤3, in file order): resolve target collection (override or default) → resolve\nparent_url if set (its failure fails ONLY that entry) → read + cap content →\nCreateDocument{Title, CollectionID, ParentDocumentID, Text: content, Publish: true} → record\n{Title, absURL = cfg base_url + doc.URL} or {Title, err}.\n\n- Card comment (goes through the existing markdownToHTML comment path):\n published to Yonote:\n - \u003cTitle\u003e → \u003cabs url\u003e\n - \u003cTitle\u003e → FAILED: \u003cmessage\u003e\n- appendEvent \"yonote_published\" {\"ok\": n, \"failed\": m}.\n- Publish failures NEVER change the run outcome — the run already succeeded; publication is a\n side effect. Operator retries by re-running, or publishes manually from the archived tar.gz\n (the content survives there by construction).\n- Idempotency (DECISION): re-running a card re-publishes and creates a NEW doc. Accepted for v1 —\n re-runs are rare, docs are cheap, and dedup would need a doc-id echo channel; revisit only if it\n annoys in practice (then: UpdateDocument by echoed id).\n\n## Agent contract (role-prompt snippet, config-side; document in deploy notes)\n\n To publish a document to Yonote: write markdown to .task/publish/\u003cname\u003e.md and declare it in\n .task/publish.json as {\"documents\":[{\"title\":\"…\",\"file\":\"publish/\u003cname\u003e.md\"}]}. Publication\n happens AFTER your run succeeds; resulting links are posted on the task card.\n\n## Tests\n\n- reader: absent → nil; malformed → tasks.json-parity bounce; \u003e3 entries; empty title; path\n guards (absolute, .., symlink escape); oversize file.\n- finalize with fake yonote writer (interface seam: CreateDocument + DocumentInfo): 2 ok + 1 fail\n → comment body lines, event payload, run outcome unchanged; lane-off (no collection id) →\n \"publishing not configured\" per entry; parent_url resolve failure isolates the entry;\n collection_id override honored.\n- prepareTaskDir clears publish.json + the publish/ dir between attempts.\n\nwave 2.5 — implement after feat/wave2-archive-links merges. Depends on: internal/yonote client\nbead (CreateDocument/DocumentInfo), materialization bead (yonote config block + prepareTaskDir\nchannel-clearing site).\n","notes":"ROLLOUT LIVE-SMOKE CHECKLIST (from wave-2.5 implementer, against bigbes.yonote.ru after deploy): 1) mint bot per ah-gxa runbook (POST /api/v2/bots + token, collections.add_user grant — grant call is spec-only, NOT live-verified), AuthInfo under bot token must show IsBot=true (daemon WARNs at startup otherwise); 2) documents.create with parentDocumentId (child nesting unverified live) + bot WRITE rights on publish_collection_id + publish:true attribution to bot; 3) archived-doc export ((archived) marker path); 4) ExportMarkdown empty-string cases (API-created never-edited draft, database-type doc) fall back to .text; 5) comments.create/list/resolve field names (only Q\u0026A bead ah-ptu uses them). SEMANTICS NOTE: publish fires on ROUTED successful runs too (inside firstFinalize\u0026\u0026success before maybeRoute) — a design stage with publish.json publishes before routing to review; intentional. publish_collection_id (UUID) must be chosen by operator at config staging.","status":"closed","priority":2,"issue_type":"feature","assignee":"Eugene Blikh","owner":"bigbes@gmail.com","created_at":"2026-07-18T15:04:25Z","created_by":"Eugene Blikh","updated_at":"2026-07-18T15:59:08Z","started_at":"2026-07-18T15:06:57Z","closed_at":"2026-07-18T15:59:08Z","close_reason":"Merged to master f9a7b4b (3 commits 6c9e259/a25b692/f9a7b4b): yonote client + claim-time artifact materialization + publish lane. Live smoke at rollout per ah-25e notes."} | {"notes":"\n ROLLOUT LIVE-SMOKE CHECKLIST (from wave-2.5 implementer, against \n bigbes.yonote.ru after deploy): 1) mint bot per ah-gxa runbook (POST \n /api/v2/bots + token, collections.add_user grant — grant call is spec-only, \n NOT live-verified), AuthInfo under bot token must show IsBot=true (daemon \n WARNs at startup otherwise); 2) documents.create with parentDocumentId \n (child nesting unverified live) + bot WRITE rights on publish_collection_id \n + publish:true attribution to bot; 3) archived-doc export ((archived) marker\n path); 4) ExportMarkdown empty-string cases (API-created never-edited draft,\n database-type doc) fall back to .text; 5) comments.create/list/resolve field\n names (only Q\u0026A bead ah-ptu uses them). SEMANTICS NOTE: publish fires on \n ROUTED successful runs too (inside firstFinalize\u0026\u0026success before maybeRoute)\n — a design stage with publish.json publishes before routing to review; \n intentional. publish_collection_id (UUID) must be chosen by operator at \n config staging. \n\n\n\nDEPENDS ON\n → ✓ ah-2lh: Claim-time Yonote artifact materialization into .task/artifacts/ + prompt manifest ● P2\n → ✓ ah-gxa: internal/yonote: light API client (doc resolve/export, create, comments; bot-token auth) ● P2\n\nBLOCKS\n ← ○ ah-ptu: Yonote Q\u0026A bot loop: poll doc comments, answer via board tasks (post-wave-2.5) ● P3\n\nROLLOUT FACTS (2026-07-18, live-verified): bot agenthub id=34fbc9a3-ec75-4722-a01f-805db3f3ff1b (v2 create needs username field — used bigbes@gmail.com); token agent-1-daemon installed as YONOTE_TOKEN in /etc/agent-hub/env; auth.info isBot=true CONFIRMED; collections.add_user CONFIRMED; Agents collection 8656642e-0297-4d69-b6fb-4246517a015b = publish_collection_id, bot read_write. CAVEAT: bot sees ALL team-default-readable collections (not grant-scoped) — operator may tighten per-collection perms in UI. Config staging after CI deploy: yonote{base_url: https://bigbes.yonote.ru, token: ${YONOTE_TOKEN}, publish_collection_id: 8656642e-0297-4d69-b6fb-4246517a015b}."} | NULL | 2026-07-18T19:09:01Z |
| 019f7609-5df3-7e2c-9c47-040780bda56d | ah-ydx.3 | closed | Eugene Blikh | Merged b761cbe: recall-on-claim (## Relevant memories via ctxpack, proj+global scopes, 15s budget, feature-off byte-identical) + save-on-Done (save→archive→prune ordering, infer=true, once-only via worktree signal, Cancelled excluded). Rollout: install MEM0_API_KEY in /etc/agent-hub/env, live-smoke recall block + one memory_saved event. | NULL | 2026-07-18T19:22:33Z | |
| 019f7609-61b6-7e0d-8001-0288bc028518 | ah-ydx.4 | created | Eugene Blikh | NULL | 2026-07-18T19:22:34Z | ||
| 019f7609-8a21-7553-a4f0-84f8ae3a598d | ah-0ge.6 | claimed | Eugene Blikh | {"id":"ah-0ge.6","title":"HTTP tools API: per-task bearer tokens + ahub tool CLI over loopback","description":"SPEC §14.4 transport for MID-RUN agent tools (task-create/ask-user without stopping): daemon loopback HTTP with per-task bearer tokens minted at claim (exposed via .task/ env), 'ahub tool \u003cname\u003e' CLI subcommands, server-side policies (Triage-only, gen\u003c=2, per-session caps, dedup, project allowlist). File channel already covers stop-and-resume — this is the deferrable last item of the approved slate.","status":"open","priority":3,"issue_type":"feature","owner":"bigbes@gmail.com","created_at":"2026-07-18T13:23:55Z","created_by":"Eugene Blikh","updated_at":"2026-07-18T13:23:55Z"} | {"assignee":"Eugene Blikh","status":"in_progress"} | NULL | 2026-07-18T19:22:44Z |
| 019f762f-44ca-7298-9cde-de27ea37fa07 | ah-0ge.6 | closed | Eugene Blikh | Merged 07bd9d4+03f4496: per-task 256-bit bearer tokens (.task/tool-auth.json, constant-time check, invalidated at finalize, in-memory registry), /api/tool/{task-create,ask-user,answer} reusing file-channel policy code, ahub tool CLI. Additive — file channels intact. Live smoke list in agent report; note stall_timeout must exceed ask-user poll deadline. | NULL | 2026-07-18T20:03:57Z |