From f0b0823bf52bae5719e2a0fbe0b430a923d288ee Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 22 Jul 2026 22:35:27 +0300 Subject: [PATCH 1/5] Remove requires rule relations --- AGENTS.md | 4 ---- 1 file changed, 4 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index eb44d8e..e0a9387 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -11,15 +11,11 @@ ## HasbaraTops - [HASBARA-CHECK-01] Run `dialogue-lab check` before a canonical write, after a failed write or readiness check, or when database state is uncertain; ordinary read-only queries must reuse fresh sufficient evidence. - - requires: HASBARA-CANONICAL-01 - [HASBARA-CANONICAL-01] Treat repository Markdown as canonical governance, strategy, and evidence content and the configured SQLite database as canonical Case and Turn state; do not use MCP for Dialogue Lab work. - [HASBARA-IDENTITY-01] Use `config/storage.toml`; preserve the SQLite schema version; treat Case ID as definitive, allow multiple Cases per `Post ID + Root Comment ID`, and treat root lookup as candidate discovery only. - - requires: HASBARA-CANONICAL-01 - [HASBARA-IDENTITY-02] Deduplicate Turns by supplied `reply_comment_id`; when absent, use Case ID + Parent Turn ID (including null roots) + Direction + Exact Text; never use mutable latest-reply state as identity. - - requires: HASBARA-IDENTITY-01 - limits: HASBARA-OPEN-CASES-01 - [HASBARA-OPEN-CASES-01] When presenting open Cases, use each Case's latest public Turn supplied exact URL; never substitute the Case root URL, and mark a missing link explicitly. - - requires: HASBARA-CANONICAL-01 - Invoke the matching Dialogue Lab skill for intake, follow-up, posting confirmation, closeout, or strategy review. - [DL-REPLY-01] When proposing a public Dialogue Lab reply, provide one complete, self-contained, ready-to-post response From c6aaa783908f9dcc665d9ebc675d15404fc9e994 Mon Sep 17 00:00:00 2001 From: Codex Date: Wed, 22 Jul 2026 22:43:04 +0300 Subject: [PATCH 2/5] Move General responses into docs --- .../General responses.docx | Bin 1 file changed, 0 insertions(+), 0 deletions(-) rename General responses.docx => docs/General responses.docx (100%) diff --git a/General responses.docx b/docs/General responses.docx similarity index 100% rename from General responses.docx rename to docs/General responses.docx From 1dc85d957ea87a4aa78da00aaa933de16ad3c900 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 04:41:37 +0300 Subject: [PATCH 3/5] refactor: consolidate managed skill sources --- AGENTS.history.json | 163 +++--------------- AGENTS.md | 124 ++++--------- README.md | 18 +- scripts/install-skills.py | 123 +++++++++++++ .../dialogue-lab-closeout/SKILL.md | 1 - .../dialogue-lab-closeout/agents/openai.yaml | 0 .../dialogue-lab-followup/SKILL.md | 0 .../dialogue-lab-followup/agents/openai.yaml | 0 .../dialogue-lab-intake/SKILL.md | 0 .../dialogue-lab-intake/agents/openai.yaml | 0 .../dialogue-lab-posting/SKILL.md | 1 - .../dialogue-lab-posting/agents/openai.yaml | 0 .../dialogue-lab-strategy-review/SKILL.md | 4 - .../agents/openai.yaml | 0 templates/sections/core.md | 20 +++ templates/skill-sections.json | 26 +++ 16 files changed, 244 insertions(+), 236 deletions(-) create mode 100644 scripts/install-skills.py rename {.agents/skills => skills}/dialogue-lab-closeout/SKILL.md (94%) rename {.agents/skills => skills}/dialogue-lab-closeout/agents/openai.yaml (100%) rename {.agents/skills => skills}/dialogue-lab-followup/SKILL.md (100%) rename {.agents/skills => skills}/dialogue-lab-followup/agents/openai.yaml (100%) rename {.agents/skills => skills}/dialogue-lab-intake/SKILL.md (100%) rename {.agents/skills => skills}/dialogue-lab-intake/agents/openai.yaml (100%) rename {.agents/skills => skills}/dialogue-lab-posting/SKILL.md (97%) rename {.agents/skills => skills}/dialogue-lab-posting/agents/openai.yaml (100%) rename {.agents/skills => skills}/dialogue-lab-strategy-review/SKILL.md (94%) rename {.agents/skills => skills}/dialogue-lab-strategy-review/agents/openai.yaml (100%) create mode 100644 templates/sections/core.md create mode 100644 templates/skill-sections.json diff --git a/AGENTS.history.json b/AGENTS.history.json index 4d2a605..e333ed2 100644 --- a/AGENTS.history.json +++ b/AGENTS.history.json @@ -2,166 +2,43 @@ "version": 2, "entries": [ { - "id": "2026-07-22-case-turn-identity-redesign", - "date": "2026-07-22", - "rules": [ - "HASBARA-IDENTITY-01", - "HASBARA-IDENTITY-02" - ], - "relations": { - "requires": [ - "configured SQLite database is canonical Case and Turn state", - "canonical writes use explicit-approval transactional commands with read-back" - ] - }, - "reason": "Replace mutable Facebook-root and date-scoped identity with stable Case and Turn keys.", - "preserved": "SQLite schema version 1, case-local Turn IDs and parent graph, explicit write approval, transactional rollback, committed read-back, and Facebook-access restrictions.", - "introduced": "Globally sequential Case IDs, multiple Cases per Facebook root, root candidate lookup, and deterministic Turn duplicate identities.", - "validation": "The candidate permits separate same-root reply branches, keeps explicit Case-ID selection definitive, covers null-parent root Turns, and excludes latest-reply identity.", - "original_sha256": "c11a2f51ac39e4ff2cac7dee48f581533c256b898a556c866e85e05ebb5b20e5", - "rules_sha256": "64e705236213c82f520f93a1f4a40cd1746cdc629c5c0def29a5ef86219009f5", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42" - }, - { - "id": "2026-07-22-canonical-markdown-check", - "date": "2026-07-22", - "rules": [ - "HASBARA-CHECK-01", - "HASBARA-CANONICAL-01" - ], - "relations": { - "requires": [ - "HASBARA-IDENTITY-01", - "explicit-approval transactional SQLite writes" - ] - }, - "reason": "Give the mandatory readiness command a plain operational name and keep its canonical Markdown scope explicit.", - "preserved": "The pre-case readiness gate, SQLite schema and integrity checks, canonical Case and Turn state, and external-write restrictions.", - "introduced": "The check command validates canonical repository Markdown and configured SQLite before case work.", - "validation": "The old command name has no current reference or alias, while the readiness gate continues to hash configured repository Markdown before checking SQLite.", - "original_sha256": "64e705236213c82f520f93a1f4a40cd1746cdc629c5c0def29a5ef86219009f5", - "rules_sha256": "9f172e07852ddd349ccf7bf65ca115cc5ba8a0d05a62e629aba547ab56310830", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42" - }, - { - "id": "2026-07-22-readiness-and-open-case-presentation", - "date": "2026-07-22", "rules": [ "HASBARA-CHECK-01", + "HASBARA-CANONICAL-01", + "HASBARA-IDENTITY-01", + "HASBARA-IDENTITY-02", "HASBARA-OPEN-CASES-01" ], - "relations": { - "requires": [ - "HASBARA-CANONICAL-01", - "HASBARA-IDENTITY-01" - ] - }, - "reason": "Avoid redundant readiness work on ordinary reads and prevent root permalinks from being presented as latest-comment links.", - "preserved": "Readiness validation before canonical writes and after failures, exact supplied URL preservation, and the rule that mutable latest-Turn state is never identity.", - "introduced": "Read-only queries may reuse fresh evidence, and open-Case presentation reports the latest public Turn permalink or an explicit missing-link state without root fallback.", - "validation": "The open-Case producer selects public Turns only, exposes the selected Turn ID, reports missing URLs explicitly, and skills place readiness checks immediately before approved writes rather than routine reads.", - "original_sha256": "9f172e07852ddd349ccf7bf65ca115cc5ba8a0d05a62e629aba547ab56310830", - "rules_sha256": "a13eb777209419f0fd81ee29bde8c2c3091041ec8e737d3e1970529f7c18c0ca", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42" - }, - { - "id": "2026-07-22-readiness-evidence-reuse", - "date": "2026-07-22", - "rules": [ - "HASBARA-CHECK-01" - ], - "relations": { - "requires": [ - "HASBARA-CANONICAL-01", - "HASBARA-OPEN-CASES-01" - ] - }, - "reason": "Prevent redundant readiness checks during ordinary read-only queries when fresh sufficient evidence already exists.", - "preserved": "Mandatory readiness checks before canonical writes, after failures, and whenever database state is uncertain.", - "introduced": "Ordinary read-only queries must reuse fresh sufficient readiness evidence rather than optionally rerunning the check.", - "validation": "The rule retains every write and uncertainty gate while making the previously optional read-only reuse behavior mandatory.", - "original_sha256": "a13eb777209419f0fd81ee29bde8c2c3091041ec8e737d3e1970529f7c18c0ca", - "rules_sha256": "0c66144eed98d6320a6d0ccbad65e84257a28f7856e28e3bc0e50b4b763f6497", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42" - }, - { - "id": "2026-07-22-complete-public-replies", - "date": "2026-07-22", - "rules": [ - "DL-REPLY-01" - ], - "related_global_rules": [ - "OUT-03" - ], - "reason": "A recommended public reply was delivered as staged fragments and was later expanded using private planning instead of as one complete response to the supplied public exchange.", - "preserved": "Reply brevity, one-pivotal-point focus, verified external fact use, and the follow-up skill's public-context boundary remain unchanged.", - "introduced": "DL-REPLY-01 requires complete ready-to-post text for the supplied post and target comment chain unless the user requests partial or staged content.", - "validation": "Replayed the fragmentary-reply and private-context failures; the rule requires a complete public-exchange reply while retaining user-requested fragments and verified external facts.", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42", - "rules_sha256": "8cb6faeeee366a2b175440bc30f776821acb9739a06ed47765ab59d78e18dfea", - "source_history": "Targeted Git history was used because no prior local AGENTS.history.json existed." + "decision": "Use repository Markdown and the configured SQLite database as canonical state, stable Case and Turn keys, candidate-only Facebook-root lookup, reusable fresh read evidence, and exact latest-public-Turn links.", + "reason": "Prevent mutable root, date, latest-reply, and presentation state from becoming identity while preserving an explicit readiness gate for canonical writes.", + "regression": "Preserve schema version 1, multiple Cases per Facebook root, null-parent Turn identity, explicit transactional approval and read-back, and the prohibition on root-URL fallback for open Cases." }, { - "id": "2026-07-22-unconditional-complete-public-replies", - "date": "2026-07-22", "rules": [ - "DL-REPLY-01" - ], - "related_global_rules": [ + "DL-REPLY-01", "OUT-03" ], - "reason": "The user required every proposed public Dialogue Lab reply to be complete and ready to post, without a partial-or-staged exception.", - "preserved": "Self-contained ready-to-post output remains required; fact verification and public-context isolation remain separate in the follow-up skill.", - "introduced": "DL-REPLY-01 now unconditionally requires one complete, self-contained, ready-to-post response.", - "validation": "Replayed full and partial reply outputs; a complete ready-to-post response passes, while partial or staged output fails regardless of request wording.", - "global_rules_sha256": "8ef798b3330613534c7b03ca75aeb7fb9413a4c6774e2f7388d258a09140ed42", - "rules_sha256": "cbf29456442e949912c4af8e0419f2cb36cf3b8f10d2c5400486d848734dc4ba", - "source_history": "Prior structured local history and the related global OUT-03 history were consulted." + "decision": "Every proposed public Dialogue Lab reply is one complete, self-contained, ready-to-post response while remaining concise.", + "reason": "Prevent staged fragments and private planning context from replacing a complete response to the supplied public exchange.", + "regression": "Preserve concise one-point replies, verified facts, public-context isolation, and the ban on partial or staged output." }, { - "id": "2026-07-22-reapply-complete-public-replies-to-current-release", - "date": "2026-07-22", "rules": [ - "DL-REPLY-01" - ], - "related_global_rules": [ - "OUT-03" + "*" ], - "reason": "Apply the approved complete-public-reply rule and public-context boundary to release/local rebuilt from current main.", - "preserved": "All shipped Case and Turn identity rules, earlier history records, current global output brevity, fact verification, and unrelated Dialogue Lab behavior remain unchanged.", - "introduced": "The final DL-REPLY-01 wording and matching follow-up skill boundary coexist with the shipped identity model on release/local.", - "validation": "Current global OUT-03 and every existing local rule were re-read; concise output remains compatible with one complete ready-to-post public reply, and no prior history relation was rewritten.", - "global_rules_sha256": "b0df68cb05e31cf808cbcc932b71241c672471146720d99601de168c2e4ce5ce", - "rules_sha256": "9f6fd346849cf39880007660957e0a432944004f18c5bf5e80118eb5fbea824e", - "source_history": "The uncommitted full-reply-rule history, shipped local history, and current global OUT-03 history were consulted." + "decision": "Keep local AGENTS rules structured and project-specific; global instructions own general worktree, maintenance, and output policy.", + "reason": "Prevent invalid unnumbered rules and duplicated global governance from drifting inside the HasbaraTops scope.", + "regression": "Preserve every Hasbara canonical-state, identity, write-safety, Facebook, protected-document, and reply-completeness boundary." }, { - "id": "2026-07-22-active-rule-relation-metadata", - "date": "2026-07-22", "rules": [ - "HASBARA-CHECK-01", - "HASBARA-IDENTITY-01", - "HASBARA-IDENTITY-02", - "HASBARA-OPEN-CASES-01", - "DL-REPLY-01" - ], - "relation_neighbors": [ - "HASBARA-CANONICAL-01", - "HASBARA-IDENTITY-01", - "HASBARA-IDENTITY-02", - "HASBARA-OPEN-CASES-01", - "OUT-03" - ], - "related_global_rules": [ - "OUT-03" + "HASBARA-SKILLS-SOURCE-01", + "HASBARA-SKILLS-LIFECYCLE-01", + "FILE-02" ], - "reason": "Make current rule interactions queryable instead of inferring them from historical entries.", - "preserved": "Every prior history entry remains unchanged, and HASBARA-CHECK-01 has no current relation to HASBARA-OPEN-CASES-01.", - "introduced": "Active requires and limits metadata for canonical state, Case and Turn identity, open-Case presentation, and complete public replies.", - "validation": "The rule-history helper resolves direct current neighbors across the local and global rule sources without unknown IDs.", - "global_rules_sha256": "b0df68cb05e31cf808cbcc932b71241c672471146720d99601de168c2e4ce5ce", - "original_sha256": "9f6fd346849cf39880007660957e0a432944004f18c5bf5e80118eb5fbea824e", - "rules_sha256": "ae6738bd2deca5db34ee3c1ec0832aec84c6d84d0c2c0bfb0080fb93fe830a68" + "decision": "Use repo-root skills as the sole HasbaraTops skill source and route compatible skill maintenance, installation, and staging through the lifecycle and repository installer.", + "reason": "Replace the obsolete source layout and nonexistent installer command with the current Ceratops-compatible ownership model.", + "regression": "Retain task-worktree isolation, prevent duplicate skill sources, and do not imply that runtime installation occurs without explicit scope." } ] } diff --git a/AGENTS.md b/AGENTS.md index eb44d8e..db35b10 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,94 +1,46 @@ -## Instruction enforcement +# HasbaraTops -- All instruction bullets in this file are mandatory, blocking, and - closure-gating for the phase, action, decision, artifact, or response they - govern. -- Do not proceed with or claim completion for any action, decision, artifact, or - response when an applicable instruction bullet is unmet, unverifiable, or in - conflict; report the blocker or conflict instead. - - -## HasbaraTops - -- [HASBARA-CHECK-01] Run `dialogue-lab check` before a canonical write, after a failed write or readiness check, or when database state is uncertain; ordinary read-only queries must reuse fresh sufficient evidence. +- [HASBARA-CHECK-01] Run `dialogue-lab check` before a canonical write, + after a failed write or readiness check, or when database state is uncertain; + ordinary read-only queries must reuse fresh sufficient evidence. - requires: HASBARA-CANONICAL-01 -- [HASBARA-CANONICAL-01] Treat repository Markdown as canonical governance, strategy, and evidence content and the configured SQLite database as canonical Case and Turn state; do not use MCP for Dialogue Lab work. -- [HASBARA-IDENTITY-01] Use `config/storage.toml`; preserve the SQLite schema version; treat Case ID as definitive, allow multiple Cases per `Post ID + Root Comment ID`, and treat root lookup as candidate discovery only. +- [HASBARA-CANONICAL-01] Treat repository Markdown as canonical governance, + strategy, and evidence content and the configured SQLite database as canonical + Case and Turn state; do not use MCP for Dialogue Lab work. +- [HASBARA-IDENTITY-01] Use `config/storage.toml`, preserve the SQLite schema + version, treat Case ID as definitive, allow multiple Cases per Facebook root, + and use root lookup only for candidate discovery. - requires: HASBARA-CANONICAL-01 -- [HASBARA-IDENTITY-02] Deduplicate Turns by supplied `reply_comment_id`; when absent, use Case ID + Parent Turn ID (including null roots) + Direction + Exact Text; never use mutable latest-reply state as identity. +- [HASBARA-IDENTITY-02] Deduplicate Turns by supplied `reply_comment_id`; when + absent, use Case ID, Parent Turn ID, Direction, and Exact Text; never use + mutable latest-reply state as identity. - requires: HASBARA-IDENTITY-01 - limits: HASBARA-OPEN-CASES-01 -- [HASBARA-OPEN-CASES-01] When presenting open Cases, use each Case's latest public Turn supplied exact URL; never substitute the Case root URL, and mark a missing link explicitly. +- [HASBARA-OPEN-CASES-01] Present each open Case with its latest public Turn's + supplied exact URL; never substitute the Case root URL, and mark a missing + link explicitly. - requires: HASBARA-CANONICAL-01 -- Invoke the matching Dialogue Lab skill for intake, follow-up, posting confirmation, closeout, or strategy review. +- [HASBARA-SKILL-ROUTING-01] Invoke the matching Dialogue Lab skill for the + requested case or strategy workflow. - [DL-REPLY-01] When proposing a public Dialogue Lab reply, provide one - complete, self-contained, ready-to-post response + complete, self-contained, ready-to-post response. - limits: OUT-03 -- Never edit, move, rename, replace, delete, import, or summarize `General responses` without the user's explicit instruction for that exact action. -- Perform canonical writes only through explicit-approval `dialogue-lab` commands that use SQLite transactions and committed read-back verification. -- After a failed canonical write, verify rollback and database integrity; block further writes while either remains unresolved. -- Never open, inspect, click, scroll, or otherwise interact with Facebook unless the user explicitly asks. Never post to Facebook autonomously. Never commit SQLite databases, exports, backups, credentials, or secrets. - -### Repo checkout and worktrees: - -- The primary skills repo checkout used to generate installed HasbaraTops skill - copies must stay either on local `main` tracking `origin/main` or on a local - `release/*` branch created from `main` for an active unpublished batch. -- Do not develop or patch HasbaraTops skill source directly in the skills repo - checkout during create, update, audit, or repair work; for any task that - modifies skills, work in one thread-owned git worktree, name that worktree - after the thread rather than a subtask, reuse it for follow-on skill changes - in the same thread unless conflicting branch histories or explicit user - direction require a new one, and do not place it inside the skills repo - checkout. -- Keep installed HasbaraTops skill folders generated from the skills repo checkout - path, not from task worktrees. For local preview of unpublished batches, - refresh remote refs with `git fetch --prune origin`, then merge ready worktree - branches into the skills repo checkout's local `release/*` branch and rerun - `skills/ceratops-skill-lifecycle/scripts/runtime/install-managed-skills.ps1` - instead of generating installed skills from task worktrees. -- Do not stage skill-source changes into a local `release/*` batch unless the - task explicitly requests staging, shipping, or local preview sync. -- Skills-repo changes must ship from `release/*`, never directly from task or - feature branches. -- New HasbaraTops skill creation is the only default-staging exception: - `$ceratops-skill-lifecycle` create must finish with change-promotion and - install verification unless the user opts out. -- For staging or shipping, use `$ceratops-skill-lifecycle` change-promotion or - ship-to-remote. After shipping, restore the checkout from `origin/main`, - reinstall managed skills from `main`, and report retained worktrees or release - branches. - -### Instruction and skill maintenance: - -- Before proposing or editing `AGENTS.md`, `automation.toml`, `SKILL.md`, shared - skill sections, skill manifests, or helper-contract text, re-open the relevant - files from disk and use the current contents as the source of truth. -- Treat recommendations about instruction, automation, skill, and - helper-contract changes as advisory unless the user explicitly asks to apply a - named change. -- In repo-tracked files intended for public sharing or GitHub, including - repo-tracked `AGENTS.md`, `automation.toml`, `SKILL.md`, generated runtime - skill files, scripts, docs, and examples, do not hardcode user-local absolute - filesystem paths unless an external runtime explicitly requires them; use - repo-relative paths or portable variables such as `$CODEX_HOME`. -- For skill runtime workflows, invoke shared helpers through installed console - commands, `python -m ` entrypoints, or scripts copied into the - installed skill folder; do not locate shared helpers by absolute paths or by - the repo's parent directory. -- When a workflow needs a shared repo-maintenance script, run `scripts/` - from the active source checkout root when available, otherwise from the - installed skill folder; when a helper is skill-local, run it from that skill - folder or the corresponding source skill folder; stop as blocked if neither - declared location contains it. -- Prefer concise, principle-based, machine-oriented wording; avoid example lists - unless the examples are needed to disambiguate behavior. -- After instruction edits, verify the changed diff or reopened section and - confirm no new duplicate, contradiction, or dropped behavior was introduced. -- When an automation uses a script or helper, compare prompt and code before - finishing and keep outcome, blocker, cleanup, alert, and memory paths aligned. -- Put deterministic, testable, or procedural automation behavior in scripts or - helpers rather than prompt text when such helpers exist. -- When updating an automation, skill, instruction, or related helper script, - assess whether the change could materially increase recurring or avoidable - credit usage; if so, report that before treating the update as done. +- [HASBARA-GENERAL-RESPONSES-01] Never inspect, edit, move, rename, replace, + delete, import, or summarize `General responses` without explicit instruction + for that exact action. +- [HASBARA-WRITE-01] Perform canonical writes only through explicitly approved + `dialogue-lab` commands using SQLite transactions and committed read-back. + - requires: HASBARA-CHECK-01, HASBARA-CANONICAL-01 +- [HASBARA-ROLLBACK-01] After a failed canonical write, verify rollback and + database integrity; block further writes until both pass. + - requires: HASBARA-WRITE-01 +- [HASBARA-FACEBOOK-01] Interact with Facebook only when the user explicitly + requests that interaction; never post autonomously. +- [HASBARA-SENSITIVE-STATE-01] Never commit SQLite state, data exports or + backups, or credentials and secrets. +- [HASBARA-SKILLS-SOURCE-01] Treat repo-root `skills/` as the authoritative + source for HasbaraTops skills. +- [HASBARA-SKILLS-LIFECYCLE-01] Use the installed Ceratops skill lifecycle for + skill maintenance, runtime installation, and release staging through the + repository installer. + - requires: HASBARA-SKILLS-SOURCE-01 diff --git a/README.md b/README.md index 0a88e10..210a9d2 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ docs/reply-strategy-guide.md canonical cross-case strategy docs/evidence-base.md canonical reusable evidence external SQLite database canonical Cases and Turns dialogue-lab CLI only canonical storage boundary -.agents/skills/ model-driven analysis and reply workflows +skills/ model-driven analysis and reply workflows ``` `General responses` is protected and outside this workflow. The CLI exposes no operation for it. @@ -71,6 +71,16 @@ A non-null `reply_comment_id` from a supplied permalink is globally unique acros `case-list-open` returns one row per Case with the latest public Turn's supplied exact URL. It never substitutes the Case root URL; when the latest Turn has no supplied URL, it returns `permalink_status: "missing"` and a null permalink. This ordering is presentation only and never identity. When sibling branches in one Case must be tracked independently, `case-split-branch` keeps the selected branch in a newly allocated Case, copies its shared ancestor path with fresh case-local Turn IDs, creates a verified backup, commits transactionally, and reads both graphs back. It refuses to copy a shared ancestor carrying a globally unique `reply_comment_id`. +## Skills + +| Skill | Purpose | +| --- | --- | +| `dialogue-lab-intake` | Start or identify a case and prepare an approval-gated intake transaction. | +| `dialogue-lab-followup` | Process a public turn in an existing case and prepare one follow-up transaction. | +| `dialogue-lab-posting` | Record an explicitly confirmed published reply without posting to Facebook. | +| `dialogue-lab-closeout` | Close a case from observable evidence through one approved transaction. | +| `dialogue-lab-strategy-review` | Review closed-case evidence and propose strategy-guide changes. | + ## Dialogue workflows - Intake: `$dialogue-lab-intake` @@ -81,6 +91,12 @@ A non-null `reply_comment_id` from a supplied permalink is globally unique acros Skills use read commands automatically. They may pass `--approved` only after the user approves the exact canonical write. No skill publishes to Facebook. +Install the managed Codex skills after repository setup: + +```powershell +python scripts/install-skills.py --repo-root . +``` + ## Import and identity migration 1. Prepare a UTF-8 JSON snapshot with `cases` and `turns` arrays using the exact payload contract. diff --git a/scripts/install-skills.py b/scripts/install-skills.py new file mode 100644 index 0000000..8144aaf --- /dev/null +++ b/scripts/install-skills.py @@ -0,0 +1,123 @@ +#!/usr/bin/env python3 +"""Bootstrap managed skill installation through the lifecycle runtime bundle. + +The Ceratops lifecycle bundle owns the authoritative template for this +bootstrap. Compatible repositories carry it as ``scripts/install-skills.py``. +Keep repository-specific behavior out of this file: validation, rendering, +ownership checks, and stale cleanup belong to the installed lifecycle bundle. +""" + +from __future__ import annotations + +import argparse +import json +import os +import pathlib +import subprocess +import sys + + +INSTALLER_VERSION = 2 +LIFECYCLE_SKILL = "ceratops-skill-lifecycle" +RESOLVER_RELATIVE = pathlib.Path("scripts/runtime/resolve-lifecycle-bundle.py") +INSTALLER_RELATIVE = pathlib.Path("scripts/runtime/install-managed-skills.py") + + +def codex_skills_root() -> pathlib.Path: + """Return the personal runtime skill root used to discover the bundle.""" + + codex_home = os.environ.get("CODEX_HOME") + return pathlib.Path(codex_home).expanduser() / "skills" if codex_home else pathlib.Path.home() / ".codex" / "skills" + + +def resolver_path(repo_root: pathlib.Path) -> pathlib.Path: + """Use a capable installed resolver or the newer Ceratops source resolver.""" + + installed_bundle = codex_skills_root() / LIFECYCLE_SKILL + installed = installed_bundle / RESOLVER_RELATIVE + installed_version = 0 + try: + manifest = json.loads( + (installed_bundle / ".runtime-manifest.json").read_text(encoding="utf-8") + ) + value = manifest.get("installer_version") if isinstance(manifest, dict) else None + if isinstance(value, int) and not isinstance(value, bool): + installed_version = value + except (OSError, json.JSONDecodeError): + pass + if installed.is_file() and installed_version >= INSTALLER_VERSION: + return installed + + checkout = repo_root / "skills" / LIFECYCLE_SKILL / RESOLVER_RELATIVE + if checkout.is_file(): + return checkout + if installed.is_file(): + return installed + + raise FileNotFoundError( + "A supported installed ceratops-skill-lifecycle bundle is required. " + "Only the Ceratops source repository can bootstrap the first installation." + ) + + +def run_checked(arguments: list[str], failure: str) -> str: + """Run one helper and preserve compact failure evidence.""" + + result = subprocess.run(arguments, capture_output=True, text=True, check=False) + if result.returncode != 0: + detail = (result.stderr or result.stdout).strip() + raise RuntimeError(f"{failure}: {detail}" if detail else failure) + return result.stdout.strip() + + +def main() -> int: + """Resolve one lifecycle bundle and run its validating installer.""" + + parser = argparse.ArgumentParser(description="Install managed Ceratops-compatible skills.") + parser.add_argument("--repo-root", type=pathlib.Path, help="Source repository root; defaults to this script's repository.") + parser.add_argument("--install-root", type=pathlib.Path, help="Runtime skills root; defaults to $CODEX_HOME/skills.") + parser.add_argument("--skill", action="append", help="Install only this skill; repeat for multiple skills.") + args = parser.parse_args() + + repo_root = (args.repo_root or pathlib.Path(__file__).resolve().parents[1]).resolve() + try: + resolver = resolver_path(repo_root) + bundle_text = run_checked( + [ + sys.executable, + str(resolver), + "--repo-root", + str(repo_root), + "--installer-version", + str(INSTALLER_VERSION), + ], + "Lifecycle bundle resolution failed", + ) + bundle_root = pathlib.Path(bundle_text).resolve() + runtime_installer = bundle_root / INSTALLER_RELATIVE + if not runtime_installer.is_file(): + raise FileNotFoundError(f"Missing lifecycle runtime installer: {runtime_installer}") + + command = [ + sys.executable, + str(runtime_installer), + "--repo-root", + str(repo_root), + "--installer-version", + str(INSTALLER_VERSION), + ] + if args.install_root is not None: + command.extend(("--install-root", str(args.install_root.resolve()))) + for skill_name in args.skill or []: + command.extend(("--skill", skill_name)) + output = run_checked(command, "Managed skill installation failed") + except (FileNotFoundError, RuntimeError, OSError) as exc: + print(str(exc), file=sys.stderr) + return 1 + + print(output or "OK") + return 0 + + +if __name__ == "__main__": + raise SystemExit(main()) diff --git a/.agents/skills/dialogue-lab-closeout/SKILL.md b/skills/dialogue-lab-closeout/SKILL.md similarity index 94% rename from .agents/skills/dialogue-lab-closeout/SKILL.md rename to skills/dialogue-lab-closeout/SKILL.md index f0140c0..54c9500 100644 --- a/.agents/skills/dialogue-lab-closeout/SKILL.md +++ b/skills/dialogue-lab-closeout/SKILL.md @@ -21,7 +21,6 @@ description: Close an Israel Facebook Dialogue Lab case from observable evidence ## Safety - Do not close solely because no new message is visible unless the user requests or reports no-response closure. -- Never access or write `General responses`, and never post to Facebook. ## Output diff --git a/.agents/skills/dialogue-lab-closeout/agents/openai.yaml b/skills/dialogue-lab-closeout/agents/openai.yaml similarity index 100% rename from .agents/skills/dialogue-lab-closeout/agents/openai.yaml rename to skills/dialogue-lab-closeout/agents/openai.yaml diff --git a/.agents/skills/dialogue-lab-followup/SKILL.md b/skills/dialogue-lab-followup/SKILL.md similarity index 100% rename from .agents/skills/dialogue-lab-followup/SKILL.md rename to skills/dialogue-lab-followup/SKILL.md diff --git a/.agents/skills/dialogue-lab-followup/agents/openai.yaml b/skills/dialogue-lab-followup/agents/openai.yaml similarity index 100% rename from .agents/skills/dialogue-lab-followup/agents/openai.yaml rename to skills/dialogue-lab-followup/agents/openai.yaml diff --git a/.agents/skills/dialogue-lab-intake/SKILL.md b/skills/dialogue-lab-intake/SKILL.md similarity index 100% rename from .agents/skills/dialogue-lab-intake/SKILL.md rename to skills/dialogue-lab-intake/SKILL.md diff --git a/.agents/skills/dialogue-lab-intake/agents/openai.yaml b/skills/dialogue-lab-intake/agents/openai.yaml similarity index 100% rename from .agents/skills/dialogue-lab-intake/agents/openai.yaml rename to skills/dialogue-lab-intake/agents/openai.yaml diff --git a/.agents/skills/dialogue-lab-posting/SKILL.md b/skills/dialogue-lab-posting/SKILL.md similarity index 97% rename from .agents/skills/dialogue-lab-posting/SKILL.md rename to skills/dialogue-lab-posting/SKILL.md index 50c57a7..cbfde63 100644 --- a/.agents/skills/dialogue-lab-posting/SKILL.md +++ b/skills/dialogue-lab-posting/SKILL.md @@ -22,7 +22,6 @@ description: Record a user's explicit confirmation that a Dialogue Lab reply was - Never publish, edit, or delete Facebook content. - Never assume the posted text equals an earlier draft. -- Never access or write `General responses`. ## Output diff --git a/.agents/skills/dialogue-lab-posting/agents/openai.yaml b/skills/dialogue-lab-posting/agents/openai.yaml similarity index 100% rename from .agents/skills/dialogue-lab-posting/agents/openai.yaml rename to skills/dialogue-lab-posting/agents/openai.yaml diff --git a/.agents/skills/dialogue-lab-strategy-review/SKILL.md b/skills/dialogue-lab-strategy-review/SKILL.md similarity index 94% rename from .agents/skills/dialogue-lab-strategy-review/SKILL.md rename to skills/dialogue-lab-strategy-review/SKILL.md index 72a753d..e1f13de 100644 --- a/.agents/skills/dialogue-lab-strategy-review/SKILL.md +++ b/skills/dialogue-lab-strategy-review/SKILL.md @@ -19,10 +19,6 @@ description: Review deterministic SQLite evidence across closed Israel Facebook 5. Distinguish supported findings, provisional hypotheses, and inconclusive results. Keep case-specific lessons in SQLite. 6. Draft the smallest exact Strategy Guide change only when evidence supports it. Do not edit the guide in this run; a separate explicit approval and reviewed repository change are required. -## Safety - -- Never access or write `General responses`, and never post to Facebook. - ## Output Return review scope, closed-case count, comparable-case count, sample table, commenter outcomes, silent-reader signals, confounders, contradictory evidence, conclusion strength, and exact proposed Strategy Guide wording or `No change proposed`. diff --git a/.agents/skills/dialogue-lab-strategy-review/agents/openai.yaml b/skills/dialogue-lab-strategy-review/agents/openai.yaml similarity index 100% rename from .agents/skills/dialogue-lab-strategy-review/agents/openai.yaml rename to skills/dialogue-lab-strategy-review/agents/openai.yaml diff --git a/templates/sections/core.md b/templates/sections/core.md new file mode 100644 index 0000000..31bf614 --- /dev/null +++ b/templates/sections/core.md @@ -0,0 +1,20 @@ +## Instruction enforcement + +- Treat every instruction bullet in this skill as mandatory and closure-gating + for the action it governs. +- Report the exact blocker instead of claiming completion when required + approval, canonical evidence, or committed read-back is missing. + +## Core rules + +- Use the `dialogue-lab` CLI as the only Case and Turn storage boundary; do not + use MCP for Dialogue Lab work. +- Treat repository Markdown as canonical governance, strategy, and reusable + evidence and the configured SQLite database as canonical Case and Turn state. +- Use only user-supplied public content and repository-owned canonical sources; + never derive personal data from Facebook profiles. +- Never inspect or modify `General responses`. Interact with Facebook only when + explicitly requested, and never publish autonomously. +- Before a canonical write, require approval for the exact transaction, run + `dialogue-lab check`, execute one high-level write, and require committed + read-back. diff --git a/templates/skill-sections.json b/templates/skill-sections.json new file mode 100644 index 0000000..8153098 --- /dev/null +++ b/templates/skill-sections.json @@ -0,0 +1,26 @@ +{ + "runtime_source_id": "hasbaratops", + "validation_profile": "ceratops-compatible", + "sections": { + "core": "templates/sections/core.md" + }, + "skills": { + "dialogue-lab-closeout": [ + "core" + ], + "dialogue-lab-followup": [ + "core" + ], + "dialogue-lab-intake": [ + "core" + ], + "dialogue-lab-posting": [ + "core" + ], + "dialogue-lab-strategy-review": [ + "core" + ] + }, + "maintenance_workflows": {}, + "runtime_payloads": {} +} From 70c600d1e0b8684ee45a3c5a1f0c233fb82715a9 Mon Sep 17 00:00:00 2001 From: Codex Date: Fri, 24 Jul 2026 04:55:38 +0300 Subject: [PATCH 4/5] refactor: rename project to HasbaraTops --- .codex/config.toml.example | 6 +- .github/workflows/ci.yml | 8 +- AGENTS.history.json | 4 +- AGENTS.md | 10 +-- CONTRIBUTING.md | 2 +- LICENSE | 2 +- README.md | 76 +++++++++---------- config/storage.toml | 4 +- docs/cli-payloads.md | 4 +- docs/migration-receipt.json.example | 2 +- docs/rollback.md | 2 +- docs/rollout.md | 4 +- pyproject.toml | 10 +-- scripts/install-skills.py | 23 ++++-- .../dialogue-lab-closeout/agents/openai.yaml | 4 - .../dialogue-lab-followup/agents/openai.yaml | 4 - skills/dialogue-lab-intake/agents/openai.yaml | 4 - .../dialogue-lab-posting/agents/openai.yaml | 4 - .../agents/openai.yaml | 4 - .../SKILL.md | 10 +-- .../hasbaratops-closeout/agents/openai.yaml | 4 + .../SKILL.md | 10 +-- .../hasbaratops-followup/agents/openai.yaml | 4 + .../SKILL.md | 12 +-- skills/hasbaratops-intake/agents/openai.yaml | 4 + .../SKILL.md | 8 +- skills/hasbaratops-posting/agents/openai.yaml | 4 + .../SKILL.md | 8 +- .../agents/openai.yaml | 4 + src/dialogue_lab/__init__.py | 3 - src/hasbaratops/__init__.py | 3 + .../case_identity.py | 0 src/{dialogue_lab => hasbaratops}/cli.py | 42 +++++----- src/{dialogue_lab => hasbaratops}/enums.py | 0 src/{dialogue_lab => hasbaratops}/errors.py | 16 ++-- .../facebook_url.py | 0 .../identifiers.py | 0 .../lifecycle.py | 0 .../migration_receipt.py | 10 +-- src/{dialogue_lab => hasbaratops}/models.py | 0 .../parent_graph.py | 0 src/{dialogue_lab => hasbaratops}/readback.py | 0 src/{dialogue_lab => hasbaratops}/schema.py | 0 .../source_consistency.py | 0 src/{dialogue_lab => hasbaratops}/storage.py | 2 +- .../validation.py | 40 +++++----- templates/sections/core.md | 6 +- templates/skill-sections.json | 10 +-- tests/helpers.py | 4 +- tests/test_cli_commands.py | 8 +- tests/test_facebook_url.py | 4 +- tests/test_identity_identifiers.py | 6 +- tests/test_lifecycle_graph_validation.py | 16 ++-- tests/test_migration_cli.py | 8 +- tests/test_sqlite_storage.py | 42 +++++----- uv.lock | 20 ++--- 56 files changed, 249 insertions(+), 236 deletions(-) delete mode 100644 skills/dialogue-lab-closeout/agents/openai.yaml delete mode 100644 skills/dialogue-lab-followup/agents/openai.yaml delete mode 100644 skills/dialogue-lab-intake/agents/openai.yaml delete mode 100644 skills/dialogue-lab-posting/agents/openai.yaml delete mode 100644 skills/dialogue-lab-strategy-review/agents/openai.yaml rename skills/{dialogue-lab-closeout => hasbaratops-closeout}/SKILL.md (61%) create mode 100644 skills/hasbaratops-closeout/agents/openai.yaml rename skills/{dialogue-lab-followup => hasbaratops-followup}/SKILL.md (79%) create mode 100644 skills/hasbaratops-followup/agents/openai.yaml rename skills/{dialogue-lab-intake => hasbaratops-intake}/SKILL.md (57%) create mode 100644 skills/hasbaratops-intake/agents/openai.yaml rename skills/{dialogue-lab-posting => hasbaratops-posting}/SKILL.md (57%) create mode 100644 skills/hasbaratops-posting/agents/openai.yaml rename skills/{dialogue-lab-strategy-review => hasbaratops-strategy-review}/SKILL.md (85%) create mode 100644 skills/hasbaratops-strategy-review/agents/openai.yaml delete mode 100644 src/dialogue_lab/__init__.py create mode 100644 src/hasbaratops/__init__.py rename src/{dialogue_lab => hasbaratops}/case_identity.py (100%) rename src/{dialogue_lab => hasbaratops}/cli.py (95%) rename src/{dialogue_lab => hasbaratops}/enums.py (100%) rename src/{dialogue_lab => hasbaratops}/errors.py (71%) rename src/{dialogue_lab => hasbaratops}/facebook_url.py (100%) rename src/{dialogue_lab => hasbaratops}/identifiers.py (100%) rename src/{dialogue_lab => hasbaratops}/lifecycle.py (100%) rename src/{dialogue_lab => hasbaratops}/migration_receipt.py (89%) rename src/{dialogue_lab => hasbaratops}/models.py (100%) rename src/{dialogue_lab => hasbaratops}/parent_graph.py (100%) rename src/{dialogue_lab => hasbaratops}/readback.py (100%) rename src/{dialogue_lab => hasbaratops}/schema.py (100%) rename src/{dialogue_lab => hasbaratops}/source_consistency.py (100%) rename src/{dialogue_lab => hasbaratops}/storage.py (99%) rename src/{dialogue_lab => hasbaratops}/validation.py (69%) diff --git a/.codex/config.toml.example b/.codex/config.toml.example index b1f5e2a..bfac2cd 100644 --- a/.codex/config.toml.example +++ b/.codex/config.toml.example @@ -1,7 +1,7 @@ # Safe local example. The canonical database path is supplied through the -# DIALOGUE_LAB_DB environment variable and must resolve outside the Git repo. -[dialogue_lab] +# HASBARATOPS_DB environment variable and must resolve outside the Git repo. +[hasbaratops] storage_config = "config/storage.toml" -database_environment_variable = "DIALOGUE_LAB_DB" +database_environment_variable = "HASBARATOPS_DB" writes = "explicit-approval" transactional = true diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 38d87b6..931f723 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -25,9 +25,9 @@ jobs: - run: uv run pytest - run: uv run ruff check . - run: uv run mypy - - run: uv run dialogue-lab db-init --approved + - run: uv run HasbaraTops db-init --approved env: - DIALOGUE_LAB_DB: ${{ runner.temp }}/dialogue-lab-ci.sqlite3 - - run: uv run dialogue-lab check + HASBARATOPS_DB: ${{ runner.temp }}/HasbaraTops-ci.sqlite3 + - run: uv run HasbaraTops check env: - DIALOGUE_LAB_DB: ${{ runner.temp }}/dialogue-lab-ci.sqlite3 + HASBARATOPS_DB: ${{ runner.temp }}/HasbaraTops-ci.sqlite3 diff --git a/AGENTS.history.json b/AGENTS.history.json index e333ed2..40ec4ed 100644 --- a/AGENTS.history.json +++ b/AGENTS.history.json @@ -15,10 +15,10 @@ }, { "rules": [ - "DL-REPLY-01", + "HASBARATOPS-REPLY-01", "OUT-03" ], - "decision": "Every proposed public Dialogue Lab reply is one complete, self-contained, ready-to-post response while remaining concise.", + "decision": "Every proposed public HasbaraTops reply is one complete, self-contained, ready-to-post response while remaining concise.", "reason": "Prevent staged fragments and private planning context from replacing a complete response to the supplied public exchange.", "regression": "Preserve concise one-point replies, verified facts, public-context isolation, and the ban on partial or staged output." }, diff --git a/AGENTS.md b/AGENTS.md index db35b10..610ffb1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,12 +1,12 @@ # HasbaraTops -- [HASBARA-CHECK-01] Run `dialogue-lab check` before a canonical write, +- [HASBARA-CHECK-01] Run `HasbaraTops check` before a canonical write, after a failed write or readiness check, or when database state is uncertain; ordinary read-only queries must reuse fresh sufficient evidence. - requires: HASBARA-CANONICAL-01 - [HASBARA-CANONICAL-01] Treat repository Markdown as canonical governance, strategy, and evidence content and the configured SQLite database as canonical - Case and Turn state; do not use MCP for Dialogue Lab work. + Case and Turn state; do not use MCP for HasbaraTops work. - [HASBARA-IDENTITY-01] Use `config/storage.toml`, preserve the SQLite schema version, treat Case ID as definitive, allow multiple Cases per Facebook root, and use root lookup only for candidate discovery. @@ -20,16 +20,16 @@ supplied exact URL; never substitute the Case root URL, and mark a missing link explicitly. - requires: HASBARA-CANONICAL-01 -- [HASBARA-SKILL-ROUTING-01] Invoke the matching Dialogue Lab skill for the +- [HASBARA-SKILL-ROUTING-01] Invoke the matching HasbaraTops skill for the requested case or strategy workflow. -- [DL-REPLY-01] When proposing a public Dialogue Lab reply, provide one +- [HASBARATOPS-REPLY-01] When proposing a public HasbaraTops reply, provide one complete, self-contained, ready-to-post response. - limits: OUT-03 - [HASBARA-GENERAL-RESPONSES-01] Never inspect, edit, move, rename, replace, delete, import, or summarize `General responses` without explicit instruction for that exact action. - [HASBARA-WRITE-01] Perform canonical writes only through explicitly approved - `dialogue-lab` commands using SQLite transactions and committed read-back. + `HasbaraTops` commands using SQLite transactions and committed read-back. - requires: HASBARA-CHECK-01, HASBARA-CANONICAL-01 - [HASBARA-ROLLBACK-01] After a failed canonical write, verify rollback and database integrity; block further writes until both pass. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index ee1d002..9c3240b 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,7 +10,7 @@ Before proposing a change, run: uv run pytest uv run ruff check . uv run mypy -uv run dialogue-lab --database doctor +uv run HasbaraTops --database doctor ``` Contributions are accepted only under the repository's proprietary license. diff --git a/LICENSE b/LICENSE index 4021c65..e4931c6 100644 --- a/LICENSE +++ b/LICENSE @@ -1,4 +1,4 @@ -Copyright (c) 2026 Dialogue Lab. All rights reserved. +Copyright (c) 2026 HasbaraTops. All rights reserved. This software and its documentation are proprietary. No permission is granted to use, copy, modify, distribute, sublicense, or sell any portion of this diff --git a/README.md b/README.md index 210a9d2..a612eee 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ -# Israel Facebook Dialogue Lab +# HasbaraTops -This repository is the canonical governance and execution layer for the Dialogue Lab. Repository Markdown owns governance, strategy, and reusable evidence. One SQLite database outside Git owns Case and Turn state. +This repository is the canonical governance and execution layer for HasbaraTops. Repository Markdown owns governance, strategy, and reusable evidence. One SQLite database outside Git owns Case and Turn state. The runtime reads repository Markdown and the configured SQLite database only. @@ -11,7 +11,7 @@ AGENTS.md canonical governance docs/reply-strategy-guide.md canonical cross-case strategy docs/evidence-base.md canonical reusable evidence external SQLite database canonical Cases and Turns -dialogue-lab CLI only canonical storage boundary +HasbaraTops CLI only canonical storage boundary skills/ model-driven analysis and reply workflows ``` @@ -27,36 +27,36 @@ The model owns interpretation, materially ambiguous parentage, reply drafting, f ```powershell uv sync --frozen --extra dev -$env:DIALOGUE_LAB_DB = '\dialogue-lab.sqlite3' -uv run dialogue-lab db-init --approved -uv run dialogue-lab check +$env:HASBARATOPS_DB = '\HasbaraTops.sqlite3' +uv run HasbaraTops db-init --approved +uv run HasbaraTops check ``` -`DIALOGUE_LAB_DB` must resolve outside the Git repository. `--database ` may override it and must appear before the subcommand. +`HASBARATOPS_DB` must resolve outside the Git repository. `--database ` may override it and must appear before the subcommand. Database initialization, imports, backups, and Case or Turn mutations require `--approved`. Read commands do not mutate state. ## High-level commands ```text -dialogue-lab check -dialogue-lab db-init --approved -dialogue-lab db-status -dialogue-lab db-backup --destination --approved -dialogue-lab db-import --approved -dialogue-lab db-migrate-identity --backup-destination --approved - -dialogue-lab case-find --case-id -dialogue-lab case-find --post-id --root-comment-id -dialogue-lab case-show --case-id -dialogue-lab case-split-branch --case-id --branch-root-turn-id --new-case-title --new-topic <topic> --backup-destination <outside-repo-path> --approved -dialogue-lab case-list-open -dialogue-lab strategy-dataset - -dialogue-lab case-intake <payload.json> --approved -dialogue-lab case-followup --case-id <id> <payload.json> --approved -dialogue-lab case-record-posting --case-id <id> <payload.json> --approved -dialogue-lab case-close --case-id <id> <payload.json> --approved +HasbaraTops check +HasbaraTops db-init --approved +HasbaraTops db-status +HasbaraTops db-backup --destination <outside-repo-path> --approved +HasbaraTops db-import <snapshot.json> --approved +HasbaraTops db-migrate-identity --backup-destination <outside-repo-path> --approved + +HasbaraTops case-find --case-id <Case-NNN> +HasbaraTops case-find --post-id <id> --root-comment-id <id> +HasbaraTops case-show --case-id <id> +HasbaraTops case-split-branch --case-id <id> --branch-root-turn-id <turn-id> --new-case-title <title> --new-topic <topic> --backup-destination <outside-repo-path> --approved +HasbaraTops case-list-open +HasbaraTops strategy-dataset + +HasbaraTops case-intake <payload.json> --approved +HasbaraTops case-followup --case-id <id> <payload.json> --approved +HasbaraTops case-record-posting --case-id <id> <payload.json> --approved +HasbaraTops case-close --case-id <id> <payload.json> --approved ``` The high-level write commands allocate identifiers, validate all affected records, write inside an immediate SQLite transaction, commit, reopen, and compare the committed records. Errors produce compact JSON on stderr and a nonzero exit code. @@ -75,19 +75,19 @@ A non-null `reply_comment_id` from a supplied permalink is globally unique acros | Skill | Purpose | | --- | --- | -| `dialogue-lab-intake` | Start or identify a case and prepare an approval-gated intake transaction. | -| `dialogue-lab-followup` | Process a public turn in an existing case and prepare one follow-up transaction. | -| `dialogue-lab-posting` | Record an explicitly confirmed published reply without posting to Facebook. | -| `dialogue-lab-closeout` | Close a case from observable evidence through one approved transaction. | -| `dialogue-lab-strategy-review` | Review closed-case evidence and propose strategy-guide changes. | - -## Dialogue workflows - -- Intake: `$dialogue-lab-intake` -- Follow-up: `$dialogue-lab-followup` -- Posting confirmation: `$dialogue-lab-posting` -- Closeout: `$dialogue-lab-closeout` -- Strategy review: `$dialogue-lab-strategy-review` +| `hasbaratops-intake` | Start or identify a case and prepare an approval-gated intake transaction. | +| `hasbaratops-followup` | Process a public turn in an existing case and prepare one follow-up transaction. | +| `hasbaratops-posting` | Record an explicitly confirmed published reply without posting to Facebook. | +| `hasbaratops-closeout` | Close a case from observable evidence through one approved transaction. | +| `hasbaratops-strategy-review` | Review closed-case evidence and propose strategy-guide changes. | + +## HasbaraTops workflows + +- Intake: `$hasbaratops-intake` +- Follow-up: `$hasbaratops-followup` +- Posting confirmation: `$hasbaratops-posting` +- Closeout: `$hasbaratops-closeout` +- Strategy review: `$hasbaratops-strategy-review` Skills use read commands automatically. They may pass `--approved` only after the user approves the exact canonical write. No skill publishes to Facebook. diff --git a/config/storage.toml b/config/storage.toml index 7357138..c6db84c 100644 --- a/config/storage.toml +++ b/config/storage.toml @@ -1,10 +1,10 @@ [project] -name = "Israel Facebook Dialogue Lab" +name = "HasbaraTops" timezone = "Asia/Jerusalem" [database] schema_version = 1 -path_environment_variable = "DIALOGUE_LAB_DB" +path_environment_variable = "HASBARATOPS_DB" [documents] governance = "AGENTS.md" diff --git a/docs/cli-payloads.md b/docs/cli-payloads.md index 0ca3d12..d636c33 100644 --- a/docs/cli-payloads.md +++ b/docs/cli-payloads.md @@ -95,7 +95,7 @@ A posting payload must use `direction: "Outgoing"` and `state: "Posted"`. It may ## Branch split ```text -dialogue-lab case-split-branch --case-id <id> --branch-root-turn-id <turn-id> --new-case-title <title> --new-topic <topic> --backup-destination <outside-repo-path> --approved +HasbaraTops case-split-branch --case-id <id> --branch-root-turn-id <turn-id> --new-case-title <title> --new-topic <topic> --backup-destination <outside-repo-path> --approved ``` The branch root must be a non-root Turn with another branch remaining in the source Case. The command allocates the next global Case ID, copies the shared ancestor path with fresh case-local Turn IDs, moves the selected branch and all descendants, preserves exact public text and URLs, and verifies the backup and both committed graphs. It stops when a copied shared ancestor has `reply_comment_id`, because that identifier is globally unique. @@ -125,7 +125,7 @@ Only closure fields are updated. The Case identity and public context remain unc ## Identity migration ```text -dialogue-lab db-migrate-identity --backup-destination <outside-repo-path> --approved +HasbaraTops db-migrate-identity --backup-destination <outside-repo-path> --approved ``` This command is the only supported path for renumbering an existing canonical database. It requires explicit approval, creates and verifies a non-overwriting backup, preserves schema version 1, renumbers Cases in stable creation/allocation order, updates every Turn and graph reference transactionally, and verifies the committed mapping and integrity before success. Its JSON receipt reports the backup, unchanged schema version, migrated counts, committed read-back, and integrity result. A failed migration rolls back and blocks further writes until rollback and integrity are verified. diff --git a/docs/migration-receipt.json.example b/docs/migration-receipt.json.example index cc71b43..d1fc206 100644 --- a/docs/migration-receipt.json.example +++ b/docs/migration-receipt.json.example @@ -24,6 +24,6 @@ "Stop canonical writes", "Restore the verified SQLite backup", "Use the recorded pre-migration repository commit", - "Run dialogue-lab check against the restored database" + "Run HasbaraTops check against the restored database" ] } diff --git a/docs/rollback.md b/docs/rollback.md index 58c4cfe..977c457 100644 --- a/docs/rollback.md +++ b/docs/rollback.md @@ -11,7 +11,7 @@ 1. Obtain explicit user authorization and stop canonical writes. 2. Preserve the failed database for diagnosis; do not overwrite it. 3. Restore the verified backup to a new outside-Git path. -4. Use the recorded pre-migration repository commit, point `DIALOGUE_LAB_DB` to the restored database, and run `dialogue-lab check`. +4. Use the recorded pre-migration repository commit, point `HASBARATOPS_DB` to the restored database, and run `HasbaraTops check`. 5. Verify schema version 1, integrity, Case count, Turn count, Case/Turn references, and the affected records before resuming one writer. Never copy, restore, or overwrite a canonical database without explicit approval. diff --git a/docs/rollout.md b/docs/rollout.md index 409f621..e8c760f 100644 --- a/docs/rollout.md +++ b/docs/rollout.md @@ -10,7 +10,7 @@ Review repository governance, strategy, and evidence Markdown. Keep `General res ## Stage 3 - Empty database validation -After explicit approval, initialize a database outside Git, run `dialogue-lab check`, create a verified backup, and record its schema signature and integrity result. +After explicit approval, initialize a database outside Git, run `HasbaraTops check`, create a verified backup, and record its schema signature and integrity result. ## Stage 4 - Deterministic import or identity migration @@ -26,6 +26,6 @@ Require zero duplicate identities, URL corruption incidents, graph failures, unv ## Stage 7 - Cutover -Set `DIALOGUE_LAB_DB` to the verified external database and use only high-level `dialogue-lab` commands for canonical Case and Turn operations. Keep one writer. +Set `HASBARATOPS_DB` to the verified external database and use only high-level `HasbaraTops` commands for canonical Case and Turn operations. Keep one writer. Rollback requires explicit authorization and `docs/rollback.md`. diff --git a/pyproject.toml b/pyproject.toml index 8c820ae..8be46b8 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -3,13 +3,13 @@ requires = ["hatchling==1.31.0"] build-backend = "hatchling.build" [project] -name = "israel-facebook-dialogue-lab" +name = "hasbaratops" version = "0.2.0" -description = "Deterministic local execution and SQLite storage for the Israel Facebook Dialogue Lab" +description = "Deterministic local execution and SQLite storage for HasbaraTops" readme = "README.md" requires-python = ">=3.12" license = { text = "Proprietary" } -authors = [{ name = "Dialogue Lab" }] +authors = [{ name = "HasbaraTops" }] classifiers = [ "License :: Other/Proprietary License", "Programming Language :: Python :: 3.12", @@ -26,10 +26,10 @@ dev = [ ] [project.scripts] -dialogue-lab = "dialogue_lab.cli:main" +HasbaraTops = "hasbaratops.cli:main" [tool.hatch.build.targets.wheel] -packages = ["src/dialogue_lab"] +packages = ["src/hasbaratops"] [tool.pytest.ini_options] addopts = "-q" diff --git a/scripts/install-skills.py b/scripts/install-skills.py index 8144aaf..0506667 100644 --- a/scripts/install-skills.py +++ b/scripts/install-skills.py @@ -16,7 +16,6 @@ import subprocess import sys - INSTALLER_VERSION = 2 LIFECYCLE_SKILL = "ceratops-skill-lifecycle" RESOLVER_RELATIVE = pathlib.Path("scripts/runtime/resolve-lifecycle-bundle.py") @@ -27,7 +26,11 @@ def codex_skills_root() -> pathlib.Path: """Return the personal runtime skill root used to discover the bundle.""" codex_home = os.environ.get("CODEX_HOME") - return pathlib.Path(codex_home).expanduser() / "skills" if codex_home else pathlib.Path.home() / ".codex" / "skills" + return ( + pathlib.Path(codex_home).expanduser() / "skills" + if codex_home + else pathlib.Path.home() / ".codex" / "skills" + ) def resolver_path(repo_root: pathlib.Path) -> pathlib.Path: @@ -74,9 +77,19 @@ def main() -> int: """Resolve one lifecycle bundle and run its validating installer.""" parser = argparse.ArgumentParser(description="Install managed Ceratops-compatible skills.") - parser.add_argument("--repo-root", type=pathlib.Path, help="Source repository root; defaults to this script's repository.") - parser.add_argument("--install-root", type=pathlib.Path, help="Runtime skills root; defaults to $CODEX_HOME/skills.") - parser.add_argument("--skill", action="append", help="Install only this skill; repeat for multiple skills.") + parser.add_argument( + "--repo-root", + type=pathlib.Path, + help="Source repository root; defaults to this script's repository.", + ) + parser.add_argument( + "--install-root", + type=pathlib.Path, + help="Runtime skills root; defaults to $CODEX_HOME/skills.", + ) + parser.add_argument( + "--skill", action="append", help="Install only this skill; repeat for multiple skills." + ) args = parser.parse_args() repo_root = (args.repo_root or pathlib.Path(__file__).resolve().parents[1]).resolve() diff --git a/skills/dialogue-lab-closeout/agents/openai.yaml b/skills/dialogue-lab-closeout/agents/openai.yaml deleted file mode 100644 index 178a5bc..0000000 --- a/skills/dialogue-lab-closeout/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dialogue Lab Closeout" - short_description: "Close a case from observable outcomes" - default_prompt: "Use $dialogue-lab-closeout to close this Dialogue Lab case." diff --git a/skills/dialogue-lab-followup/agents/openai.yaml b/skills/dialogue-lab-followup/agents/openai.yaml deleted file mode 100644 index 201bc3f..0000000 --- a/skills/dialogue-lab-followup/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dialogue Lab Follow-up" - short_description: "Process a new turn in an existing case" - default_prompt: "Use $dialogue-lab-followup to process this new public turn." diff --git a/skills/dialogue-lab-intake/agents/openai.yaml b/skills/dialogue-lab-intake/agents/openai.yaml deleted file mode 100644 index 13bada5..0000000 --- a/skills/dialogue-lab-intake/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dialogue Lab Intake" - short_description: "Start or identify a Dialogue Lab case" - default_prompt: "Use $dialogue-lab-intake to analyze this Facebook reply branch." diff --git a/skills/dialogue-lab-posting/agents/openai.yaml b/skills/dialogue-lab-posting/agents/openai.yaml deleted file mode 100644 index aab162a..0000000 --- a/skills/dialogue-lab-posting/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dialogue Lab Posting" - short_description: "Record an explicitly confirmed posted reply" - default_prompt: "Use $dialogue-lab-posting to record my exact published reply." diff --git a/skills/dialogue-lab-strategy-review/agents/openai.yaml b/skills/dialogue-lab-strategy-review/agents/openai.yaml deleted file mode 100644 index b57a221..0000000 --- a/skills/dialogue-lab-strategy-review/agents/openai.yaml +++ /dev/null @@ -1,4 +0,0 @@ -interface: - display_name: "Dialogue Lab Strategy Review" - short_description: "Review strategy evidence across closed cases" - default_prompt: "Use $dialogue-lab-strategy-review to review comparable closed cases." diff --git a/skills/dialogue-lab-closeout/SKILL.md b/skills/hasbaratops-closeout/SKILL.md similarity index 61% rename from skills/dialogue-lab-closeout/SKILL.md rename to skills/hasbaratops-closeout/SKILL.md index 54c9500..4fbb3de 100644 --- a/skills/dialogue-lab-closeout/SKILL.md +++ b/skills/hasbaratops-closeout/SKILL.md @@ -1,9 +1,9 @@ --- -name: dialogue-lab-closeout -description: Close an Israel Facebook Dialogue Lab case from observable evidence and record the result through one approval-gated SQLite transaction. +name: hasbaratops-closeout +description: Close a HasbaraTops case from observable evidence and record the result through one approval-gated SQLite transaction. --- -# Dialogue Lab Closeout +# HasbaraTops Closeout ## Required inputs @@ -13,10 +13,10 @@ description: Close an Israel Facebook Dialogue Lab case from observable evidence ## Workflow 1. Load the complete Case and Turn graph once with `case-show`. -2. Resolve any missing public Turn through `$dialogue-lab-followup` before closure. +2. Resolve any missing public Turn through `$hasbaratops-followup` before closure. 3. Verify privacy and record observable chronology only. Never infer persuasion from silence, deletion, blocking, a reaction, or disappearance. 4. Choose the closed status, Outcome Class, highest outcome score reached, concise Outcome Notes, What Worked, What Failed, and exactly one Next Test. -5. Prepare one closeout payload. After explicit approval, run `dialogue-lab check`; if it passes, run exactly one `dialogue-lab case-close --case-id <id> <payload> --approved` transaction and report its compact receipt. +5. Prepare one closeout payload. After explicit approval, run `HasbaraTops check`; if it passes, run exactly one `HasbaraTops case-close --case-id <id> <payload> --approved` transaction and report its compact receipt. ## Safety diff --git a/skills/hasbaratops-closeout/agents/openai.yaml b/skills/hasbaratops-closeout/agents/openai.yaml new file mode 100644 index 0000000..03b1ae3 --- /dev/null +++ b/skills/hasbaratops-closeout/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "HasbaraTops Closeout" + short_description: "Close a case from observable outcomes" + default_prompt: "Use $hasbaratops-closeout to close this HasbaraTops case." diff --git a/skills/dialogue-lab-followup/SKILL.md b/skills/hasbaratops-followup/SKILL.md similarity index 79% rename from skills/dialogue-lab-followup/SKILL.md rename to skills/hasbaratops-followup/SKILL.md index 25c2e62..1dba55f 100644 --- a/skills/dialogue-lab-followup/SKILL.md +++ b/skills/hasbaratops-followup/SKILL.md @@ -1,9 +1,9 @@ --- -name: dialogue-lab-followup -description: Process supplied public content in an existing Israel Facebook Dialogue Lab case, resolve parentage, and prepare one approval-gated SQLite follow-up transaction. +name: hasbaratops-followup +description: Process supplied public content in an existing HasbaraTops case, resolve parentage, and prepare one approval-gated SQLite follow-up transaction. --- -# Dialogue Lab Follow-up +# HasbaraTops Follow-up ## Required inputs @@ -14,12 +14,12 @@ description: Process supplied public content in an existing Israel Facebook Dial 1. Treat an explicit Case ID as definitive; otherwise use root-based `case-find` only for candidate discovery and select the Case from branch context, asking when multiple candidates remain materially ambiguous. Load the selected Case once with `case-show`. 2. Parse supplied URLs with `parse-url`, preserve each exact URL, and confirm that the selected Case contains the tracked branch. Never identify a Case from its latest reply. -3. When sibling branches already stored in one Case must be tracked independently, prepare one `case-split-branch` command with an outside-Git backup and exact new title/topic. After explicit approval, run `dialogue-lab check`; if it passes, run the split and use its committed Case/Turn mapping. +3. When sibling branches already stored in one Case must be tracked independently, prepare one `case-split-branch` command with an outside-Git backup and exact new title/topic. After explicit approval, run `HasbaraTops check`; if it passes, run the split and use its committed Case/Turn mapping. 4. Assign or reuse only `P1`, `P2`, ... or `USER`. Resolve the direct parent from visible context or user confirmation and retain Parent Confidence; ask only when ambiguity changes the reply. 5. Build one incoming Turn payload. A supplied `reply_comment_id` is its strongest duplicate identity; without one, the transaction uses Case ID + Parent Turn ID (including a null root) + Direction + Exact Text. The transaction returns the existing Turn on a duplicate or allocates a Turn ID, validates the complete parent graph, appends, commits, and reads back. 6. Read repository strategy and evidence Markdown only as needed and verify material claims. 7. Base the public reply's substantive response only on the supplied public post, target comment chain, and verified relevant facts; never treat private planning, predicted replies, or unrelated conversation context as public content. -8. Return the standard output. After explicit approval, run `dialogue-lab check`; if it passes, run exactly one `dialogue-lab case-followup --case-id <id> <payload> --approved` transaction and report its compact receipt. +8. Return the standard output. After explicit approval, run `HasbaraTops check`; if it passes, run exactly one `HasbaraTops case-followup --case-id <id> <payload> --approved` transaction and report its compact receipt. ## Output diff --git a/skills/hasbaratops-followup/agents/openai.yaml b/skills/hasbaratops-followup/agents/openai.yaml new file mode 100644 index 0000000..c01ab64 --- /dev/null +++ b/skills/hasbaratops-followup/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "HasbaraTops Follow-up" + short_description: "Process a new turn in an existing case" + default_prompt: "Use $hasbaratops-followup to process this new public turn." diff --git a/skills/dialogue-lab-intake/SKILL.md b/skills/hasbaratops-intake/SKILL.md similarity index 57% rename from skills/dialogue-lab-intake/SKILL.md rename to skills/hasbaratops-intake/SKILL.md index 127ca91..b7b0d6f 100644 --- a/skills/dialogue-lab-intake/SKILL.md +++ b/skills/hasbaratops-intake/SKILL.md @@ -1,9 +1,9 @@ --- -name: dialogue-lab-intake -description: Start or identify an Israel Facebook Dialogue Lab case from supplied public content, resolve Case-ID or root candidates, and prepare one approval-gated SQLite intake transaction. +name: hasbaratops-intake +description: Start or identify a HasbaraTops case from supplied public content, resolve Case-ID or root candidates, and prepare one approval-gated SQLite intake transaction. --- -# Dialogue Lab Intake +# HasbaraTops Intake ## Required inputs @@ -12,12 +12,12 @@ description: Start or identify an Israel Facebook Dialogue Lab case from supplie ## Workflow -1. Parse every supplied Facebook URL with `dialogue-lab parse-url`; preserve exact URLs and never infer a parent solely from `reply_comment_id`. -2. Use an explicitly supplied Case ID as definitive. Otherwise run `dialogue-lab case-find` by `Post ID + Root Comment ID`, treat every result as a candidate, and compare its Turn graph with the supplied reply branch. Hand off an identified existing Case to `$dialogue-lab-followup`; an unmatched branch may become a new Case even when the Facebook root already has candidates. +1. Parse every supplied Facebook URL with `HasbaraTops parse-url`; preserve exact URLs and never infer a parent solely from `reply_comment_id`. +2. Use an explicitly supplied Case ID as definitive. Otherwise run `HasbaraTops case-find` by `Post ID + Root Comment ID`, treat every result as a candidate, and compare its Turn graph with the supplied reply branch. Hand off an identified existing Case to `$hasbaratops-followup`; an unmatched branch may become a new Case even when the Facebook root already has candidates. 3. Extract the public context, case-local participant references, material claims, hidden assumption, hostility, evidence confidence, privacy state, and supplied URLs. Store no names or profile links. 4. Read repository strategy and evidence Markdown only as needed. Verify material current claims with authoritative sources and distinguish fact, assessment, allegation, legal status, and moral judgment. 5. Prepare one `case-intake` JSON payload containing the Case and initial public Turns. Do not persist exploratory drafts. -6. Return the standard output. After explicit approval, run `dialogue-lab check`; if it passes, run exactly one `dialogue-lab case-intake <payload> --approved` transaction and report its compact receipt. +6. Return the standard output. After explicit approval, run `HasbaraTops check`; if it passes, run exactly one `HasbaraTops case-intake <payload> --approved` transaction and report its compact receipt. ## Output diff --git a/skills/hasbaratops-intake/agents/openai.yaml b/skills/hasbaratops-intake/agents/openai.yaml new file mode 100644 index 0000000..91948aa --- /dev/null +++ b/skills/hasbaratops-intake/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "HasbaraTops Intake" + short_description: "Start or identify a HasbaraTops case" + default_prompt: "Use $hasbaratops-intake to analyze this Facebook reply branch." diff --git a/skills/dialogue-lab-posting/SKILL.md b/skills/hasbaratops-posting/SKILL.md similarity index 57% rename from skills/dialogue-lab-posting/SKILL.md rename to skills/hasbaratops-posting/SKILL.md index cbfde63..c0f2021 100644 --- a/skills/dialogue-lab-posting/SKILL.md +++ b/skills/hasbaratops-posting/SKILL.md @@ -1,9 +1,9 @@ --- -name: dialogue-lab-posting -description: Record a user's explicit confirmation that a Dialogue Lab reply was posted, using one deterministic SQLite transaction; this skill never publishes to Facebook. +name: hasbaratops-posting +description: Record a user's explicit confirmation that a HasbaraTops reply was posted, using one deterministic SQLite transaction; this skill never publishes to Facebook. --- -# Dialogue Lab Posting Confirmation +# HasbaraTops Posting Confirmation ## Required inputs @@ -16,7 +16,7 @@ description: Record a user's explicit confirmation that a Dialogue Lab reply was 1. Load the Case once with `case-show`. 2. Parse any supplied URL with `parse-url`; preserve it exactly. Use supplied `reply_comment_id` as the strongest duplicate identity, but resolve the immediate parent only from visible context or user confirmation. 3. Prepare one Outgoing Posted Turn payload containing the exact published wording. Include `draft_turn_id` only when an existing Outgoing Draft must be marked Replaced. -4. After explicit approval, run `dialogue-lab check`; if it passes, run exactly one `dialogue-lab case-record-posting --case-id <id> <payload> --approved` transaction. Without `reply_comment_id`, it deduplicates by Case ID + Parent Turn ID (including a null root) + Direction + Exact Text. It otherwise allocates the Turn ID, validates the graph and lifecycle, writes atomically, and reads back the committed state. +4. After explicit approval, run `HasbaraTops check`; if it passes, run exactly one `HasbaraTops case-record-posting --case-id <id> <payload> --approved` transaction. Without `reply_comment_id`, it deduplicates by Case ID + Parent Turn ID (including a null root) + Direction + Exact Text. It otherwise allocates the Turn ID, validates the graph and lifecycle, writes atomically, and reads back the committed state. ## Safety diff --git a/skills/hasbaratops-posting/agents/openai.yaml b/skills/hasbaratops-posting/agents/openai.yaml new file mode 100644 index 0000000..6ad8c3f --- /dev/null +++ b/skills/hasbaratops-posting/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "HasbaraTops Posting" + short_description: "Record an explicitly confirmed posted reply" + default_prompt: "Use $hasbaratops-posting to record my exact published reply." diff --git a/skills/dialogue-lab-strategy-review/SKILL.md b/skills/hasbaratops-strategy-review/SKILL.md similarity index 85% rename from skills/dialogue-lab-strategy-review/SKILL.md rename to skills/hasbaratops-strategy-review/SKILL.md index e1f13de..7ae221a 100644 --- a/skills/dialogue-lab-strategy-review/SKILL.md +++ b/skills/hasbaratops-strategy-review/SKILL.md @@ -1,9 +1,9 @@ --- -name: dialogue-lab-strategy-review -description: Review deterministic SQLite evidence across closed Israel Facebook Dialogue Lab cases and propose, but never apply, repository strategy-guide changes. +name: hasbaratops-strategy-review +description: Review deterministic SQLite evidence across closed HasbaraTops cases and propose, but never apply, repository strategy-guide changes. --- -# Dialogue Lab Strategy Review +# HasbaraTops Strategy Review ## Required inputs @@ -12,7 +12,7 @@ description: Review deterministic SQLite evidence across closed Israel Facebook ## Workflow -1. Read `docs/reply-strategy-guide.md` and load all closed Cases and Turns in one `dialogue-lab strategy-dataset` call. +1. Read `docs/reply-strategy-guide.md` and load all closed Cases and Turns in one `HasbaraTops strategy-dataset` call. 2. Reject fewer than three reasonably comparable cases. Label reviews below twenty total closed cases as preliminary. 3. Compare topic, hostility, evidence confidence, reply length, thread position, and strategy. Separate commenter outcomes from silent-reader signals. 4. Report sample sizes, contradictory evidence, missing data, and confounders. Do not infer causation from correlation or treat one case as a reusable finding. diff --git a/skills/hasbaratops-strategy-review/agents/openai.yaml b/skills/hasbaratops-strategy-review/agents/openai.yaml new file mode 100644 index 0000000..8c8f215 --- /dev/null +++ b/skills/hasbaratops-strategy-review/agents/openai.yaml @@ -0,0 +1,4 @@ +interface: + display_name: "HasbaraTops Strategy Review" + short_description: "Review strategy evidence across closed cases" + default_prompt: "Use $hasbaratops-strategy-review to review comparable closed cases." diff --git a/src/dialogue_lab/__init__.py b/src/dialogue_lab/__init__.py deleted file mode 100644 index e41be7c..0000000 --- a/src/dialogue_lab/__init__.py +++ /dev/null @@ -1,3 +0,0 @@ -"""Deterministic execution layer for the Israel Facebook Dialogue Lab.""" - -__version__ = "0.2.0" diff --git a/src/hasbaratops/__init__.py b/src/hasbaratops/__init__.py new file mode 100644 index 0000000..db1eae1 --- /dev/null +++ b/src/hasbaratops/__init__.py @@ -0,0 +1,3 @@ +"""Deterministic execution layer for HasbaraTops.""" + +__version__ = "0.2.0" diff --git a/src/dialogue_lab/case_identity.py b/src/hasbaratops/case_identity.py similarity index 100% rename from src/dialogue_lab/case_identity.py rename to src/hasbaratops/case_identity.py diff --git a/src/dialogue_lab/cli.py b/src/hasbaratops/cli.py similarity index 95% rename from src/dialogue_lab/cli.py rename to src/hasbaratops/cli.py index 0f1e1a1..83e3e40 100644 --- a/src/dialogue_lab/cli.py +++ b/src/hasbaratops/cli.py @@ -1,4 +1,4 @@ -"""Installable command-line interface for deterministic Dialogue Lab operations.""" +"""Installable command-line interface for deterministic HasbaraTops operations.""" from __future__ import annotations @@ -15,7 +15,7 @@ from . import __version__ from .enums import CaseStatus, OutcomeClass, ParentConfidence, TurnDirection, TurnKind, TurnState -from .errors import DialogueLabError, StorageError, WriteSafetyError +from .errors import HasbaraTopsError, StorageError, WriteSafetyError from .facebook_url import parse_facebook_url from .identifiers import next_case_id, next_turn_id from .lifecycle import CLOSED_STATUSES, validate_posted_turn, validate_transition @@ -37,13 +37,13 @@ def _load_json(path: str) -> object: def _mapping(value: object, label: str = "JSON document") -> Mapping[str, Any]: if not isinstance(value, dict): - raise DialogueLabError(f"{label} must be a JSON object") + raise HasbaraTopsError(f"{label} must be a JSON object") return value def _list(value: object, label: str = "JSON document") -> list[object]: if not isinstance(value, list): - raise DialogueLabError(f"{label} must be a JSON list") + raise HasbaraTopsError(f"{label} must be a JSON list") return value @@ -62,7 +62,7 @@ def _boolean(value: object, label: str) -> bool: return True if isinstance(value, str) and value.lower() in {"false", "no"}: return False - raise DialogueLabError(f"{label} must be a boolean") + raise HasbaraTopsError(f"{label} must be a boolean") def _reject_unknown( @@ -70,7 +70,7 @@ def _reject_unknown( ) -> None: unknown = sorted(set(payload) - allowed) if unknown: - raise DialogueLabError(f"unknown {label} fields: {', '.join(unknown)}") + raise HasbaraTopsError(f"unknown {label} fields: {', '.join(unknown)}") def _case_record(payload: Mapping[str, Any]) -> CaseRecord: @@ -216,11 +216,11 @@ def _add_database_write_arguments(parser: argparse.ArgumentParser) -> None: def _build_parser() -> argparse.ArgumentParser: parser = argparse.ArgumentParser( - prog="dialogue-lab", - description="Deterministic local operations for the Israel Facebook Dialogue Lab", + prog="HasbaraTops", + description="Deterministic local operations for HasbaraTops", ) parser.add_argument("--version", action="version", version=f"%(prog)s {__version__}") - parser.add_argument("--database", help="SQLite path; defaults to DIALOGUE_LAB_DB") + parser.add_argument("--database", help="SQLite path; defaults to HASBARATOPS_DB") sub = parser.add_subparsers(dest="command", required=True) sub.add_parser( @@ -333,7 +333,7 @@ def _turn_for_case( if parsed.reply_comment_id is not None: supplied = _optional_string(values.get("reply_comment_id")) if supplied is not None and supplied != parsed.reply_comment_id: - raise DialogueLabError( + raise HasbaraTopsError( "reply_comment_id conflicts with the supplied Exact URL" ) values["reply_comment_id"] = parsed.reply_comment_id @@ -370,13 +370,13 @@ def _run_database_command(args: argparse.Namespace, command: str) -> object: root_comment_id = _optional_string(args.root_comment_id) if case_id is not None: if post_id is not None or root_comment_id is not None: - raise DialogueLabError( + raise HasbaraTopsError( "case-find accepts either --case-id or a Post ID + Root Comment ID pair" ) case = store.get_case(case_id) return {"found": True, "case_id": case.case_id, "status": case.status.value} if post_id is None or root_comment_id is None: - raise DialogueLabError( + raise HasbaraTopsError( "case-find requires --case-id or both --post-id and --root-comment-id" ) candidates = store.find_cases(post_id, root_comment_id) @@ -422,13 +422,13 @@ def _run_database_command(args: argparse.Namespace, command: str) -> object: case_id = str(args.case_id) case = store.get_case(case_id) if case.status in CLOSED_STATUSES: - raise DialogueLabError(f"case is closed: {case_id}") + raise HasbaraTopsError(f"case is closed: {case_id}") payload = _mapping(_load_json(str(args.json_file)), "Turn payload") _reject_unknown(payload, {*TURN_FIELDS, "draft_turn_id"}, "Turn") turn = _turn_for_case(payload, case, "") if command == "case-followup": if turn.direction is not TurnDirection.INCOMING: - raise DialogueLabError("case-followup requires an Incoming Turn") + raise HasbaraTopsError("case-followup requires an Incoming Turn") target_status = CaseStatus.ACTIVE_EXCHANGE reason = "incoming public turn" replace_draft_id = None @@ -464,7 +464,7 @@ def _run_database_command(args: argparse.Namespace, command: str) -> object: _reject_unknown(payload, close_fields, "case close") status = CaseStatus(str(payload.get("status", ""))) if status not in CLOSED_STATUSES: - raise DialogueLabError("case-close requires a Closed status") + raise HasbaraTopsError("case-close requires a Closed status") required_text = ( "updated_at", "outcome_notes", @@ -475,7 +475,7 @@ def _run_database_command(args: argparse.Namespace, command: str) -> object: ) missing = [name for name in required_text if not str(payload.get(name, "")).strip()] if missing or payload.get("outcome_score") is None or payload.get("outcome_class") is None: - raise DialogueLabError("case-close payload lacks required outcome fields") + raise HasbaraTopsError("case-close payload lacks required outcome fields") updated = replace( case, updated_at=str(payload["updated_at"]), @@ -494,7 +494,7 @@ def _run_database_command(args: argparse.Namespace, command: str) -> object: reason=str(payload.get("reason", "explicit closeout")), approved=bool(args.approved), ) - raise DialogueLabError(f"unknown database command: {command}") + raise HasbaraTopsError(f"unknown database command: {command}") def _run(args: argparse.Namespace) -> object: @@ -502,7 +502,7 @@ def _run(args: argparse.Namespace) -> object: if command == "check": result = _check(args) if not result["ok"]: - raise DialogueLabError("local readiness checks failed") + raise HasbaraTopsError("local readiness checks failed") return result database_commands = { "db-init", @@ -557,14 +557,14 @@ def _run(args: argparse.Namespace) -> object: actual = _mapping(_load_json(str(args.actual))) verification = verify_readback(expected, actual) if not verification.succeeded: - raise DialogueLabError( + raise HasbaraTopsError( "read-back verification failed: " + "; ".join(verification.mismatches) ) return verification if command == "migration-receipt": receipt = migration_receipt_from_mapping(_mapping(_load_json(str(args.json_file)))) return json.loads(render_migration_receipt(receipt)) - raise DialogueLabError(f"unknown command: {command}") + raise HasbaraTopsError(f"unknown command: {command}") def main(argv: list[str] | None = None) -> int: @@ -573,7 +573,7 @@ def main(argv: list[str] | None = None) -> int: try: _print(_run(args)) return 0 - except (DialogueLabError, OSError, ValueError, KeyError, sqlite3.Error) as error: + except (HasbaraTopsError, OSError, ValueError, KeyError, sqlite3.Error) as error: category = ( "storage_error" if isinstance(error, sqlite3.Error) diff --git a/src/dialogue_lab/enums.py b/src/hasbaratops/enums.py similarity index 100% rename from src/dialogue_lab/enums.py rename to src/hasbaratops/enums.py diff --git a/src/dialogue_lab/errors.py b/src/hasbaratops/errors.py similarity index 71% rename from src/dialogue_lab/errors.py rename to src/hasbaratops/errors.py index 0daef14..ac28f36 100644 --- a/src/dialogue_lab/errors.py +++ b/src/hasbaratops/errors.py @@ -1,43 +1,43 @@ """Domain exceptions with stable machine-readable categories.""" -class DialogueLabError(ValueError): +class HasbaraTopsError(ValueError): """Base exception for deterministic validation failures.""" - category = "dialogue_lab_error" + category = "hasbaratops_error" -class CompatibilityError(DialogueLabError): +class CompatibilityError(HasbaraTopsError): """Raised when a canonical source is incompatible with this repository.""" category = "compatibility_error" -class IdentifierError(DialogueLabError): +class IdentifierError(HasbaraTopsError): """Raised when an identifier is malformed, duplicated, or unsafe to allocate.""" category = "identifier_error" -class LifecycleError(DialogueLabError): +class LifecycleError(HasbaraTopsError): """Raised when a lifecycle action is not allowed.""" category = "lifecycle_error" -class GraphError(DialogueLabError): +class GraphError(HasbaraTopsError): """Raised when the public-turn parent graph is invalid.""" category = "graph_error" -class WriteSafetyError(DialogueLabError): +class WriteSafetyError(HasbaraTopsError): """Raised when a requested canonical write violates safety policy.""" category = "write_safety_error" -class StorageError(DialogueLabError): +class StorageError(HasbaraTopsError): """Raised when local canonical storage is missing, incompatible, or corrupt.""" category = "storage_error" diff --git a/src/dialogue_lab/facebook_url.py b/src/hasbaratops/facebook_url.py similarity index 100% rename from src/dialogue_lab/facebook_url.py rename to src/hasbaratops/facebook_url.py diff --git a/src/dialogue_lab/identifiers.py b/src/hasbaratops/identifiers.py similarity index 100% rename from src/dialogue_lab/identifiers.py rename to src/hasbaratops/identifiers.py diff --git a/src/dialogue_lab/lifecycle.py b/src/hasbaratops/lifecycle.py similarity index 100% rename from src/dialogue_lab/lifecycle.py rename to src/hasbaratops/lifecycle.py diff --git a/src/dialogue_lab/migration_receipt.py b/src/hasbaratops/migration_receipt.py similarity index 89% rename from src/dialogue_lab/migration_receipt.py rename to src/hasbaratops/migration_receipt.py index 3952c36..d95bcaf 100644 --- a/src/dialogue_lab/migration_receipt.py +++ b/src/hasbaratops/migration_receipt.py @@ -3,7 +3,7 @@ import json from collections.abc import Mapping -from .errors import DialogueLabError +from .errors import HasbaraTopsError from .models import MigrationReceipt, to_jsonable @@ -12,15 +12,15 @@ def migration_receipt_from_mapping(payload: Mapping[str, object]) -> MigrationRe missing = sorted(required - payload.keys()) extra = sorted(payload.keys() - required) if missing or extra: - raise DialogueLabError( + raise HasbaraTopsError( f"migration receipt fields mismatch; missing={missing}, extra={extra}" ) case_id_map = payload["case_id_map"] if not isinstance(case_id_map, dict): - raise DialogueLabError("migration receipt case_id_map must be a JSON object") + raise HasbaraTopsError("migration receipt case_id_map must be a JSON object") backup_verified = payload["backup_verified"] if not isinstance(backup_verified, bool): - raise DialogueLabError("migration receipt backup_verified must be a boolean") + raise HasbaraTopsError("migration receipt backup_verified must be a boolean") return MigrationReceipt( operation=str(payload["operation"]), cutover_timestamp=str(payload["cutover_timestamp"]), @@ -49,7 +49,7 @@ def migration_receipt_from_mapping(payload: Mapping[str, object]) -> MigrationRe def _list(value: object) -> list[object]: if not isinstance(value, list): - raise DialogueLabError("migration receipt list field must be a JSON list") + raise HasbaraTopsError("migration receipt list field must be a JSON list") return value diff --git a/src/dialogue_lab/models.py b/src/hasbaratops/models.py similarity index 100% rename from src/dialogue_lab/models.py rename to src/hasbaratops/models.py diff --git a/src/dialogue_lab/parent_graph.py b/src/hasbaratops/parent_graph.py similarity index 100% rename from src/dialogue_lab/parent_graph.py rename to src/hasbaratops/parent_graph.py diff --git a/src/dialogue_lab/readback.py b/src/hasbaratops/readback.py similarity index 100% rename from src/dialogue_lab/readback.py rename to src/hasbaratops/readback.py diff --git a/src/dialogue_lab/schema.py b/src/hasbaratops/schema.py similarity index 100% rename from src/dialogue_lab/schema.py rename to src/hasbaratops/schema.py diff --git a/src/dialogue_lab/source_consistency.py b/src/hasbaratops/source_consistency.py similarity index 100% rename from src/dialogue_lab/source_consistency.py rename to src/hasbaratops/source_consistency.py diff --git a/src/dialogue_lab/storage.py b/src/hasbaratops/storage.py similarity index 99% rename from src/dialogue_lab/storage.py rename to src/hasbaratops/storage.py index 2383030..9ed5bb5 100644 --- a/src/dialogue_lab/storage.py +++ b/src/hasbaratops/storage.py @@ -1,4 +1,4 @@ -"""Transactional SQLite storage for canonical Dialogue Lab case state. +"""Transactional SQLite storage for canonical HasbaraTops case state. All mutations require an explicit approval flag, run inside an immediate transaction, and are read back after commit. The caller supplies the database diff --git a/src/dialogue_lab/validation.py b/src/hasbaratops/validation.py similarity index 69% rename from src/dialogue_lab/validation.py rename to src/hasbaratops/validation.py index bce8542..8d1bff0 100644 --- a/src/dialogue_lab/validation.py +++ b/src/hasbaratops/validation.py @@ -3,7 +3,7 @@ import re from .enums import TurnDirection, TurnState -from .errors import DialogueLabError +from .errors import HasbaraTopsError from .facebook_url import parse_facebook_url from .identifiers import TURN_ID_RE, case_id_number from .lifecycle import CLOSED_STATUSES @@ -26,57 +26,57 @@ def validate_case(record: CaseRecord) -> None: } missing = [name for name, value in required.items() if not value.strip()] if missing: - raise DialogueLabError(f"missing required Case fields: {', '.join(missing)}") + raise HasbaraTopsError(f"missing required Case fields: {', '.join(missing)}") if not record.privacy_checked: - raise DialogueLabError("Privacy Checked must be true") + raise HasbaraTopsError("Privacy Checked must be true") if record.outcome_score is not None and not 0 <= record.outcome_score <= 5: - raise DialogueLabError("Outcome Score 0-5 must be between 0 and 5") + raise HasbaraTopsError("Outcome Score 0-5 must be between 0 and 5") if record.user_rating is not None and not 1 <= record.user_rating <= 5: - raise DialogueLabError("User Rating 1-5 must be between 1 and 5") + raise HasbaraTopsError("User Rating 1-5 must be between 1 and 5") if record.status in CLOSED_STATUSES and not record.closed_at: - raise DialogueLabError("Closed At is required for closed cases") + raise HasbaraTopsError("Closed At is required for closed cases") parsed = parse_facebook_url(record.post_url) if not parsed.is_facebook_url: - raise DialogueLabError("Post URL must be a Facebook URL") + raise HasbaraTopsError("Post URL must be a Facebook URL") if parsed.post_id is not None and parsed.post_id != record.post_id: - raise DialogueLabError("Post URL Post ID conflicts with Case Post ID") + raise HasbaraTopsError("Post URL Post ID conflicts with Case Post ID") if parsed.root_comment_id is not None and parsed.root_comment_id != record.root_comment_id: - raise DialogueLabError("Post URL Root Comment ID conflicts with Case Root Comment ID") + raise HasbaraTopsError("Post URL Root Comment ID conflicts with Case Root Comment ID") if ( record.status not in CLOSED_STATUSES and parsed.root_comment_id is None and parsed.reply_comment_id is None ): - raise DialogueLabError("open Case Post URL requires a comment or reply identifier") + raise HasbaraTopsError("open Case Post URL requires a comment or reply identifier") def validate_turn(record: TurnRecord) -> None: case_id_number(record.case_id) if TURN_ID_RE.fullmatch(record.turn_id) is None: - raise DialogueLabError(f"malformed Turn ID: {record.turn_id}") + raise HasbaraTopsError(f"malformed Turn ID: {record.turn_id}") if not PARTICIPANT_RE.fullmatch(record.participant_ref): - raise DialogueLabError("Participant Ref must be USER or a case-local P-number") + raise HasbaraTopsError("Participant Ref must be USER or a case-local P-number") if not record.exact_text: - raise DialogueLabError("Exact Text is required") + raise HasbaraTopsError("Exact Text is required") if record.reply_comment_id is not None and not record.reply_comment_id.strip(): - raise DialogueLabError("reply_comment_id must not be blank") + raise HasbaraTopsError("reply_comment_id must not be blank") if not record.observed_at: - raise DialogueLabError("Observed At is required") + raise HasbaraTopsError("Observed At is required") if record.direction is TurnDirection.INCOMING and record.state is TurnState.DRAFT: - raise DialogueLabError("Incoming turns cannot be Draft") + raise HasbaraTopsError("Incoming turns cannot be Draft") if record.exact_url is not None: parsed = parse_facebook_url(record.exact_url) if not parsed.is_facebook_url: - raise DialogueLabError("Exact URL must be a Facebook URL") + raise HasbaraTopsError("Exact URL must be a Facebook URL") if parsed.post_id is not None and parsed.post_id != record.post_id: - raise DialogueLabError("Exact URL Post ID conflicts with Turn Post ID") + raise HasbaraTopsError("Exact URL Post ID conflicts with Turn Post ID") if parsed.root_comment_id is not None and parsed.root_comment_id != record.root_comment_id: - raise DialogueLabError("Exact URL Root Comment ID conflicts with Turn Root Comment ID") + raise HasbaraTopsError("Exact URL Root Comment ID conflicts with Turn Root Comment ID") if ( parsed.reply_comment_id is not None and parsed.reply_comment_id != record.reply_comment_id ): - raise DialogueLabError( + raise HasbaraTopsError( "reply_comment_id must match the supplied Exact URL reply_comment_id" ) diff --git a/templates/sections/core.md b/templates/sections/core.md index 31bf614..650c70f 100644 --- a/templates/sections/core.md +++ b/templates/sections/core.md @@ -7,8 +7,8 @@ ## Core rules -- Use the `dialogue-lab` CLI as the only Case and Turn storage boundary; do not - use MCP for Dialogue Lab work. +- Use the `HasbaraTops` CLI as the only Case and Turn storage boundary; do not + use MCP for HasbaraTops work. - Treat repository Markdown as canonical governance, strategy, and reusable evidence and the configured SQLite database as canonical Case and Turn state. - Use only user-supplied public content and repository-owned canonical sources; @@ -16,5 +16,5 @@ - Never inspect or modify `General responses`. Interact with Facebook only when explicitly requested, and never publish autonomously. - Before a canonical write, require approval for the exact transaction, run - `dialogue-lab check`, execute one high-level write, and require committed + `HasbaraTops check`, execute one high-level write, and require committed read-back. diff --git a/templates/skill-sections.json b/templates/skill-sections.json index 8153098..80d75bd 100644 --- a/templates/skill-sections.json +++ b/templates/skill-sections.json @@ -5,19 +5,19 @@ "core": "templates/sections/core.md" }, "skills": { - "dialogue-lab-closeout": [ + "hasbaratops-closeout": [ "core" ], - "dialogue-lab-followup": [ + "hasbaratops-followup": [ "core" ], - "dialogue-lab-intake": [ + "hasbaratops-intake": [ "core" ], - "dialogue-lab-posting": [ + "hasbaratops-posting": [ "core" ], - "dialogue-lab-strategy-review": [ + "hasbaratops-strategy-review": [ "core" ] }, diff --git a/tests/helpers.py b/tests/helpers.py index 8c0b556..a9a2e04 100644 --- a/tests/helpers.py +++ b/tests/helpers.py @@ -1,13 +1,13 @@ """Synthetic records shared by deterministic tests.""" -from dialogue_lab.enums import ( +from hasbaratops.enums import ( CaseStatus, ParentConfidence, TurnDirection, TurnKind, TurnState, ) -from dialogue_lab.models import CaseRecord, TurnRecord +from hasbaratops.models import CaseRecord, TurnRecord def make_case(**overrides: object) -> CaseRecord: diff --git a/tests/test_cli_commands.py b/tests/test_cli_commands.py index e29ff7d..140c5a0 100644 --- a/tests/test_cli_commands.py +++ b/tests/test_cli_commands.py @@ -3,10 +3,10 @@ import pytest -from dialogue_lab.cli import main -from dialogue_lab.enums import CaseStatus, OutcomeClass, TurnDirection, TurnState -from dialogue_lab.models import to_jsonable -from dialogue_lab.storage import SQLiteStore +from hasbaratops.cli import main +from hasbaratops.enums import CaseStatus, OutcomeClass, TurnDirection, TurnState +from hasbaratops.models import to_jsonable +from hasbaratops.storage import SQLiteStore from tests.helpers import make_case, make_reply, make_turn diff --git a/tests/test_facebook_url.py b/tests/test_facebook_url.py index b9c5b7c..f61f6c3 100644 --- a/tests/test_facebook_url.py +++ b/tests/test_facebook_url.py @@ -1,5 +1,5 @@ -from dialogue_lab.facebook_url import parse_facebook_url -from dialogue_lab.models import to_jsonable +from hasbaratops.facebook_url import parse_facebook_url +from hasbaratops.models import to_jsonable def test_numeric_reel_and_comment_parse_with_exact_url_preserved() -> None: diff --git a/tests/test_identity_identifiers.py b/tests/test_identity_identifiers.py index a0383d4..60b28b0 100644 --- a/tests/test_identity_identifiers.py +++ b/tests/test_identity_identifiers.py @@ -1,8 +1,8 @@ import pytest -from dialogue_lab.case_identity import find_case, find_case_candidates -from dialogue_lab.errors import IdentifierError -from dialogue_lab.identifiers import next_case_id, next_turn_id +from hasbaratops.case_identity import find_case, find_case_candidates +from hasbaratops.errors import IdentifierError +from hasbaratops.identifiers import next_case_id, next_turn_id from tests.helpers import make_case diff --git a/tests/test_lifecycle_graph_validation.py b/tests/test_lifecycle_graph_validation.py index 139f824..e0995b6 100644 --- a/tests/test_lifecycle_graph_validation.py +++ b/tests/test_lifecycle_graph_validation.py @@ -1,22 +1,22 @@ import pytest -from dialogue_lab.enums import ( +from hasbaratops.enums import ( CaseStatus, ParentConfidence, TurnDirection, TurnKind, TurnState, ) -from dialogue_lab.errors import DialogueLabError, GraphError, LifecycleError -from dialogue_lab.lifecycle import ( +from hasbaratops.errors import GraphError, HasbaraTopsError, LifecycleError +from hasbaratops.lifecycle import ( validate_closure_evidence, validate_draft_creation, validate_posted_turn, validate_transition, ) -from dialogue_lab.models import LifecycleTransition, to_jsonable -from dialogue_lab.parent_graph import is_branched, thread_map, validate_parent_graph -from dialogue_lab.validation import validate_turn +from hasbaratops.models import LifecycleTransition, to_jsonable +from hasbaratops.parent_graph import is_branched, thread_map, validate_parent_graph +from hasbaratops.validation import validate_turn from tests.helpers import make_reply, make_turn @@ -91,9 +91,9 @@ def test_reply_without_parent_is_rejected_even_with_reply_comment_id() -> None: def test_participant_names_and_profile_urls_are_rejected() -> None: - with pytest.raises(DialogueLabError, match="Participant Ref"): + with pytest.raises(HasbaraTopsError, match="Participant Ref"): validate_turn(make_turn(participant_ref="Synthetic Name")) - with pytest.raises(DialogueLabError, match="Participant Ref"): + with pytest.raises(HasbaraTopsError, match="Participant Ref"): validate_turn(make_turn(participant_ref="https://facebook.com/profile")) diff --git a/tests/test_migration_cli.py b/tests/test_migration_cli.py index 92d27c7..855e31a 100644 --- a/tests/test_migration_cli.py +++ b/tests/test_migration_cli.py @@ -3,9 +3,9 @@ import pytest -from dialogue_lab.cli import main -from dialogue_lab.migration_receipt import migration_receipt_from_mapping -from dialogue_lab.models import to_jsonable +from hasbaratops.cli import main +from hasbaratops.migration_receipt import migration_receipt_from_mapping +from hasbaratops.models import to_jsonable def test_migration_receipt_requires_database_evidence_and_git_commit() -> None: @@ -45,7 +45,7 @@ def test_cli_parse_url_and_version_emit_success(capsys: pytest.CaptureFixture[st def test_no_python_module_exposes_facebook_publishing_behavior() -> None: - source_root = Path(__file__).parents[1] / "src" / "dialogue_lab" + source_root = Path(__file__).parents[1] / "src" / "hasbaratops" text = "\n".join(path.read_text(encoding="utf-8") for path in source_root.glob("*.py")) assert "post_to_facebook" not in text assert "publish_to_facebook" not in text diff --git a/tests/test_sqlite_storage.py b/tests/test_sqlite_storage.py index eddaec9..8e6848b 100644 --- a/tests/test_sqlite_storage.py +++ b/tests/test_sqlite_storage.py @@ -4,17 +4,17 @@ import pytest -from dialogue_lab.enums import CaseStatus, OutcomeClass, TurnDirection, TurnState -from dialogue_lab.errors import DialogueLabError, StorageError, WriteSafetyError -from dialogue_lab.storage import _LEGACY_IDENTITY_SCHEMA_SIGNATURE, SQLiteStore -from dialogue_lab.validation import validate_case, validate_turn +from hasbaratops.enums import CaseStatus, OutcomeClass, TurnDirection, TurnState +from hasbaratops.errors import HasbaraTopsError, StorageError, WriteSafetyError +from hasbaratops.storage import _LEGACY_IDENTITY_SCHEMA_SIGNATURE, SQLiteStore +from hasbaratops.validation import validate_case, validate_turn from tests.helpers import make_case, make_reply, make_turn def test_database_initialization_requires_approval_and_reports_integrity( tmp_path: Path, ) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") with pytest.raises(WriteSafetyError, match="approval"): store.initialize(approved=False) @@ -27,7 +27,7 @@ def test_database_initialization_requires_approval_and_reports_integrity( def test_case_creation_is_atomic_allows_root_candidates_and_reads_back( tmp_path: Path, ) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case() turn = make_turn() @@ -53,7 +53,7 @@ def test_case_creation_is_atomic_allows_root_candidates_and_reads_back( def test_branch_split_allocates_case_copies_ancestors_and_verifies_backup( tmp_path: Path, ) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case(status=CaseStatus.ACTIVE_EXCHANGE) root = make_turn() @@ -127,7 +127,7 @@ def test_branch_split_allocates_case_copies_ancestors_and_verifies_backup( def test_branch_split_failure_rolls_back_and_retains_verified_backup( tmp_path: Path, monkeypatch: pytest.MonkeyPatch ) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case(status=CaseStatus.ACTIVE_EXCHANGE) turns = [ @@ -161,7 +161,7 @@ def fail_insert(_connection: sqlite3.Connection, _record: object) -> None: def test_failed_import_rolls_back_every_row(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) first = make_case(case_id="Case-001") duplicate_identity = make_case(case_id="Case-001") @@ -173,7 +173,7 @@ def test_failed_import_rolls_back_every_row(tmp_path: Path) -> None: def test_schema_version_mismatch_blocks_reads(tmp_path: Path) -> None: - path = tmp_path / "dialogue-lab.sqlite3" + path = tmp_path / "HasbaraTops.sqlite3" store = SQLiteStore(path) store.initialize(approved=True) with sqlite3.connect(path) as connection: @@ -193,7 +193,7 @@ def test_initialization_refuses_an_unrelated_database(tmp_path: Path) -> None: def test_followup_updates_status_and_verifies_committed_turn(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case() first = make_turn() @@ -219,7 +219,7 @@ def test_followup_updates_status_and_verifies_committed_turn(tmp_path: Path) -> def test_posting_retires_named_draft_in_same_transaction(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case(status=CaseStatus.ACTIVE_EXCHANGE) first = make_turn() @@ -256,7 +256,7 @@ def test_posting_retires_named_draft_in_same_transaction(tmp_path: Path) -> None def test_close_case_writes_only_validated_outcome_and_reads_back(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case(status=CaseStatus.ACTIVE_EXCHANGE) store.create_case(case, [make_turn()], approved=True) @@ -283,10 +283,10 @@ def test_open_case_returns_latest_turn_permalink_without_root_fallback( tmp_path: Path, ) -> None: invalid = make_case(post_url="https://www.facebook.com/example/posts/123") - with pytest.raises(DialogueLabError, match="comment or reply"): + with pytest.raises(HasbaraTopsError, match="comment or reply"): validate_case(invalid) - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case( post_url=( @@ -344,7 +344,7 @@ def test_open_case_returns_latest_turn_permalink_without_root_fallback( def test_backup_is_consistent_and_never_overwrites(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) store.create_case(make_case(), [], approved=True) destination = tmp_path / "backups" / "snapshot.sqlite3" @@ -358,7 +358,7 @@ def test_backup_is_consistent_and_never_overwrites(tmp_path: Path) -> None: def test_turn_duplicate_identity_uses_reply_id_then_null_parent_fallback( tmp_path: Path, ) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case() root = make_turn() @@ -458,15 +458,15 @@ def test_permalink_reply_id_cannot_be_omitted_or_conflict() -> None: "https://www.facebook.com/example/posts/123" "?comment_id=456&reply_comment_id=789" ) - with pytest.raises(DialogueLabError, match="must match"): + with pytest.raises(HasbaraTopsError, match="must match"): validate_turn(make_turn(exact_url=exact_url)) - with pytest.raises(DialogueLabError, match="must match"): + with pytest.raises(HasbaraTopsError, match="must match"): validate_turn(make_turn(exact_url=exact_url, reply_comment_id="999")) validate_turn(make_turn(exact_url=exact_url, reply_comment_id="789")) def test_posting_unchanged_draft_promotes_and_enriches_same_turn(tmp_path: Path) -> None: - store = SQLiteStore(tmp_path / "dialogue-lab.sqlite3") + store = SQLiteStore(tmp_path / "HasbaraTops.sqlite3") store.initialize(approved=True) case = make_case(status=CaseStatus.ACTIVE_EXCHANGE) root = make_turn() @@ -510,7 +510,7 @@ def test_posting_unchanged_draft_promotes_and_enriches_same_turn(tmp_path: Path) def test_status_rejects_identity_index_tampering(tmp_path: Path) -> None: - path = tmp_path / "dialogue-lab.sqlite3" + path = tmp_path / "HasbaraTops.sqlite3" store = SQLiteStore(path) store.initialize(approved=True) with sqlite3.connect(path) as connection: diff --git a/uv.lock b/uv.lock index 897a605..49adb71 100644 --- a/uv.lock +++ b/uv.lock @@ -57,16 +57,7 @@ wheels = [ ] [[package]] -name = "iniconfig" -version = "2.3.0" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, -] - -[[package]] -name = "israel-facebook-dialogue-lab" +name = "hasbaratops" version = "0.2.0" source = { editable = "." } dependencies = [ @@ -89,6 +80,15 @@ requires-dist = [ ] provides-extras = ["dev"] +[[package]] +name = "iniconfig" +version = "2.3.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/72/34/14ca021ce8e5dfedc35312d08ba8bf51fdd999c576889fc2c24cb97f4f10/iniconfig-2.3.0.tar.gz", hash = "sha256:c76315c77db068650d49c5b56314774a7804df16fee4402c1f19d6d15d8c4730", size = 20503, upload-time = "2025-10-18T21:55:43.219Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/cb/b1/3846dd7f199d53cb17f49cba7e651e9ce294d8497c8c150530ed11865bb8/iniconfig-2.3.0-py3-none-any.whl", hash = "sha256:f631c04d2c48c52b84d0d0549c99ff3859c98df65b3101406327ecc7d53fbf12", size = 7484, upload-time = "2025-10-18T21:55:41.639Z" }, +] + [[package]] name = "librt" version = "0.13.0" From a37e2b3c73fe2d71d4612c7f018f20bed25738c3 Mon Sep 17 00:00:00 2001 From: Codex <codex@local> Date: Fri, 24 Jul 2026 05:20:13 +0300 Subject: [PATCH 5/5] fix: remove unsupported rule relations --- AGENTS.md | 7 ------- 1 file changed, 7 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 610ffb1..2c6072b 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -3,23 +3,19 @@ - [HASBARA-CHECK-01] Run `HasbaraTops check` before a canonical write, after a failed write or readiness check, or when database state is uncertain; ordinary read-only queries must reuse fresh sufficient evidence. - - requires: HASBARA-CANONICAL-01 - [HASBARA-CANONICAL-01] Treat repository Markdown as canonical governance, strategy, and evidence content and the configured SQLite database as canonical Case and Turn state; do not use MCP for HasbaraTops work. - [HASBARA-IDENTITY-01] Use `config/storage.toml`, preserve the SQLite schema version, treat Case ID as definitive, allow multiple Cases per Facebook root, and use root lookup only for candidate discovery. - - requires: HASBARA-CANONICAL-01 - [HASBARA-IDENTITY-02] Deduplicate Turns by supplied `reply_comment_id`; when absent, use Case ID, Parent Turn ID, Direction, and Exact Text; never use mutable latest-reply state as identity. - - requires: HASBARA-IDENTITY-01 - limits: HASBARA-OPEN-CASES-01 - [HASBARA-OPEN-CASES-01] Present each open Case with its latest public Turn's supplied exact URL; never substitute the Case root URL, and mark a missing link explicitly. - - requires: HASBARA-CANONICAL-01 - [HASBARA-SKILL-ROUTING-01] Invoke the matching HasbaraTops skill for the requested case or strategy workflow. - [HASBARATOPS-REPLY-01] When proposing a public HasbaraTops reply, provide one @@ -30,10 +26,8 @@ for that exact action. - [HASBARA-WRITE-01] Perform canonical writes only through explicitly approved `HasbaraTops` commands using SQLite transactions and committed read-back. - - requires: HASBARA-CHECK-01, HASBARA-CANONICAL-01 - [HASBARA-ROLLBACK-01] After a failed canonical write, verify rollback and database integrity; block further writes until both pass. - - requires: HASBARA-WRITE-01 - [HASBARA-FACEBOOK-01] Interact with Facebook only when the user explicitly requests that interaction; never post autonomously. - [HASBARA-SENSITIVE-STATE-01] Never commit SQLite state, data exports or @@ -43,4 +37,3 @@ - [HASBARA-SKILLS-LIFECYCLE-01] Use the installed Ceratops skill lifecycle for skill maintenance, runtime installation, and release staging through the repository installer. - - requires: HASBARA-SKILLS-SOURCE-01