Skip to content

Commit b4792ee

Browse files
authored
Merge pull request #80 from nj-io/develop
Release: content vehicles, advisory flow, diagnostics
2 parents a00cf0f + 615a871 commit b4792ee

137 files changed

Lines changed: 9119 additions & 3028 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/loops/docs-maintenance.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ setup:
88
checks:
99
- "python scripts/generate_cli_docs.py && git diff --exit-code site-docs/cli/"
1010
- "ruff check src/ tests/"
11+
- "ruff format --check src/ tests/"
1112
- "mypy src/social_hook/"
1213

1314
autonomous:

docs/workbench-architecture.html

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -387,7 +387,7 @@ <h2>4. Captured Output Structure</h2>
387387
│ │ └── stage3: <span class="accent3">(grouped by draft group, not flat list)</span>
388388
│ │ └── draft_groups
389389
│ │ └── [strategy]: { targets[], drafter_output: <span class="existing">CreateDraftInput</span>
390-
│ │ { content, reasoning, format_hint, beat_count,
390+
│ │ { content, reasoning, vehicle, beat_count,
391391
│ │ media_type, media_spec, variants[] },
392392
│ │ resulting_drafts: [{ target, draft_id, status }] }
393393
│ │

mkdocs.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ nav:
6868
- Overview: cli/index.md
6969
- Commands: cli/root-commands.md
7070
- account: cli/account.md
71+
- advisory: cli/advisory.md
7172
- arc: cli/arc.md
7273
- bot: cli/bot.md
7374
- brief: cli/brief.md

scripts/e2e/harness.py

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -418,6 +418,7 @@ def seed_draft(self, project_id, status="draft", **kwargs):
418418
project_id=project_id,
419419
decision_id=decision.id,
420420
platform=kwargs.pop("platform", "x"),
421+
vehicle=kwargs.pop("vehicle", "single"),
421422
content=kwargs.pop("content", "E2E test draft content for social media."),
422423
status=status,
423424
media_paths=kwargs.pop("media_paths", []),
@@ -467,7 +468,7 @@ def clean_scenario_state(self):
467468
"SELECT id FROM drafts WHERE project_id = ?", (self.project_id,)
468469
).fetchall():
469470
self.conn.execute("DELETE FROM draft_changes WHERE draft_id = ?", (draft_row[0],))
470-
self.conn.execute("DELETE FROM draft_tweets WHERE draft_id = ?", (draft_row[0],))
471+
self.conn.execute("DELETE FROM draft_parts WHERE draft_id = ?", (draft_row[0],))
471472
self.conn.execute("DELETE FROM posts WHERE project_id = ?", (self.project_id,))
472473
self.conn.execute("DELETE FROM drafts WHERE project_id = ?", (self.project_id,))
473474
self.conn.execute("DELETE FROM decisions WHERE project_id = ?", (self.project_id,))

scripts/e2e/sections/__init__.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,7 @@
2222
"S": "base-project",
2323
"T": "base-project",
2424
"U": "base-project",
25+
"V": "base-project",
2526
}
2627

2728
SECTION_REGISTRY = {
@@ -53,4 +54,9 @@
5354
"needs_adapter": False,
5455
"needs_live": True,
5556
},
57+
"V": {
58+
"name": "Content Vehicles & Advisory",
59+
"module": "vehicles",
60+
"needs_adapter": True,
61+
},
5662
}

scripts/e2e/sections/crosspost.py

Lines changed: 48 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -184,8 +184,8 @@ def s2():
184184
draft = drafts[0]
185185
if draft.reference_post_id:
186186
detail += f", draft ref_id={draft.reference_post_id}"
187-
if draft.post_format:
188-
detail += f", post_format={draft.post_format}"
187+
if draft.reference_type:
188+
detail += f", reference_type={draft.reference_type}"
189189

190190
evaluation = {
191191
"angle": d.angle,
@@ -312,7 +312,7 @@ def s4():
312312
external_url="https://x.com/user/status/s4_external_123",
313313
)
314314

