@danypops/papyrus 0.47.1 → 0.47.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/README.md +81 -54
  2. package/package.json +1 -2
package/README.md CHANGED
@@ -1,29 +1,45 @@
1
1
  # @danypops/papyrus
2
2
 
3
- The daemon, CLI, and domain services behind Papyrus's graph artifact store. Installable and runnable standalone, with no Pi dependency the Pi extension lives in the sibling `@danypops/pi-papyrus` package.
3
+ The daemon, CLI, and domain services behind Papyrus's graph artifact store: evidence-bearing Tasks, Docs, Rules, Playbooks, and Notes over one authenticated local database. Runs standalone as a plain daemon and CLI. `@danypops/pi-papyrus` projects the same daemon into native Pi tools.
4
4
 
5
- ## Storage and service
5
+ ## Contents
6
6
 
7
- ```text
8
- $XDG_DATA_HOME/papyrus/papyrus.db # durable graph
9
- $XDG_RUNTIME_DIR/papyrus/{port,token} # private daemon discovery
7
+ - [Install](#install)
8
+ - [Quick start](#quick-start)
9
+ - [Schema protocol](#schema-protocol)
10
+ - [Hierarchy and traversal](#hierarchy-and-traversal)
11
+ - [Playbooks](#playbooks)
12
+ - [Naming vs. ids](#naming-vs-ids)
13
+ - [Mutability](#mutability)
14
+ - [Idempotent lifecycle mutations](#idempotent-lifecycle-mutations)
15
+ - [Removing an artifact](#removing-an-artifact)
16
+ - [Context Mesh persistence model](#context-mesh-persistence-model)
17
+ - [Storage and service](#storage-and-service)
18
+ - [Related packages](#related-packages)
19
+
20
+ ## Install
21
+
22
+ ```bash
23
+ bun add @danypops/papyrus
10
24
  ```
11
25
 
26
+ The `papyrus` binary ships with the package.
27
+
28
+ ## Quick start
29
+
12
30
  ```bash
13
- bun src/cli.ts service install # install, enable, and start user service
14
- bun src/cli.ts service status
15
- bun src/cli.ts service restart
31
+ papyrus service install # install, enable, and start the user service
32
+ papyrus service status
33
+ papyrus service restart
16
34
 
17
- # Authenticated daemon-backed Task operations (add --json for machine output)
35
+ # Task operations (add --json for machine output)
18
36
  papyrus tasks plan
19
37
  papyrus tasks depend <task-id> <prerequisite-id>
20
38
  papyrus tasks update <task-id> --title "Revised task"
21
39
  papyrus tasks focus <task-id>
22
- papyrus tasks pause
23
- papyrus tasks unpause
24
40
  papyrus tasks complete <task-id>
25
41
 
26
- # Resolve a human project name safely before scoped operations
42
+ # Resolve a registered project name before scoped operations
27
43
  papyrus tasks projects --query lector --json
28
44
  papyrus tasks resolve-project Lector --json
29
45
 
@@ -32,79 +48,90 @@ papyrus notes capture "Review release provenance later"
32
48
  papyrus notes list --json
