Skip to content

Commit b0f7644

Browse files
authored
Fix approved automatic drafts stranded before manual publication (pre-OC4) (#44)
* Allow explicitly approved, never-sent auto draft to be safely handed to manual publishing * Guard publisher review and send with atomic mode/decision claims to prevent stale auto handoff * Expose explicit manual publication action for never-sent, approved auto jobs in existing Activity Manager * Regression: recovered approved auto drafts, no prior sends, owner review, stale worker CAS and manager security * Keep approved-auto manual handoff safety tests in CI triggers * Release publication handoff fix from operator-accepted OC3 via bobctl update * Document approval-to-manual handoff, no-send safeguards, race prevention and acceptance * Pin synthetic publisher fixture integrity assertions before send and handoff race * Checkpoint approved OC3 and publication safety fix before OC4 investigator * Record OC3 acceptance and bugfix-before-OC4 release chain in TODO * Bind synthetic publisher module to test-scoped validated feed source; retain real integrity validation
1 parent 7143143 commit b0f7644

9 files changed

Lines changed: 469 additions & 22 deletions

‎.github/workflows/inventory-tests.yml‎

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ on:
5656
- 'tests/test_oc01_operations.py'
5757
- 'tests/test_oc2_automation_center.py'
5858
- 'tests/test_oc3_diagnostic_console.py'
59+
- 'tests/test_publication_manual_handoff.py'
5960
- 'tests/test_v071_integration.py'
6061
- 'tests/test_v071_http_smoke.py'
6162
- 'tests/test_v071_ui_update.py'
@@ -119,6 +120,7 @@ on:
119120
- 'tests/test_oc01_operations.py'
120121
- 'tests/test_oc2_automation_center.py'
121122
- 'tests/test_oc3_diagnostic_console.py'
123+
- 'tests/test_publication_manual_handoff.py'
122124
- 'tests/test_v071_integration.py'
123125
- 'tests/test_v071_http_smoke.py'
124126
- 'tests/test_v071_ui_update.py'

‎TODO.md‎

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
**Last reviewed:** October 30, 2026 (America/Los_Angeles)
44
**Canonical repo:** `merberg-ai/bob-openclaw-moltbook`, `main`
5-
**Operator-tested deployed baseline:** `v0.7.6-oc2-automation1` (OC0+OC1 and OC2 operator-accepted Oct 30, 2026; OC3 candidate not yet operator-tested)
5+
**Operator-tested deployed baseline:** `v0.7.6-oc3-console1` (OC0–OC3 operator-accepted Oct 30, 2026; publication bugfix candidate awaiting acceptance)
66
**Status:** FM3 complete; **FM4/FM5 on hold** until operator's other issues and new feature are handled. **CH1 remains planned only, not implemented or installed.**
77
**Deployment contract:** future implementation through `bobctl update` with verified private backup, installed-module import checks, reversible release and short live acceptance. A Git commit alone never changes the running Activity Manager.
88

@@ -12,7 +12,7 @@
1212
| --- | --- | --- |
1313
| Personality (PM1) and Memory (PM2) | Implemented / operator-tested | Maintain existing explicit memory recall and owner review boundaries |
1414
| **Friends (FM1–FM3.2d)** | **All FM3 phases complete, operator-accepted**; final release `v0.7.5-fm32d-focus1`, PR #38, 537 CI tests | **HOLD** until unrelated issues and separate requested feature are addressed. Then resume **FM4.1 evidence-linked Social Memory Timeline**, later FM5 only with explicit approval. See [Friends roadmap & resumption handoff](docs/FRIENDS-ROADMAP-AND-HANDOFF.md) |
15-
| **Operations Center: activity / automation / console / inactivity investigation** | **OC0+OC1 and OC2 operator-tested; OC3 Console release candidate** | OC3 `v0.7.6-oc3-console1`: authenticated terminal-styled safe diagnostics, bounded local health/queue and opt-in classified journal, pause/copy/export. **OC4** inactivity investigator remains planned. [Operations Center roadmap](docs/OPERATIONS-CENTER-ROADMAP.md) |
15+
| **Operations Center: activity / automation / console / inactivity investigation** | **OC0 through OC3 operator-accepted; pre-OC4 publication repair candidate** | `v0.7.6-pubfix-manual1` repairs approved automatic drafts stranded in failed/queued jobs without retrying anything sent. **OC4** is next: evidence-backed why-no-activity diagnostics and historic owner-decision classifications. [Operations Center roadmap](docs/OPERATIONS-CENTER-ROADMAP.md) |
1616
| Logs & Diagnostics | Installed / operator-tested | Reuse existing bounded redacted journal, job, event and checkpoint collectors rather than duplicate raw logging |
1717
| **Direct Bob Chat (CH1)** | **Planned here, not started** | Gateway compatibility spike, then authenticated LAN chat UI |
1818
| Fresh-host installer | Deferred | Keep current WSL/Windows startup, services and updater intact |
@@ -21,7 +21,7 @@
2121

2222
**Owner request (Oct 30, 2026):** The Overview gauges show 2/10 comment attempts, 0/6 automatic, 0/4 originals and six queued manual comments, but reveal too little about the reason for inactivity. Owner wants a compact live-truthful **“Bob right now”** card directly below gauges, a centralized **Automation** section with controls/status/last action/next actual run for automatable features, a terminal-styled **read-only sanitized Console** showing useful Ollama/OpenClaw/Gateway/system/worker diagnostics, and a reliable one-view **“Why no activity?”** explanation. All pages must be compact and mobile-friendly.
2323

24-
**Implementation status:** **OC0+OC1 and OC2 operator-accepted**; compact Overview and Automation page working on LAN. **OC3 Diagnostic Console candidate `v0.7.6-oc3-console1`** is built, with CI/live acceptance pending. See [docs/V076-OC3-DIAGNOSTIC-CONSOLE.md](docs/V076-OC3-DIAGNOSTIC-CONSOLE.md). **OC4 why-inactivity investigator remains unimplemented**, detailed in [Operations Center roadmap](docs/OPERATIONS-CENTER-ROADMAP.md). Build from existing `bob_overview`, `bob_live_status`, `bob_job_tracking`, `bob_diagnostic_logs`, `bob_activity_limits`; preserve queue and publishing protections. Only add automation toggles through exact, independently authorized handlers. **Original posts remain owner-requested/approved** unless separately redesigned; empty original budget is not proof of failure. “Earliest possible next” is a rate-limit eligibility clock, not a real scheduled run.
24+
**Implementation status:** **OC0–OC3 operator-accepted** including read-only Console. The Console exposed a publication handoff bug; **pre-OC4 candidate `v0.7.6-pubfix-manual1`** corrects it with explicit owner action only. See [docs/V076-PUBFIX-MANUAL-HANDOFF.md](docs/V076-PUBFIX-MANUAL-HANDOFF.md). **OC4 why-inactivity investigator remains next, not yet built**, detailed in [Operations Center roadmap](docs/OPERATIONS-CENTER-ROADMAP.md). Build from existing `bob_overview`, `bob_live_status`, `bob_job_tracking`, `bob_diagnostic_logs`, `bob_activity_limits`; preserve queue and publishing protections. Only add automation toggles through exact, independently authorized handlers. **Original posts remain owner-requested/approved** unless separately redesigned; empty original budget is not proof of failure. “Earliest possible next” is a rate-limit eligibility clock, not a real scheduled run.
2525

2626
**User preference:** no sprawling command-marathon; one `bobctl update` per small code release, with CI first and a concise mobile/LAN acceptance. Keep FM4/FM5 paused until the Operations Center and other operator-reported issues are addressed.
2727

@@ -34,7 +34,7 @@
3434

3535
**Resume Friends at FM4.1** only after the operator lifts the hold: a small private, source-verifiable, per-agent **Social Memory Timeline** distinguishing actual saved observations, Bob's tentative ideas and owner-reviewed records. Start with read-only evidence linked from FM1/FM2 and existing FM3.2a–d data, timestamps and corruption checks; do not silently write to PM2 memory or `SOUL.md`, change Scout priorities or authorize outreach. FM4.2 can add revisable impressions and owner corrections; FM4.3 can add explicitly enabled bounded recall. FM5.1 independent exploration and FM5.2 separately approved public interactions remain further off. Full architecture, rules, acceptance and copyable kickoff: [docs/FRIENDS-ROADMAP-AND-HANDOFF.md](docs/FRIENDS-ROADMAP-AND-HANDOFF.md).
3636

37-
**Future versioning:** the last operator-confirmed installed release is `v0.7.6-oc2-automation1` following October 30, 2026 acceptance. OC3's candidate upgrade is `from: v0.7.6-oc2-automation1` and must not be marked installed before operator acceptance. Future `bobctl update` releases must match the latest accepted installed version, including intervening bug fixes/new features.
37+
**Future versioning:** the last operator-confirmed installed release is `v0.7.6-oc3-console1`. Publication fix candidate upgrades from OC3 to `v0.7.6-pubfix-manual1`; this is not an installed baseline until operator acceptance. OC4 must be built on the latest accepted installed version. Future updates must preserve the exact release chain.
3838

3939
---
4040

‎docs/OPERATIONS-CENTER-ROADMAP.md‎

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
**Planning checkpoint:** October 30, 2026 (America/Los_Angeles).
44
**Priority:** Owner-requested visibility, unified automation control, and safe diagnostic console **before** resuming Friends Manager FM4.1 / FM5.
55
**Last operator-tested release:** `v0.7.5-fm32d-focus1`, final FM3 acceptance PASS.
6-
**Implementation status:** **OC0+OC1 operator-accepted** as `v0.7.6-oc01-activity1`, **OC2 operator-accepted** as `v0.7.6-oc2-automation1` on October 30, 2026. **OC3 Console candidate** `v0.7.6-oc3-console1` awaits CI/merge/live acceptance. **OC4 investigator** remains planned. No new posting or agent permissions.
6+
**Implementation status:** **OC0+OC1, OC2, and OC3 operator-accepted** through `v0.7.6-oc3-console1` (Oct 30, 2026). **Pre-OC4 publication safety fix candidate:** `v0.7.6-pubfix-manual1`, not yet operator-tested. **OC4 investigator** remains next. No new posting or agent permissions.
77
**Principle:** First explain existing inactivity accurately; do not increase autonomous posting merely to move a gauge.
88

99
## 1. User problem and what is known
@@ -131,6 +131,14 @@ Suggested distinct UI states:
131131
- **Acceptance:** see [V076-OC3-DIAGNOSTIC-CONSOLE.md](V076-OC3-DIAGNOSTIC-CONSOLE.md). One update and a short LAN Console smoke test; no need to publish anything.
132132
- **Next:** OC4 deterministic why-inactivity screen with durable sanitized skip reasons. **FM4/FM5 remain paused**.
133133

134+
### Pre-OC4 publication approval handoff repair (candidate pending live acceptance)
135+
136+
- **Why:** Console revealed previously reviewed automatic drafts stored as `failed` with `Human decision already exists`. A prior automatic queue row masked the manual Publish button and blocked the normal manual requeue, even when **approved** and **never sent**. The warning did not establish whether the owner had approved or rejected each run.
137+
- **Fix:** `v0.7.6-pubfix-manual1` from accepted OC3. An approved source-verified draft may be explicitly converted from `auto` to `manual,queued` **only** if a matching previous automatic job was never attempted and is either still queued or failed solely for a known pre-send review block. The original dashboard publish handler remains unchanged; UI exposes the **Queue approved draft manually** action only for provably recoverable records.
138+
- **Safety:** failed attempts from integrity/expiry/uncertain/sent/verification remain non-retryable; approval hash/source exact match, no send fields and full atomic compare-and-swap required. Publisher rechecks mode and decision atomically before the sending intent; stale auto workers cannot send following the owner's handoff. No automatic publication, no changed rate limits, no loosened friend-directed rules.
139+
- **Acceptance:** synthetic strict no-send and real/manual-send tests plus full CI; `bobctl update` only after green PR. Short operator UI check, without clicking Publish just for testing. [V076-PUBFIX-MANUAL-HANDOFF.md](V076-PUBFIX-MANUAL-HANDOFF.md).
140+
- **OC4 next:** Explain separately **previously rejected**, **approved awaiting explicit manual Publish**, **already reviewed auto intentionally blocked**, and **actually sent/uncertain** rather than treating every warning as an outstanding approval. Preserve source-based read-only explanations.
141+
134142
### OC4: “Why no activity?” single-view investigator
135143

136144
- Add per-lane **Explain inactivity** action in Overview and Automation, backed by deterministic OC0 reason engine (not a new LLM reasoning prompt).

‎docs/V076-PUBFIX-MANUAL-HANDOFF.md‎

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
# Publication-workflow repair: approved automatic draft → explicit manual queue
2+
3+
**Candidate release:** `v0.7.6-pubfix-manual1` from operator-accepted `v0.7.6-oc3-console1`. **Not installed until the operator runs `bobctl update`.**
4+
5+
## What went wrong
6+
7+
The Console revealed several recorded automatic comment jobs blocked with `Human decision already exists`. This message applies to either approved or rejected drafts; it is not evidence of an owner approval waiting to complete. The publisher correctly refuses to send a draft *automatically* when an owner has made a review decision. However, a blocked/queued automatic job occupied the unique `run_id` in `publish_jobs`, and the owner-visible page hid its manual publication button. The existing manual `queue_manual` refused **any** previous job, even one that demonstrably never attempted to send. Approving therefore could leave a draft stranded.
8+
9+
## The repair
10+
11+
An operator who has **approved a verified, unchanged Scout draft** can now use an explicitly displayed **Queue approved draft manually** button for an existing auto job which is (a) still queued with no previous error or (b) failed solely because an owner decision already existed or because friend-targeted automatic posting was intentionally blocked. The existing `/publish/<run_id>` handler, CSRF/digest check, manual approval and database backup are unchanged.
12+
13+
This action atomically converts the one *existing* `publish_jobs` row from `auto` to `manual` with `state='queued'`, clears the historic no-send error and records `publish_manual_handoff`. It does not immediately call Moltbook or bypass rate limits, safety guards, post preflight or verification. The normal publisher timer later prioritizes this approved manual job.
14+
15+
### Critical fail-closed rules
16+
17+
A handoff is forbidden unless all of the following hold:
18+
- A current owner `decisions` record explicitly says **approved** for the exact saved `approval_sha256`. Reject/changed digest remains blocked.
19+
- The entire original `scout-result` and saved feed revalidate, and the stored job has the exact same post ID, approval digest and unchanged draft text.
20+
- The job is `mode=auto`, has no `attempted_at`, no recorded `comment_id`, and no challenge/verification data. Only `queued` with no error or one of two known **never-sent** review-block reasons is recoverable. No arbitrary failed, expired, sending, uncertain, verified, rejected, manual, already published or previously attempted jobs may be retried.
21+
- The database `UPDATE` has a full compare-and-swap predicate, including current approval, job mode/state, original post/draft/digest, all no-send fields and exact past review-block reason.
22+
- The publisher's review/friend auto-block writes are constrained to *still-queued automatic jobs*. Its final send-intent claim atomically rechecks the exact current mode, original data, approval status for manual mode or *absence of an owner review* for auto mode. If the job changed during preflight, the publisher **does not send** under stale permission. Its persisted `sending` state still precedes any irreversible network POST.
23+
24+
There is **no automatic retry**, no database migration and no reopening of ambiguous network attempts. An explicit review approval does **not** by itself publish; the owner must still click the queue action.
25+
26+
## Release content and rollback
27+
28+
Normal `bobctl update` installs three reviewed files: `bob_publish_common.py`, `bob-moltbook-publisher`, and a pinned generated `bob-activity-manager`. Activity Manager restart only, with existing private DB backup and code rollback. Existing credentials are not touched or displayed. Existing auth, Host, Origin, CSRF, review, publisher rate/budget, global pause, friend-targeted and duplicate-post safeguards remain unchanged.
29+
30+
Synthetic `tests/test_publication_manual_handoff.py` covers fresh approved automatic queue, historic review-blocked failed queue, previously blocked friend auto job, exact source/digest/decision checks, no-action cases for all sent/uncertain/challenge/expired/tampered states, prevention of duplicate manual clicks, a stale publisher selection raced by owner handoff, normal manually authorized publishing and AST identity of all generated HTTP handler methods.
31+
32+
## Short operator acceptance
33+
34+
1. In OpenClawLab run `bobctl update`, confirm `v0.7.6-oc3-console1` → `v0.7.6-pubfix-manual1`, type **UPDATE**.
35+
2. On the authenticated LAN **Scout & review** page, find an existing **approved** draft corresponding to a blocked automatic job. If it was blocked *before* a send, the page now offers **Queue approved draft manually**. It should not offer it for rejected/uncertain/published jobs.
36+
3. **Do not click the button merely to test the release.** It schedules a real Moltbook comment. If you actually want that comment, click it once and confirm it appears as `manual, queued`. Normal publisher limits and timers then apply.
37+
4. Existing Console, Overview, Automation and existing review actions should continue to load.
38+
39+
**Next, after operator verification:** OC4, the one-view **Why no activity?** investigation panel with evidence-backed skip reasons and explicit approved-vs-rejected automatic block categorization. FM4/FM5 remain on hold.

‎releases/current.json‎

Lines changed: 10 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,22 +1,21 @@
11
{
22
"format": "bob-updater-v1",
3-
"release": "v0.7.6-oc3-console1",
4-
"from": "v0.7.6-oc2-automation1",
5-
"notes": "OC3 authenticated terminal-style read-only Console: safe classified local jobs/events/checkpoints and status, optional bounded explicit journal, severity/source/service filters, pause/scroll/copy/export. No shell or publishing authority.",
3+
"release": "v0.7.6-pubfix-manual1",
4+
"from": "v0.7.6-oc3-console1",
5+
"notes": "Repair explicit owner-approved automatic draft to manual queue handoff before any send; preserve strict duplicate/uncertain protections, guarded worker claim and visible Publish action. OC4 follows after acceptance.",
66
"files": [
77
{
8-
"name": "bob_diagnostic_console.py",
9-
"source": "src/lib/bob/bob_diagnostic_console.py"
8+
"name": "bob_publish_common.py",
9+
"source": "src/lib/bob/bob_publish_common.py"
1010
},
1111
{
12-
"name": "bob-activity-manager",
13-
"builder": "scripts/build_v076_oc3_candidate.py",
14-
"index": 0
12+
"name": "bob-moltbook-publisher",
13+
"source": "src/bin/bob-moltbook-publisher"
1514
},
1615
{
17-
"name": "navigation_shell.py",
18-
"builder": "scripts/build_v076_oc3_candidate.py",
19-
"index": 1
16+
"name": "bob-activity-manager",
17+
"builder": "scripts/build_v076_pubfix_handoff_candidate.py",
18+
"index": 0
2019
}
2120
],
2221
"restart_manager": true
Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
#!/usr/bin/env python3
2+
"""Safe publication handoff: add a guarded owner-action to existing manager UI.
3+
4+
Pinned OC3-generated manager only. The POST endpoint, Host/Origin/CSRF rules
5+
and all HTTP handlers stay unchanged; queue_manual owns transition policy.
6+
"""
7+
from __future__ import annotations
8+
import build_v076_oc3_candidate as prior
9+
10+
def once(code,old,new):
11+
if code.count(old)!=1:
12+
raise ValueError("publication handoff baseline drift: refusing release build")
13+
return code.replace(old,new,1)
14+
15+
def build():
16+
original,nav=prior.build()
17+
code=original.decode("utf-8")
18+
code=once(code,
19+
"queue_manual, now as utc_now",
20+
"queue_manual, manual_recovery_allowed, now as utc_now")
21+
code=once(code,
22+
" recent_replies = pdb.execute('SELECT author,content,detected_at "
23+
"FROM published_replies ORDER BY detected_at DESC LIMIT 15').fetchall()",
24+
" recoverable = {r['run_id'] for r in records if "
25+
"r['verified_draft'] and manual_recovery_allowed(pdb,r['run_id'])}\n"
26+
" recent_replies = pdb.execute('SELECT author,content,detected_at "
27+
"FROM published_replies ORDER BY detected_at DESC LIMIT 15').fetchall()")
28+
code=once(code,
29+
" cards.append(f'''<article class=\"card\" id=\"run-{esc(stamp)}\">",
30+
" if job and status=='approved' and stamp in recoverable:\n"
31+
" publication_html += (\n"
32+
" f'<form method=\"post\" action=\"/publish/{stamp}\">'\n"
33+
" f'<input type=\"hidden\" name=\"csrf\" value=\"{esc(TOKEN)}\">'\n"
34+
" f'<input type=\"hidden\" name=\"digest\" value=\"{esc(r[\"approval_sha256\"])}\">'\n"
35+
" '<button type=\"submit\">Queue approved draft manually</button></form>'\n"
36+
" '<p class=\"meta\">Automatic publication was stopped before sending. '\n"
37+
" 'Only this explicit owner action converts it to the manual queue. '\n"
38+
" 'No automatic retry of a possibly sent comment.</p>')\n"
39+
" cards.append(f'''<article class=\"card\" id=\"run-{esc(stamp)}\">")
40+
compile(code,"bob-activity-manager","exec")
41+
return code.encode("utf-8"),nav
42+
43+
if __name__=="__main__":
44+
manager,nav=build()
45+
print("OWNER APPROVAL TO MANUAL QUEUE RELEASE: PASS")
46+
print("manager_bytes:",len(manager),"navigation_bytes:",len(nav))
47+
print("live_services_modified: NO")

0 commit comments

Comments
 (0)