315-
# Create a Decision + Draft with post_format="quote" and reference_post_id
315+
# Create a Decision + Draft with reference_type="quote" and reference_post_id
316316
decision = Decision(
317317
id=generate_id("decision"),
318318
project_id=harness.project_id,
@@ -331,7 +331,7 @@ def s4():
331331
platform="x",
332332
content="This post references a previous one. #test",
333333
status="scheduled",
334-
post_format="quote",
334+
reference_type="quote",
335335
reference_post_id=seeded_post.id,
336336
)
337337
insert_draft(harness.conn, draft)
@@ -423,7 +423,7 @@ def s5():
423423
)
424424
insert_post(harness.conn, first_post)
425425

426-
# Create second Decision + Draft for "current" commit in same arc (no post_format set)
426+
# Create second Decision + Draft for "current" commit in same arc (no reference_type set)
427427
second_decision = Decision(
428428
id=generate_id("decision"),
429429
project_id=harness.project_id,
@@ -443,7 +443,7 @@ def s5():
443443
platform="x",
444444
content="Second post continuing the arc.",
445445
status="scheduled",
446-
# Intentionally no post_format or reference_post_id
446+
# Intentionally no reference_type or reference_post_id
447447
)
448448
insert_draft(harness.conn, second_draft)
449449
harness.conn.commit()
@@ -463,29 +463,30 @@ def s5():
463463
):
464464
result = _post_draft(harness.conn, second_draft, config)
465465