33
49
  ```
34
50
 
35
- For repository work, install the versioned ownership guard once (from the workspace root):
51
+ ## Schema protocol
36
52
 
37
- ```bash
38
- bun run guard:install
39
- ```
53
+ Papyrus enforces four artifact kinds, each with its own status vocabulary:
40
54
 
41
- It blocks every Papyrus push whose destination is not `DanyPops/papyrus`, including explicit fallback URLs that bypass `origin`.
42
-
43
- The daemon uses WAL, foreign keys, a bounded busy timeout, versioned migrations, periodic passive checkpoints, and periodic `PRAGMA optimize`. Keep the database on a local filesystem; SQLite WAL does not support network filesystems.
44
-
45
- Task project names are explicit registrations, never ambient-directory guesses. `tasks.projects` searches bounded registered identities; `tasks.resolve_project` requires one case-insensitive exact id, name, alias, or canonical root and fails on unknown or ambiguous references. Pass its returned `projectRoot` as `project_root` to subsequent task operations. `tasks.register_project` can rename or move an existing identity while preserving its stable id and prior name as an alias.
46
-
47
- `tasks.create` accepts an optional `idempotency_key`. Replays with the same caller, canonical project root, key, and payload return the original response without another mutation; conflicting payload reuse is rejected. Keys are retained for seven days, isolated across callers and projects, and then expire. Retry only when reusing the exact key and payload; an unkeyed create remains unsafe to replay after an ambiguous transport failure.
55
+ - `doc` knowledge: specifications, decisions, and research
56
+ - `task` — work: desired outcomes, gates, checklists, and dependencies
57
+ - `rule` governance injected into the Pi system prompt
58
+ - `playbook` — a trigger and an ordered list of steps whose validated arguments render a connected collection of deterministic Tasks plus contextual Rules and Docs
48
59
 
49
- Task lifecycle mutations (`start`, `submit`, `reject`, `retry`, `cancel`, `reopen`, `complete`, `pause`, and `unpause`) are destination-state idempotent: repeating an already-achieved transition is a successful `changed: false` no-op and creates no duplicate history. Supply an `idempotency_key` for every mutation whose response could be lost. After an unknown outcome, call `tasks.show` and `tasks.mutation_status` with the original key, then replay only that exact operation/key if needed—never invent a new key from stale state. Completed receipts are retained for seven days; concurrent duplicate completion calls share one gate run. A genuinely incompatible transition returns typed `invalid-transition` details with current/intended status, allowed actions, and recovery guidance.
60
+ Every edge endpoint resolves to a real artifact, and every edge relation is registered in `relation_names`. Relations are universal: any kind can link to any other kind.
50
61
 
51
- Task lease responses are name-first: `tasks.claim`, `tasks.heartbeat_lease`, and `tasks.lease` return the reusable artifact alias as `taskName` plus `taskTitle`, not the backend UUID. Use `taskName` for later Task operations; retain the lease token for heartbeat or release.
62
+ ## Hierarchy and traversal
52
63
 
53
- ### Context Mesh persistence model
64
+ `contains`/`part_of` express parent/child structure; `depends_on` expresses execution ordering. Dependency edges form an executable DAG: a self-dependency or cycle is rejected, fan-in waits for every prerequisite, and fan-out can expose several ready successors while active focus stays singular. Graph reads are cycle-safe and bounded by `depth`/`max_nodes` (default 4/100, ceiling 20/1,000). Executable task plans are bounded to 1,000 tasks and 10,000 relationships.
54
65
 
55
- `artifacts` is the shared graph-identity supertype, not a second copy of every application's database. `edges` references that single identity table at both endpoints, preserving foreign-key integrity for cross-domain links. Domain extension tables exist only where application invariants require indexed relational state: Task chronology/focus/scope and Discourse posts/events/session cursors/projection checkpoints. This is a class-table/table-per-type variant with explicit child-to-parent foreign keys; Papyrus does not use SQLite table inheritance or orphan-prone `(target_type, target_id)` links.
66
+ ## Playbooks
56
67
 
57
- The owning application remains the mutation authority. Discourse commits its extension rows and `context-thread`/`context-message` Doc projections atomically through `discourse.store`; generic artifact, document, lifecycle, and graph-link operations reject those owned subtypes and the `reply_to`/`discusses` relations. SQLite triggers additionally verify that each extension row references the expected Doc subtype. Domain tables are canonical for domain invariants; graph bodies and metadata are read-oriented projections committed in the same transaction.
68
+ A Playbook step is a plain prose string (a Task) or a structured object: `{kind:'doc',...}` creates a Doc, `{kind:'rule',...}` creates a Rule, `{kind:'call',...}` nests another Playbook's own run as a pipeline step. `playbooks.invoke` validates and normalizes arguments, renders placeholders in memory, validates the complete graph, then persists artifacts and edges in one transaction. Dependencies, containment, gates, checklists, and context all survive rendering. A run's Rules are active only while focus belongs to that run; Docs keep their invocation context and provenance.
58
69
 
59
- The authenticated CLI exposes the same operation for diagnostics and adapter parity:
70
+ A run result carries a stable schema: Playbook id, run id, normalized arguments, created ids grouped by kind, ready root task ids, and the bounded execution plan. An explicit run id produces deterministic artifact ids (`<run-id>-<blueprint-ref>`); a collision rolls the whole run back.
60
71
 
61
72
  ```bash
62
- papyrus discourse store read_thread --store-id team-forum \
63
- --input-json '{"forumId":"engineering","topicId":"reviews","threadId":"mesh","limit":25}' \
73
+ papyrus playbooks invoke <playbook-id> \
74
+ --arguments-json '{"project":"Papyrus"}' \
64
75
  --json
65
76
  ```
66
77
 
67
- ## Schema protocol (enforceable)
78
+ ## Naming vs. ids
68
79
 
69
- Papyrus enforces four artifact kinds:
80
+ Every agent-facing domain (tasks, docs, rules, playbooks, notes, discuss) addresses artifacts by `name` (the exact title) anywhere `id` would otherwise be required — `dependency_name`/`parent_name`/`child_name`/`root_task_name`/`depends_on_names` (tasks), `target_name` (docs link, searched across every kind), `task_name` (rules gate, discuss block/unblock), and `blocks_task_names` (discuss open). Resolution is an exact, case-insensitive, trimmed title match scoped like a plain list call; an ambiguous name's error lists the real ids, the one place disambiguation needs them. A returned result leads with name and status; id surfaces only when two artifacts in the same result share a title. `id` itself keeps working, in every tool.
70
81
 
71
- - `doc` — knowledge: specifications, decisions, and research
72
- - `task` — work: desired outcomes, gates, checklists, and dependencies
73
- - `rule` governance injected into the Pi system prompt
74
- - `playbook` — a trigger and an ordered list of steps whose validated arguments render a connected collection of deterministic Tasks plus contextual Rules and Docs
82
+ ## Mutability
83
+
84
+ Tasks, Docs, Rules, and Playbooks all support `update` (title/body/labels, at least one field required) alongside creation. Every update shares creation's own bounds (Rules keep their own tighter condition+action+body ceiling) and lands on the artifact's append-only mutation history, queryable via `graph.history`. An artifact carrying a `source:<system>` label (e.g. `source:web-spider`) is a read-only projection owned by that system; `update` is refused with a clear error, and a correction belongs in a new linked Doc until that system ships its own write-back path. Notes route every content change through their own facade.
85
+
86
+ ## Idempotent lifecycle mutations
75
87
 
76
- Each kind has an enforced status vocabulary. Every edge endpoint must exist, and every edge relation must be registered in `relation_names`. Relations are universal: any artifact kind can link to any other kind.
88
+ `tasks.create` accepts an optional `idempotency_key`, scoped to the caller and canonical project root. Replaying the same key with the same payload returns the original response; a changed payload is rejected. Keys expire after seven days.
77
89
 
78
- ### Hierarchy and traversal
90
+ Task lifecycle mutations (`start`, `submit`, `reject`, `retry`, `cancel`, `reopen`, `complete`, `pause`, `unpause`) are destination-state idempotent: repeating an already-reached transition is a `changed: false` no-op with no duplicate history. Pass `idempotency_key` on any mutation whose response might get lost; after an unclear outcome, call `tasks.show` and `tasks.mutation_status` with that same key before deciding the next action. Completed receipts are retained for seven days; concurrent duplicate completion calls share one gate run. An incompatible transition returns typed `invalid-transition` details with current/intended status, allowed actions, and recovery guidance.
79
91
 
80
- Use `contains` and `part_of` for explicit parent/child structure; use `depends_on` for execution ordering. Dependency edges form an executable DAG: self-dependencies and cycles are rejected, fan-in waits for every prerequisite, and fan-out can expose several ready successors while active focus remains singular. Graph reads are cycle-safe and bounded by `depth` and `max_nodes` (defaults: depth 4, 100 nodes; hard ceilings: depth 20, 1,000 nodes). Executable task plans are additionally bounded to 1,000 tasks and 10,000 relationships.
92
+ `tasks.claim`, `tasks.heartbeat_lease`, and `tasks.lease` return the reusable artifact alias as `taskName` plus `taskTitle`; use `taskName` for later Task operations and keep the lease token for heartbeat/release.
81
93
 
82
- ### Playbooks
94
+ Task project names are registered identities, resolved explicitly rather than guessed from a working directory. `tasks.projects` searches bounded registered identities; `tasks.resolve_project` matches one case-insensitive exact id, name, alias, or canonical root, and reports unknown or ambiguous references directly. Pass the returned `projectRoot` into subsequent task operations. `tasks.register_project` renames or moves an existing identity while keeping its stable id and folding the prior name into its aliases.
83
95
 
84
- A Playbook's steps are a plain prose string (a Task), or a structured object: `{kind:'doc',...}` creates a Doc, `{kind:'rule',...}` creates a Rule, `{kind:'call',...}` nests another Playbook's own run as a pipeline step. `playbooks.invoke` validates and normalizes all arguments, safely renders placeholders in memory, validates the complete graph, then persists artifacts and edges in one transaction. Task dependencies, containment, gates, checklists, and context survive rendering. Run Rules are injected only while active focus belongs to that run. Docs retain invocation context and provenance; missing evidence references remain unknown and no gate runs during instantiation.
96
+ ## Removing an artifact
85
97
 
86
- A run result has a stable schema: Playbook ID, run ID, normalized arguments, created IDs grouped by kind, ready root task IDs, and the bounded execution plan. Explicit run IDs produce deterministic artifact IDs (`<run-id>-<blueprint-ref>`); collisions roll back the entire run.
98
+ An artifact gets a permanent, immutable `created` row in the mutation event log the moment it exists, so removal is a time-gated trash entry. `remove` (the shared `artifact.remove`/`artifact.remove_subtree` operations every domain routes through, or `papyrus artifact remove <id> [--reason <text>]`) moves an artifact to the trash: excluded from every list/query immediately, still reachable directly by id, and recoverable via `restore` for 30 days. `remove` on a Task currently holding live Focus in any scope is refused.
99
+
100
+ Past the 30-day deadline, the daemon's periodic sweep performs a real, cascading delete — the one deliberate exception to Papyrus's append-only history, enforced by a database trigger checked at delete time.
101
+
102
+ ## Context Mesh persistence model
103
+
104
+ `artifacts` is the shared graph-identity supertype; `edges` references that single identity table at both endpoints, keeping foreign-key integrity across domains. Domain extension tables exist only where application invariants need indexed relational state — Task chronology/focus/scope, Discourse posts/events/session cursors/projection checkpoints — a class-table/table-per-type layout with explicit child-to-parent foreign keys.
105
+
106
+ The owning application stays the mutation authority for its own extension rows: Discourse commits its rows and `context-thread`/`context-message` Doc projections atomically through `discourse.store`, while generic artifact/document/lifecycle/graph-link operations reject those owned subtypes and the `reply_to`/`discusses` relations. SQLite triggers verify each extension row references its expected Doc subtype. Domain tables stay canonical for domain invariants; graph bodies and metadata are read-oriented projections committed in the same transaction.
87
107
 
88
108
  ```bash