466-
# _post_draft should have detected arc continuation and set post_format + reference_post_id
466+
# _post_draft should have detected arc continuation and set reference_type + reference_post_id
467467
updated = ops.get_draft(harness.conn, second_draft.id)
468-
assert updated.post_format == "quote", (
469-
f"Expected post_format='quote', got '{updated.post_format}'"
468+
assert updated.reference_type == "quote", (
469+
f"Expected reference_type='quote', got '{updated.reference_type}'"
470470
)
471471
assert updated.reference_post_id == first_post.id, (
472472
f"Expected reference_post_id='{first_post.id}', got '{updated.reference_post_id}'"
473473
)
474474
assert result.success, f"Post failed: {result.error}"
475475

476476
return (
477-
f"Arc continuation: post_format={updated.post_format}, "
477+
f"Arc continuation: reference_type={updated.reference_type}, "
478478
f"reference_post_id={updated.reference_post_id}"
479479
)
480480

481481
runner.run_scenario("S5", "Arc continuation regression", s5)
482482

483483
# S6: Deterministic drafting pipeline
484484
def s6():
485-
from types import SimpleNamespace
486485

486+
from social_hook.config.platforms import resolve_platform
487487
from social_hook.config.project import load_project_config
488-
from social_hook.drafting import draft_for_platforms
488+
from social_hook.drafting import DraftingIntent, PlatformSpec
489+
from social_hook.drafting import draft as run_draft
489490
from social_hook.filesystem import generate_id
490491
from social_hook.llm.dry_run import DryRunContext
491492
from social_hook.llm.prompts import assemble_evaluator_context
@@ -517,21 +518,6 @@ def s6():
517518
insert_decision(harness.conn, decision)
518519
harness.conn.commit()
519520

520-
# Build evaluation with reference_posts pointing to seeded post
521-
evaluation = SimpleNamespace(
522-
decision="draft",
523-
reasoning="S6 test",
524-
angle="Feature showcase",
525-
episode_type="milestone",
526-
post_category="arc",
527-
arc_id=None,
528-
new_arc_theme=None,
529-
media_tool=None,
530-
reference_posts=[seeded_post.id],
531-
commit_summary="WS3 adapters implementation",
532-
include_project_docs=False,
533-
)
534-
535521
config = harness.load_config()
536522
project_config = load_project_config(str(harness.repo_path))
537523
commit = parse_commit_info(COMMITS["major_feature"], str(harness.repo_path))
@@ -547,33 +533,50 @@ def s6():
547533

548534
project = ops.get_project(harness.conn, harness.project_id)
549535

550-
draft_results = draft_for_platforms(
536+
# Build DraftingIntent with reference_posts
537+
platform_specs = []
538+
for pname, pcfg in config.platforms.items():
539+
if pcfg.enabled:
540+
rpcfg = resolve_platform(pname, pcfg, config.scheduling)
541+
platform_specs.append(PlatformSpec(platform=pname, resolved=rpcfg))
542+
543+
intent = DraftingIntent(
544+
decision="draft",
545+
reasoning="S6 test",
546+
angle="Feature showcase",
547+
post_category="arc",
548+
reference_posts=[seeded_post.id],
549+
commit_summary="WS3 adapters implementation",
550+
decision_id=decision.id,
551+
platforms=platform_specs,
552+
)
553+
554+
draft_results = run_draft(
555+
intent,
551556
config,
552557
harness.conn,
553558
db,
554559
project,
555-
decision_id=decision.id,
556-
evaluation=evaluation,
557-
context=context,
558-
commit=commit,
560+
context,
561+
commit,
559562
project_config=project_config,
560563
verbose=runner.verbose,
561564
)
562565

563-
assert len(draft_results) > 0, "No drafts created by draft_for_platforms"
566+
assert len(draft_results) > 0, "No drafts created by draft()"
564567

565-
draft = draft_results[0].draft
566-
assert draft.reference_post_id == seeded_post.id, (
567-
f"Expected reference_post_id={seeded_post.id}, got {draft.reference_post_id}"
568+
draft_obj = draft_results[0].draft
569+
assert draft_obj.reference_post_id == seeded_post.id, (
570+
f"Expected reference_post_id={seeded_post.id}, got {draft_obj.reference_post_id}"
568571
)
569-
assert draft.post_format == "quote", (
570-
f"Expected post_format='quote', got '{draft.post_format}'"
572+
assert draft_obj.reference_type == "quote", (
573+
f"Expected reference_type='quote', got '{draft_obj.reference_type}'"
571574
)
572575

573576
detail = (
574-
f"Draft ref_id={draft.reference_post_id}, "
575-
f"post_format={draft.post_format}, "
576-
f"content_len={len(draft.content)}"
577+
f"Draft ref_id={draft_obj.reference_post_id}, "
578+
f"reference_type={draft_obj.reference_type}, "
579+
f"content_len={len(draft_obj.content)}"
577580
)
578581

579582
runner.add_review_item(
@@ -582,15 +585,15 @@ def s6():
582585
decision="draft",
583586
episode_type="milestone",
584587
reasoning="S6 test — seeded evaluation with reference_posts",
585-
draft_content=draft.content,
588+
draft_content=draft_obj.content,
586589
evaluation={
587590
"reference_posts": [seeded_post.id],
588-
"post_format": draft.post_format,
589-
"reference_post_id": draft.reference_post_id,
591+
"reference_type": draft_obj.reference_type,
592+
"reference_post_id": draft_obj.reference_post_id,
590593
},
591594
review_question=(
592595
"Does the draft reference the seeded post? "
593-
"Is reference_post_id and post_format='quote' set correctly?"
596+
"Is reference_post_id and reference_type='quote' set correctly?"
594597
),
595598
)
596599
return detail

scripts/e2e/sections/pipeline.py

Lines changed: 21 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -225,7 +225,7 @@ def b7():
225225

226226
# B6: Free tier + long content → thread (structural check)
227227
def b6():
228-
from social_hook.db.operations import get_draft_tweets
228+
from social_hook.db.operations import get_draft_parts
229229

230230
exit_code = run_trigger(
231231
COMMITS["large_feature"], str(harness.repo_path), verbose=runner.verbose
@@ -234,7 +234,7 @@ def b6():
234234

235235
drafts = get_pending_drafts(harness.conn, harness.project_id)
236236
for draft in drafts:
237-
tweets = get_draft_tweets(harness.conn, draft.id)
237+
tweets = get_draft_parts(harness.conn, draft.id)
238238
if tweets:
239239
return f"Thread found: {len(tweets)} tweets"
240240

@@ -766,11 +766,12 @@ def b17():
766766
# B18: Drafter + real nano_banana_pro (seeded decision, real LLM + real Gemini)
767767
def b18():
768768
from pathlib import Path as _Path
769-
from types import SimpleNamespace
770769

770+
from social_hook.config.platforms import resolve_platform
771771
from social_hook.config.project import load_project_config
772772
from social_hook.db import operations as ops
773-
from social_hook.drafting import draft_for_platforms
773+
from social_hook.drafting import DraftingIntent, PlatformSpec
774+
from social_hook.drafting import draft as run_draft
774775
from social_hook.filesystem import generate_id
775776
from social_hook.llm.dry_run import DryRunContext
776777
from social_hook.llm.prompts import assemble_evaluator_context
@@ -828,37 +829,39 @@ def b18():
828829
parent_timestamp=commit.parent_timestamp,
829830
)
830831

831-
# Build evaluation compat (what make_eval_compat would produce)
832-
eval_compat = SimpleNamespace(
832+
# Build DraftingIntent
833+
project = ops.get_project(harness.conn, harness.project_id)
834+
platform_specs = []
835+
for pname, pcfg in config.platforms.items():
836+
if pcfg.enabled:
837+
rpcfg = resolve_platform(pname, pcfg, config.scheduling)
838+
platform_specs.append(PlatformSpec(platform=pname, resolved=rpcfg))
839+
840+
intent = DraftingIntent(
833841
decision="draft",
834842
reasoning="E2E test — seeded decision for nano_banana_pro media",
835843
angle="Feature showcase",
836-
episode_type="milestone",
837844
post_category="arc",
838-
arc_id=None,
839-
new_arc_theme=None,
840845
media_tool="nano_banana_pro",
841-
reference_posts=[],
842846
commit_summary=commit.message,
843-
include_project_docs=False,
847+
decision_id=decision.id,
848+
platforms=platform_specs,
844849
)
845850

846851
# Run drafting pipeline
847-
project = ops.get_project(harness.conn, harness.project_id)
848-
draft_results = draft_for_platforms(
852+
draft_results = run_draft(
853+
intent,
849854
config,
850855
harness.conn,
851856
db,
852857
project,
853-
decision_id=decision.id,
854-
evaluation=eval_compat,
855-
context=context,
856-
commit=commit,
858+
context,
859+
commit,
857860
project_config=project_config,
858861
verbose=runner.verbose,
859862
)
860863

861-
assert len(draft_results) > 0, "No drafts created by draft_for_platforms"
864+
assert len(draft_results) > 0, "No drafts created by draft()"
862865

863866
draft = draft_results[0].draft
864867
assert draft.media_type == "nano_banana_pro", (

scripts/e2e/sections/platform_posting.py

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -375,17 +375,17 @@ def _run(plat=plat, adapter=adapter):
375375
dry_run=not live,
376376
)
377377
assert thread_result.success, f"Thread failed: {thread_result.error}"
378-
assert len(thread_result.tweet_results) >= 2, (
379-
f"Expected >=2 results, got {len(thread_result.tweet_results)}"
378+
assert len(thread_result.part_results) >= 2, (
379+
f"Expected >=2 results, got {len(thread_result.part_results)}"
380380
)
381-
for i, tr in enumerate(thread_result.tweet_results):
381+
for i, tr in enumerate(thread_result.part_results):
382382
assert tr.external_id, f"Tweet {i + 1} missing external_id"
383383

384384
# Log and review
385-
head = thread_result.tweet_results[0]
386-
urls = [tr.external_url for tr in thread_result.tweet_results if tr.external_url]
385+
head = thread_result.part_results[0]
386+
urls = [tr.external_url for tr in thread_result.part_results if tr.external_url]
387387
with open(log_path, "a") as f:
388-
for tr in thread_result.tweet_results:
388+
for tr in thread_result.part_results:
389389
f.write(f"{plat}\t{tr.external_id}\t{tr.external_url}\n")
390390

391391
runner.add_review_item(
@@ -399,7 +399,7 @@ def _run(plat=plat, adapter=adapter):
399399
_pause_before_delete(pause, head.external_url)
400400
_safe_delete(adapter, head.external_id, label="thread head")
401401

402-
return f"Thread: {len(thread_result.tweet_results)} tweets"
402+
return f"Thread: {len(thread_result.part_results)} tweets"
403403

404404
runner.run_scenario(
405405
f"U-{plat}-thread",
@@ -717,7 +717,7 @@ def u_post_media(plat=platform, adapter=plat_adapter):
717717

718718
# Consistency check for all capabilities
719719
for cap in plat_adapter.capabilities():
720-
if cap.name == "single_post":
720+
if cap.name == "single":
721721
continue
722722

723723
def u_capability(plat=platform, adapter=plat_adapter, capability=cap):

0 commit comments

Comments
 (0)