89
- papyrus playbooks invoke <playbook-id> \
90
- --arguments-json '{"project":"Papyrus"}' \
109
+ papyrus discourse store read_thread --store-id team-forum \
110
+ --input-json '{"forumId":"engineering","topicId":"reviews","threadId":"mesh","limit":25}' \
91
111
  --json
92
112
  ```
93
113
 
94
- ### Removing an artifact
114
+ ## Storage and service
95
115
 
96
- Artifacts are never hard-deleted on request: every artifact gets a permanent, immutable `created` row in the mutation event log the moment it exists, so removal is a real, time-gated trash rather than a status flip. `remove` (the shared `artifact.remove`/`artifact.remove_subtree` operations every agent-facing domain routes through, or `papyrus artifact remove <id> [--reason <text>]`) moves an artifact to the trash: it is immediately excluded from every list/query, still directly reachable by id, and fully recoverable via `restore` until its purge deadline (30 days later) passes. `remove` refuses a Task that is the live Task Focus in any scope.
116
+ ```text
117
+ $XDG_DATA_HOME/papyrus/papyrus.db # durable graph
118
+ $XDG_RUNTIME_DIR/papyrus/{port,token} # private daemon discovery
119
+ ```
120
+
121
+ The daemon runs SQLite with WAL, foreign keys, a bounded busy timeout, versioned migrations, periodic passive checkpoints, and periodic `PRAGMA optimize`. Keep the database on a local filesystem — WAL depends on real local file locking.
97
122
 
98
- Once the deadline passes, the daemon's periodic sweep performs a real, cascading, irreversible deletion the one deliberate, narrow exception to Papyrus's otherwise-absolute append-only history, enforced by the database itself (not merely application code) via a trigger condition checked at delete time.
123
+ Application services depend on the `ArtifactStore` and `GateRunner` ports; SQLite and subprocess execution are adapters the daemon composes, so task behavior is unit-tested against fakes without a database. Task visualization projects the same `TaskGraph` into semantic display graphs through a `GraphRenderer` port the Pi adapter in `@danypops/pi-papyrus` renders terminal Unicode via `beautiful-mermaid` behind that port, so the task domain carries no Mermaid syntax.
99
124
 
100
- ### Naming vs. ids
125
+ For repository work, install the versioned ownership guard from the workspace root:
101
126
 
102
- Every agent domain tool (tasks, docs, rules, playbooks, notes, discuss) addresses its artifacts by `name` (the exact title) wherever `id` would otherwise be required -- `dependency_name`/`parent_name`/`child_name`/`root_task_name`/`depends_on_names` (tasks), `target_name` (docs link, searches every kind since a link target can be any of them), `task_name` (rules gate, discuss block/unblock), and `blocks_task_names` (discuss open) are the name-based equivalents of their `*_id` counterparts. Resolution is an exact, case-insensitive, trimmed title match scoped like a plain list call; an unmatched or ambiguous name fails with a clear error (ambiguous names list the real ids, since that's the one point disambiguation genuinely needs them). Results returned to the agent likewise lead with name and status, never id, unless two artifacts in the same result share a title -- id is a backend implementation detail, not a conversational handle. `id` itself still works exactly as before for every action, in every tool.
127
+ ```bash
128
+ bun run guard:install
129
+ ```
103
130
 
104
- ### Mutability
131
+ It checks a push's destination against `DanyPops/papyrus`, including an explicit fallback URL, so a push bound for the wrong remote fails locally before it reaches GitHub.
105
132
 
106
- Tasks, Docs, Rules, and Playbooks all support first-class `update` (title/body/labels, at least one required) alongside creation -- a Doc's body is no longer immutable once created. Every update is bounded the same way creation is (Rules keep their own stricter combined condition+action+body ceiling; Docs/Playbooks share Tasks' own length bounds) and recorded on the artifact's append-only mutation history, queryable via `graph.history`. An artifact carrying a `source:<system>` label (e.g. `source:web-spider` on an ingested page) is a read-only projection from a system Papyrus doesn't own the source of; updating one is refused with a clear error rather than silently forking it -- capture a correction as a new linked Doc instead until a write-back capability to that system exists. Notes stay behind their own facade for any content change, same as every other Notes mutation.
133
+ `src/index.ts` is this package's public surface for `@danypops/pi-papyrus` and any other consumer: explicit, named exports curated for outside use.
107
134
 
108
- Internally, application services depend on the `ArtifactStore` and `GateRunner` ports. SQLite and subprocess execution are adapters composed only by the daemon; task behavior is unit-tested against fakes without a database. Task visualization projects the same `TaskGraph` into semantic display graphs and sends them through a `GraphRenderer` port -- the Pi adapter (in `@danypops/pi-papyrus`) uses `beautiful-mermaid` for terminal Unicode output without leaking Mermaid syntax into the task domain.
135
+ ## Related packages
109
136
 
110
- `src/index.ts` is this package's public surface for `@danypops/pi-papyrus` (and any other real npm consumer): explicit, named exports only, not a blanket re-export of internal daemon plumbing.
137
+ - **[`@danypops/pi-papyrus`](https://www.npmjs.com/package/@danypops/pi-papyrus)** the Pi extension: native tools, TUI panels, and context injection over this daemon's authenticated loopback connection.
package/package.json CHANGED
@@ -1,9 +1,8 @@
1
1
  {
2
2
  "name": "@danypops/papyrus",
3
- "version": "0.47.1",
3
+ "version": "0.47.2",
4
4
  "description": "Daemon-backed graph artifacts, evidence-bearing tasks, rules, skills, and native TUI workflows for Pi",
5
5
  "type": "module",
6
- "keywords": ["pi-package"],
7
6
  "main": "./src/index.ts",
8
7
  "types": "./src/index.ts",
9
8
  "bin": {