@deniscodp/skills 0.0.0-stage → 1.0.0

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 (42) hide show
  1. package/LICENSE +21 -0
  2. package/README.md +89 -2
  3. package/bin/deniscodp-skills.mjs +4 -0
  4. package/package.json +23 -4
  5. package/scripts/catalog.mjs +68 -0
  6. package/scripts/cli.mjs +264 -0
  7. package/scripts/install.mjs +184 -0
  8. package/scripts/recommended.mjs +102 -0
  9. package/skills/denisco-handoff-implementation/SKILL.md +254 -0
  10. package/skills/denisco-handoff-implementation/references/blocker-checklist.md +71 -0
  11. package/skills/denisco-handoff-implementation/references/delegated-implementation.md +168 -0
  12. package/skills/denisco-handoff-implementation/references/ownership.md +105 -0
  13. package/skills/denisco-handoff-implementation/references/session-plan.md +195 -0
  14. package/skills/denisco-handoff-implementation/scripts/ownership.mjs +153 -0
  15. package/skills/denisco-implementation-coordinator/SKILL.md +237 -0
  16. package/skills/denisco-implementation-coordinator/agents/openai.yaml +2 -0
  17. package/skills/denisco-implementation-coordinator/references/documents.md +175 -0
  18. package/skills/denisco-implementation-coordinator/references/runtimes.md +105 -0
  19. package/skills/denisco-interactive-spec-definition/SKILL.md +126 -0
  20. package/skills/denisco-interactive-spec-definition/references/blocker-checklist.md +71 -0
  21. package/skills/denisco-interactive-spec-definition/references/handoff-template.md +124 -0
  22. package/skills/denisco-project-context/SKILL.md +60 -0
  23. package/skills/denisco-project-context/references/project-documents.md +70 -0
  24. package/skills/denisco-visual-check/SKILL.md +136 -0
  25. package/skills/denisco-visual-check/references/judge-prompt.md +55 -0
  26. package/skills/denisco-visual-check/references/project-config.md +118 -0
  27. package/skills/denisco-workflow-benchmark/SKILL.md +113 -0
  28. package/skills/denisco-workflow-benchmark/agents/openai.yaml +2 -0
  29. package/skills/denisco-workflow-benchmark/references/measurement.md +166 -0
  30. package/skills/denisco-workflow-benchmark/references/workflow-contract.md +371 -0
  31. package/skills/denisco-workflow-benchmark/scripts/bench.mjs +156 -0
  32. package/skills/denisco-workflow-benchmark/scripts/cases.mjs +46 -0
  33. package/skills/denisco-workflow-benchmark/scripts/configuration.mjs +71 -0
  34. package/skills/denisco-workflow-benchmark/scripts/discover.mjs +88 -0
  35. package/skills/denisco-workflow-benchmark/scripts/eval-worker.mjs +58 -0
  36. package/skills/denisco-workflow-benchmark/scripts/io.mjs +82 -0
  37. package/skills/denisco-workflow-benchmark/scripts/prepare.mjs +69 -0
  38. package/skills/denisco-workflow-benchmark/scripts/runs.mjs +235 -0
  39. package/skills/denisco-workflow-benchmark/scripts/state-prompt.mjs +96 -0
  40. package/skills/denisco-workflow-benchmark/scripts/tracking.mjs +91 -0
  41. package/skills/denisco-workflow-benchmark/scripts/usage.mjs +175 -0
  42. package/skills/denisco-workflow-benchmark/scripts/workflow.mjs +334 -0
@@ -0,0 +1,371 @@
1
+ # Persistent task measurement and closure
2
+
3
+ This contract applies to real tasks, not only isolated prepared cases. Use Node 20+ and the
4
+ installed skill's `scripts/bench.mjs`. No command launches a model. Commands with explicit write
5
+ targets require write authorization (including existing task authorization); Plan Mode may
6
+ read/check existing files, not bind, save/clear prompts or update records.
7
+
8
+ ## Ownership and discovery
9
+
10
+ `<handoff-path>.workflow.json` is the durable entry point, discovered from the handoff alone.
11
+ denisco-handoff-implementation owns the planned block map; denisco-workflow-benchmark owns binding and measured
12
+ evidence in run.json. Keep explanatory progress in PLAN.md/STATE.md. The sidecar contains no
13
+ criterion outcomes, token counters or duplicate evidence history.
14
+
15
+ This ownership describes data responsibilities, not permission for concurrent writers. During
16
+ handoff planning/implementation, use denisco-handoff-implementation's worktree claim before any mutation,
17
+ including setup, `track`, `open`, `collect`, `report` and `close`. Native conversation keys and
18
+ registration tickets are accounting identities; two live processes may share them. They cannot
19
+ be used to bypass an execution claim. The implementation owner alone updates the run and sidecar; delegated
20
+ visual children return evidence for that principal to register. A conflicting instance uses
21
+ read-only inspection/checkpoint until an explicit ownership transfer, not a filesystem idle timer.
22
+ These CLI commands do not themselves intercept arbitrary writes or enforce the worktree claim;
23
+ the owning skill requires the guard and serializes their invocation.
24
+
25
+ The sidecar has `schemaVersion: 1`, project `taskId`, document paths `handoff`, `plan`, `state`,
26
+ `measurement`, and `blocks`. Document paths resolve relative to the sidecar. Each block has
27
+ `id`, nonempty `points` and `criteria` ID arrays, and a `dependsOn` block ID array (possibly empty).
28
+ IDs are unique within each array; dependency cycles are invalid. The complete map must cover
29
+ every required run criterion, including those of future/blocked blocks. A criterion can be shared
30
+ by blocks when the prepared plan requires it. Compare assignments against the handoff manually;
31
+ the script cannot detect a requirement that was never supplied or was mapped to the wrong block.
32
+
33
+ Measurement is `{ "status": "unknown" }`, `{ "status": "disabled" }`, or the enabled binding
34
+ generated below. Measurement is disabled by default and requires an explicit activation in the
35
+ current user request or applicable project instructions. Installation, criteria, an old register or
36
+ unknown status are not activation. Disabled is authoritative across later sessions. Unknown is
37
+ inert legacy/interrupted state and must not trigger setup without explicit activation evidence.
38
+ Never search unrelated project logs or registers.
39
+
40
+ ## Agent-managed setup and first handoff
41
+
42
+ Only after explicit activation and once the handoff's criteria are agreed, reuse the bound or
43
+ explicitly referenced run. If none is referenced, follow the project's documented register location;
44
+ otherwise use `measurement/run.json`
45
+ alongside HANDOFF. The agent writes `measurement-criteria.json` alongside HANDOFF from the actual
46
+ agreed IDs/expectations, then runs `init --out <measurement-dir> --task <task-id> --project
47
+ <actual-project-root> --variant candidate --criteria <criteria-file>` and `bind`. Do not use dummy
48
+ criteria to start counting sooner. Inspect existing destinations; reuse a matching existing run
49
+ after checking project, task and the full criterion contract, or report a conflict without overwriting.
50
+ Bind preserves explicitly disabled measurement and does not itself change the default preference.
51
+
52
+ When measurement is explicitly enabled, run `track` before ending specification, even though
53
+ PLAN/STATE and blocks do not exist yet.
54
+ It can import the current conversation's earlier specification work from the same log. Mixed-task
55
+ histories require actual time bounds; a marker proves identity, not task scope. Earlier separate
56
+ conversations require independently evidenced attribution. Report gaps instead of assigning today's
57
+ ID to them. A specification abandoned before criteria/setup can remain unmeasured; do not hide that gap.
58
+
59
+ Save and check the planning prompt with `planning-prompt --handoff FILE --save` then `--check`.
60
+ Without flags it only prints. The helper validates the draft sidecar and binding, allows missing
61
+ PLAN/STATE and empty blocks, and appends its managed section to HANDOFF using the same protected
62
+ writer as STATE prompts. Existing handoff text is preserved. Mutually exclusive flags and invalid
63
+ markers are rejected; a missing/stale prompt check exits 2. It does not create a plan, approve one
64
+ or select a block. The response links the saved section and supplies its exact text. Without a
65
+ helper the specification skill saves/rereads the equivalent manually; Plan Mode leaves writes pending.
66
+
67
+ At first planning entry register that conversation through track, preserve existing decisions,
68
+ create/reconcile PLAN and STATE, and populate the complete planned block map. Resolve unresolved
69
+ blockers, select the first dependency-ready block and save/check its execution prompt before
70
+ asking the start question. If every block waits on a user action or a third party, record that in
71
+ STATE and stop without application edits.
72
+
73
+ Specification delivers HANDOFF, its sidecar and planning prompt, then asks whether to start
74
+ planning. Planning delivers PLAN/STATE, the block map and first execution prompt, then asks whether
75
+ to start implementation. Only the user's yes to that question, or a later submitted prompt or
76
+ explicitly scoped request, moves to the next phase in the same conversation; record the instruction
77
+ and scope without asking for a redundant approval. In Plan Mode propose that phase's deliverables;
78
+ once writing is permitted, materialize them and ask. Native plan approval, automatic mode and bare
79
+ transition text do not advance phases. Implementation then continues block by block until
80
+ denisco-handoff-implementation's real stop. A prompt found in a file is not a user request. Helpers prepare
81
+ and check artifacts; they never launch model sessions.
82
+
83
+ ## Agent-managed session registration
84
+
85
+ ```text
86
+ track --handoff FILE --provider codex|claude --phase PHASE [--root LOG_DIR]
87
+ [--from ISO --to ISO --parent KEY --parent-evidence TEXT --configuration ID|--skills-root DIR]
88
+ track --ticket FILE
89
+ ```
90
+
91
+ The first call writes an internal ticket under the bound run's `registrations/` and records a
92
+ pending attempt in the run. It returns a generated marker and `nextCall` as an executable plus
93
+ argument array. The agent executes those arguments in a **separate tool call in the same
94
+ conversation**, quoting for its shell. This lets the first output reach the native transcript.
95
+ It also snapshots the provider's installed skill root (`<project>/.claude/skills` or
96
+ `<project>/.agents/skills`) when present and associates that snapshot with the new session.
97
+ No user-supplied session ID, log path or nonce is necessary. Never reuse a ticket in another
98
+ conversation or put it into a continuation prompt. Tickets contain local metadata, not messages.
99
+
100
+ The second call uses the marker and exact project to resolve one log and register/import it.
101
+ No/ambiguous matches return `pending` with a reason; retry once later in the same conversation,
102
+ then continue independent work with an explicit gap. Reports include pending attempts. A success
103
+ returns `sessionKey` and an internal ticket handle for checkpoint and close. Repeated calls preserve one session record and its
104
+ original phase; source, bounds, parent or supplied configuration conflicts stay pending. A child
105
+ requires the actual parent and evidence; do not guess it. The flags are internal agent operations.
106
+
107
+ The helper never confirms coverage or user acceptance. The agent reviews attribution, child logs
108
+ and configuration, records evidence, refreshes usage and generates the report. `collect` refreshes
109
+ registered logs only. An unchanged hash does not prove the conversation ended. After the final
110
+ response the last log may change; refresh at the next entry or from an observer with access to the
111
+ closed log, retaining a pending final-accounting note until then. No model is launched to do this.
112
+ Low-level discover/whoami/session remain available for evidence-based recovery; their IDs are not
113
+ part of the user's normal workflow.
114
+
115
+ ## Bind an existing register
116
+
117
+ ```text
118
+ bind --run <absolute-run.json> --handoff <file> [--task <project-task-id> --plan <file> --state <file>]
119
+ ```
120
+
121
+ Creates the sidecar if absent, with an empty block map and default adjacent PLAN.md/STATE.md.
122
+ When the sidecar exists, preserves its paths, project task ID and planned blocks. Optional path
123
+ arguments resolve from the command working directory, and conflicting arguments are rejected.
124
+ The command writes only the sidecar, never the handoff, plan, state or run. Bind can precede planning;
125
+ checkpoint/next-prompt require the actual documents and complete map.
126
+
127
+ The generated `measurement` stores `status: enabled`, absolute `run`, its `runId`, benchmark
128
+ `taskId`, and `criteriaHash` (canonical ID/expectation/required contract). The project task ID may
129
+ differ. Handoff must reside within run.project; worktree and main checkout are not interchangeable.
130
+ The agent selects the run from the task's binding or documented location, checking identity and criteria.
131
+ Binding is idempotent. A different run, changed criteria, explicitly disabled measurement or
132
+ conflicting existing paths stop before writes. Do not manually bypass conflicts by deleting the
133
+ sidecar; reconcile moved paths or approved scope changes with the user and preserve the history.
134
+
135
+ ## Open and close a working session
136
+
137
+ ```text
138
+ open --handoff <file> --block B2 [--phase implementation] [--nonce TOKEN]
139
+ close --handoff <file> --block B1 --implementation complete (--next-block B2 | --final)
140
+ [--ticket TICKET | --session-key provider:id --session-state closed --claim verified]
141
+ ```
142
+
143
+ After setup and registration, `open` inspects an assigned block; `close` saves the continuation,
144
+ checkpoints and refreshes usage. They require existing documents and a complete planned map.
145
+ When the current conversation has a registered ticket, use `close --ticket` so the checkpoint
146
+ automatically reuses its session key. A ticket is valid only in the conversation that created it;
147
+ never transfer it to a later prompt.
148
+ The agent also generates report at closure. Usage is refreshed again at the next entry or by an
149
+ observer after the last response. A new conversation alone does not attest the old one's closure.
150
+
151
+ `open` retains low-level identity hints for compatibility. Prefer `track` to resolve and register
152
+ together. An unresolved identity stays pending; it is never replaced by the most recent log.
153
+
154
+ ## Check a block without changing anything
155
+
156
+ ```text
157
+ checkpoint --handoff <file> --block B1 --implementation complete
158
+ [--session-key provider:id --session-state active|closed|unknown --claim verified --next-block B2]
159
+ ```
160
+
161
+ Implementation is an explicit operator/agent declaration: `complete`, `in-progress`, or
162
+ `not-started`. It is not inferred from tests. The computed point status is respectively
163
+ `verified` only with all required passing, intact, fresh evidence; otherwise
164
+ `implemented-unverified`; or `in-progress`/`planned` for the latter two declarations.
165
+ Underlying verification retains failed/blocked/reopened/invalidated distinctions.
166
+ At least one required criterion is needed for automatic verified status; optional-only blocks
167
+ remain unverified. Missing criteria, artifacts, hashes or evidence after reopening never pass.
168
+
169
+ Optional `--claim` accepts planned, in-progress, implemented-unverified or verified. A conflicting
170
+ claim gives exit code 2 plus the calculated status. Without a claim, code 0 means the inspection
171
+ succeeded, not that the block passed. Invalid binding/map/links produce code 1.
172
+ With `--next-block B2`, it also performs the saved-prompt check below and returns `continuation`
173
+ with `valid` and `status`. A missing, stale or altered prompt gives code 2, even if the current
174
+ block is technically verified; technical `status`, `verification` and `claimMatches` are unchanged.
175
+ Omitting `--next-block` preserves the existing read-only behavior and does not check prompt delivery.
176
+
177
+ The output separates technical verification from session registration, log freshness and coverage.
178
+ Missing identity remains pending. `--session-state closed` is a declaration, not process detection
179
+ or coverage confirmation. Active/unknown sessions are provisional. A changed log needs collect;
180
+ unavailable logs and parser warnings remain explicit. Registering the session does not prove
181
+ subagent completeness. Checkpoint does not register/import anything automatically.
182
+
183
+ The command checks the entire map, then only the assigned block's verification. Future unfinished
184
+ blocks do not invalidate an otherwise verified B1. It shows dependency verification but does not
185
+ decide dependency readiness: review which missing checks actually prevent the next work.
186
+ Reconcile STATE.md and the final response with the calculated result. No arbitrary Markdown parser
187
+ claims to detect phrases like "verified except A5"; the agent must use the computed status.
188
+ Evidence hashes establish integrity, not semantic sufficiency. In ordinary projects, reopen and
189
+ reverify after code changes; this version does not fingerprint all business source automatically.
190
+
191
+ With disabled/unknown measurement, structural checks and prompt generation remain available,
192
+ but acceptance verification reports `not-checked` and cannot yield automatic verified status.
193
+
194
+ ## Generate the next assigned prompt
195
+
196
+ ```text
197
+ next-prompt --handoff <file> --block B2 [--save|--check]
198
+ next-prompt --handoff <file> --clear
199
+ ```
200
+
201
+ Without an option it prints a prompt only. No form chooses a block or launches a session.
202
+ Prepare the first execution prompt during planning, before execution is authorized. When the user
203
+ submits it as the next request, it continues the task from the named block. Generating or reading
204
+ a saved prompt does not authorize anything. Respect newer user restrictions; the helper does not
205
+ infer approval from arbitrary prose or intercept application-writing tools.
206
+ Requires a block from the planned map and existing documents. The prompt is short: absolute handoff,
207
+ state, plan and sidecar paths, the project task ID, the block's points, criteria and dependencies,
208
+ and the measurement status (with run path, both task IDs and accounting instructions only when
209
+ enabled). Task-specific constraints, blockers and Your actions stay in STATE outside the generated
210
+ section; the prompt tells the next session to read STATE first. Worktree ownership, evidence
211
+ reconciliation and authorization rules come from denisco-handoff-implementation itself; an inherited claim
212
+ token is never valid for a newly resumed process.
213
+ Unknown/disabled measurement is explicitly represented. Never use old external PROMPTS.md splits
214
+ to override the current PLAN.md and map.
215
+
216
+ `--save` writes and rereads the resolved STATE file, returning JSON with `state`, `block`, `changed`,
217
+ `valid`, `status: saved`, and the verified `prompt` (LF-normalized text). `--check` never writes:
218
+ it returns `valid` and `status: saved|missing|stale-or-modified`; a failed check gives exit code 2.
219
+ The prompt is returned only on a successful check. Both compare the actual saved section with
220
+ the current generator and structural context, including the complete block map and measurement
221
+ binding. Updating usage/results alone does not invalidate the prompt. Structural changes or a
222
+ changed generator require another explicit save. After a skill update, regenerate/check saved
223
+ planning prompts and prepared execution prompts with the installed helper; preserve surrounding
224
+ notes and approval evidence. This is not a semantic check of notes or PLAN prose.
225
+
226
+ The single managed section runs from `<!-- workflow:next-prompt:start -->` to
227
+ `<!-- workflow:next-prompt:end -->`, with a heading, a context fingerprint comment and a fenced
228
+ copyable prompt. Add it on first save, replace it on later saves. All text outside it is preserved,
229
+ including history; separation newlines may be added on first insertion. The writer preserves
230
+ UTF-8 (with/without BOM), BOM-marked UTF-16 LE/BE, and existing outside line endings; generated
231
+ text uses CRLF when present, otherwise LF. Invalid encoded text is rejected rather than repaired.
232
+ Repeated identical saves do not rewrite the file. Duplicate, partial or misordered markers are
233
+ errors (code 1), never a reason to overwrite the whole document. Repair malformed sections first.
234
+ Legacy unmarked prose is preserved; the agent reconciles obsolete manual prompts outside the section.
235
+
236
+ `--clear` requires no block and rejects `--block`. It removes only the managed section; absent
237
+ sections are an idempotent success. It returns `status: cleared`, `block: null`, `prompt: null`,
238
+ `valid: true` and `changed`. It declares no implementation/acceptance outcome. The three flags are
239
+ mutually exclusive. Saves/clears use a STATE lock, recheck source bytes/context, atomically replace
240
+ through a temporary file and reread the result. A live lock or detected concurrent change stops
241
+ the operation; inspect stale locks manually. Uncooperative writers cannot be prevented from editing
242
+ after the check, so use one writer and keep the closing check last.
243
+
244
+ At each block's end: finish validation, register evidence, reconcile STATE and run checkpoint for
245
+ that block. When the working session reaches a real stop: save/check the next assignment, then
246
+ checkpoint the last block worked with --next-block. Use the same block for unfinished implementation
247
+ or verification; its pending blockers live in STATE. Do not omit the continuation merely because no
248
+ new block exists or its future launch has not yet been authorized. The working programmer never
249
+ launches the saved continuation itself. An explicitly authorized denisco-implementation-coordinator may dispatch
250
+ the next bounded assignment under the recorded user scope; a worker never launches its successor.
251
+ Clear it only when no task work remains, or the user ends the task or asks for no prompt, retaining
252
+ acceptance and accounting gaps; a user stop with work remaining keeps it. After later code/evidence changes, refresh results and checkpoint again.
253
+ Disabled/unknown measurement supports these structural operations without creating a run.
254
+ In Plan Mode propose artifacts and leave writes pending. Without the helper save/reread manually.
255
+ The helper cannot force its invocation or prove that a user submitted a saved prompt.
256
+
257
+ ## Configuration provenance across upgrades
258
+
259
+ ```text
260
+ configuration --run <file> --skills-root <installed-flat-bundle> --note <provenance>
261
+ [--loaded denisco-handoff-implementation,denisco-workflow-benchmark]
262
+ session --run <file> ... --configuration <returned-ID>
263
+ session-config --run <file> --session-key provider:id --configuration <ID> --note <attribution>
264
+ ```
265
+
266
+ `configuration` appends a uniquely identified snapshot to configurationHistory: capture/observation
267
+ time, provenance, package file hashes and declared metadata.version. It reads only current files;
268
+ it is not retrospective evidence for B1 after installing B2. It does not import repository tooling
269
+ or a YAML dependency. Simple two-space-indented metadata.version scalars are recognized; unfamiliar
270
+ YAML remains an explicit unknown with a hash, not an invented version. Repository YAML validation
271
+ is a separate stronger check.
272
+
273
+ Every package is installed-only unless named in `--loaded`, which is an operator attestation
274
+ requiring observed session/tool evidence in the note. Being installed is never proof of loading.
275
+ Neither recording a snapshot nor updating environment automatically attributes it to any session.
276
+ Associate snapshots only with supporting evidence; legacy sessions remain unknown if evidence is
277
+ unavailable. `session-config` requires a note and refuses to replace a different prior attribution.
278
+ Reimport preserves associations. Sessions that genuinely mix skill versions need explicit notes;
279
+ do not describe them as a clean single-version comparison.
280
+
281
+ `environment` now appends notes without losing the previous legacy environmentReview. Reports keep
282
+ the latest view plus histories and per-session configuration IDs. Skill version/hash changes are
283
+ visible across upgrades, not silently applied to earlier work. Existing schemaVersion 1 registers
284
+ remain readable without rewriting them or guessing missing associations.
285
+
286
+ ## Working-session / operator boundary
287
+
288
+ The working session records real evidence and identified session IDs. If its own identity is not
289
+ available after the bounded retry, record the pending action for later evidence-based recovery;
290
+ do not ask the user to find IDs or choose the latest log blindly. One native session mixing planning
291
+ and implementation counts once under its registered phase.
292
+ After closure, collect and report finalize observed logs, while coverage still requires review.
293
+ Keep exploration separately visible. Measurement-only agent overhead is distinct from implementation.
294
+ Capture changes to the workflow configuration between blocks; different business blocks are not a
295
+ matched token-efficiency experiment. No helper authorizes paid runs, production writes or new tests.
296
+
297
+ ## Visual checks and optional delegation
298
+
299
+ An optional sidecar visualChecks array identifies checks by unique id, block, nonempty criteria,
300
+ executor "principal" or "subagent", and optional required: false. New ordinary checks use principal.
301
+ Only an explicit user request delegates the whole check; a perceptual blind judge remains a separate
302
+ child used when applicable and permitted. Its result and native usage do not replace the executor.
303
+
304
+ Use visual-start --executor principal|subagent before capture, with --principal-session when known,
305
+ and visual-finish with --session-key of the executor, --loaded true|false, --results JSON and
306
+ --evidence files afterwards. Explicitly load denisco-visual-check and refresh the registered executor log
307
+ before finish. Principal checks require a top-level session matching the recorded principal;
308
+ delegated checks require a registered child whose parent matches that principal. Skill loading
309
+ must appear in the executor's native log (Skill tool or slash command). Missing proof remains
310
+ unverified; installation or an agent's assertion alone is insufficient.
311
+
312
+ For compatibility, omitting --executor retains the legacy subagent behavior; new workflow calls
313
+ always name principal or the user's requested subagent. Existing run records without executor are
314
+ read as legacy delegated attempts. Existing sidecars already name subagent and stay readable.
315
+ Do not relabel historical attempts to satisfy a new policy. For future checks reconcile an old
316
+ delegated assignment against the current user request, preserve history and choose a new attempt ID.
317
+
318
+ Checkpoint validates the executor, session attribution, block and passing results for all assigned
319
+ criteria. It never accepts principal evidence for an explicitly delegated assignment or vice versa.
320
+ Evidence relevance remains an agent responsibility. Finish also stores artifact hashes and duration.
321
+ Record criterion evidence through result as usual. With disabled measurement use STATE as the record.
322
+
323
+ Only the implementation owner updates shared state and the register. Delegated visual-only checkers write their exclusive
324
+ attempt directory; the principal merges index rows. Keep measured code/tooling stable during capture.
325
+ Close resources created solely for the check on success, failure or cancellation, preserving existing
326
+ services and explicitly needed follow-up resources. Close/release returned children where supported;
327
+ report cleanup limitations rather than claiming process exit.
328
+
329
+ Inspect existing evidence and native logs before scheduling a replacement. For Claude children,
330
+ the native JSONL is normally in the conversation's subagents directory: verify root sessionId and
331
+ agentId in its records, register with --id ROOT_ID --agent AGENT_ID and the evidenced parent.
332
+ A temporary task .output file is not the native transcript. Other providers require their actual
333
+ native identity and relationship. Include the blind judge separately; missing attribution leaves
334
+ coverage pending and never warrants inventing usage or launching a model just to count it.
335
+
336
+ ## Coordinated execution
337
+
338
+ With denisco-implementation-coordinator explicitly authorized, follow denisco-handoff-implementation's bounded
339
+ assignment and ownership contract. Its preparation writes COORDINATOR, COORDINATOR-HISTORY and
340
+ packet files, then reaches a separate execution boundary. When planning selects the coordinated path,
341
+ planning and preparation share the planning registration. Register the coordinator before its actual native
342
+ children when measurement is enabled and bound: use planning for preparation or implementation for
343
+ an execution-only entry, with a coordination note. One native conversation spanning both phases
344
+ retains one registration with its actual history explained. Record overhead rather than hiding it.
345
+ Register a programming worker with its actual parent evidence. Each worker keeps its own ticket.
346
+ The worker owns the worktree and may update the run and sidecar; the coordinator only reads until
347
+ the worker finishes and releases. Parent registration failures leave child accounting pending and
348
+ never justify registering a native child as a top-level session. Independent implementation continues.
349
+ The coordinator may register/refresh/report measurement under its own claim between workers, but
350
+ never writes HANDOFF, PLAN, STATE, STATE-HISTORY or the sidecar, including through `close`, `bind`,
351
+ planning-prompt or next-prompt save/clear. Missing measurement setup or technical prompt repairs
352
+ belong to the standard worker workflow. Preserve pending attribution if setup cannot yet complete.
353
+
354
+ A programming worker performs its own visual checks. For a native child use executor `subagent`,
355
+ the worker's session key and its actual parent as principal session. The initial explicit request
356
+ to coordinate implementation includes its required checks. Reconcile future sidecar visualChecks
357
+ entries accordingly; preserve historical attempts. This changes no executor validation rules:
358
+ operational ownership is distinct from native parentage. Register a blind judge under its actual
359
+ caller, including when the coordinator relays a blind packet for a worker unable to spawn children.
360
+ Visual-only checkers retain their exclusive artifact scope and never write shared registers.
361
+
362
+ Keep the bounded assignment and coordinator launch/resume prompt in COORDINATOR, with coordination
363
+ events in COORDINATOR-HISTORY. The programmer reads those files and updates only technical documents
364
+ through denisco-handoff-implementation; the coordinator never appends technical history on its behalf.
365
+ STATE contains the short coordination reference and its ordinary generated next-prompt section.
366
+ The helper still generates a generic starting-block prompt; the coordinator supplies the exact
367
+ allowed block list and return boundary separately. After the final implementation block, retain
368
+ that block as the continuation anchor while STATE names pending integration; clear only when no
369
+ task work remains or the user explicitly cancels. Existing helpers do not launch workers or prove
370
+ that they respected their range. Cumulative usage is not live context occupancy; coordination does
371
+ not activate measurement when the sidecar is disabled.
@@ -0,0 +1,156 @@
1
+ #!/usr/bin/env node
2
+ import fs from 'node:fs';
3
+ import path from 'node:path';
4
+ import { fileURLToPath } from 'node:url';
5
+ import { requireValue, readJSON, writeJSON, loadRun, mutateRun, now } from './io.mjs';
6
+ import { createRun, registerSession, collect, recordEvidence, evaluate, report, compare, startVisualCheck, finishVisualCheck } from './runs.mjs';
7
+ import { prepare, checkProtected } from './prepare.mjs';
8
+ import { discover, whoami } from './discover.mjs';
9
+ import { bind, checkpoint, close, nextPrompt, open, planningPrompt } from './workflow.mjs';
10
+ import { track } from './tracking.mjs';
11
+ import { captureConfiguration, attachConfiguration, recordEnvironment } from './configuration.mjs';
12
+
13
+ const HELP = `Workflow benchmark (Node 20+, no model launches)
14
+ track --handoff FILE --provider codex|claude --phase PHASE [--root LOG_DIR]
15
+ [--from ISO --to ISO --parent KEY --parent-evidence TEXT --configuration ID|--skills-root DIR]
16
+ track --ticket FILE
17
+ planning-prompt --handoff FILE [--save|--check]
18
+ open --handoff FILE --block ID [--phase PHASE] [--provider codex|claude] [--nonce TOKEN]
19
+ close --handoff FILE --block ID --implementation complete|in-progress|not-started
20
+ (--next-block ID | --final) [--ticket FILE|--session-key provider:id] [--session-state STATE --claim STATUS]
21
+ whoami --provider codex|claude --nonce TOKEN [--project DIR --root LOG_DIR]
22
+ discover --provider codex|claude --project DIR [--root LOG_DIR --limit 20]
23
+ init --out DIR --task ID --project DIR --variant NAME --criteria JSON
24
+ bind --run FILE --handoff FILE [--task PROJECT_TASK --plan FILE --state FILE]
25
+ checkpoint --handoff FILE --block ID --implementation complete|in-progress|not-started
26
+ [--session-key provider:id --session-state active|closed|unknown --claim STATUS --next-block ID]
27
+ next-prompt --handoff FILE --block ID [--save|--check]
28
+ next-prompt --handoff FILE --clear
29
+ configuration --run FILE --skills-root DIR --note TEXT [--loaded name,name]
30
+ session-config --run FILE --session-key provider:id --configuration ID --note TEXT
31
+ prepare --out DIR --case order-filter|order-summary --variant baseline|candidate
32
+ --provider codex|claude [--baseline MANIFEST]
33
+ session --run FILE --provider codex|claude --id NATIVE_ID --log JSONL --phase PHASE
34
+ [--from ISO --to ISO] [--agent ID --parent provider:id --parent-evidence TEXT] [--coverage-confirmed] [--configuration ID]
35
+ visual-start --run FILE --id ATTEMPT --block ID --criteria ID[,ID] [--executor principal|subagent] [--principal-session KEY] [--at ISO]
36
+ visual-finish --run FILE --id ATTEMPT --session-key EXECUTOR_KEY --loaded true|false [--results JSON] [--evidence FILE[,FILE]] [--at ISO]
37
+ collect --run FILE [--coverage-confirmed]
38
+ result --run FILE --criterion ID --status pass|fail|not-run --observation TEXT [--evidence FILE]
39
+ review --run FILE --status accepted|reopened|pending --note TEXT
40
+ environment --run FILE --note TEXT
41
+ intervention --run FILE --kind clarification|correction|approval|environment --note TEXT
42
+ evaluate --run FILE
43
+ report --run FILE
44
+ compare --runs FILE,FILE [--out JSON]
45
+ Phases: exploration, specification, planning, implementation, verification, correction.
46
+ Options use separate values; quote paths with spaces. Log paths and native IDs are explicit.
47
+ `;
48
+ const allowed = {
49
+ track: ['handoff', 'provider', 'phase', 'root', 'ticket', 'from', 'to', 'parent', 'parent-evidence', 'configuration', 'skills-root'],
50
+ 'planning-prompt': ['handoff', 'save', 'check'],
51
+ bind: ['run', 'handoff', 'task', 'plan', 'state'],
52
+ open: ['handoff', 'block', 'phase', 'nonce', 'provider'],
53
+ close: ['handoff', 'block', 'implementation', 'next-block', 'final', 'session-key', 'session-state', 'claim', 'ticket'],
54
+ whoami: ['provider', 'project', 'nonce', 'root'],
55
+ checkpoint: ['handoff', 'block', 'implementation', 'session-key', 'session-state', 'claim', 'next-block'],
56
+ 'next-prompt': ['handoff', 'block', 'save', 'check', 'clear'],
57
+ configuration: ['run', 'skills-root', 'note', 'loaded'],
58
+ 'session-config': ['run', 'session-key', 'configuration', 'note'],
59
+ discover: ['provider', 'project', 'root', 'limit'],
60
+ init: ['out', 'task', 'project', 'variant', 'criteria'],
61
+ prepare: ['out', 'case', 'variant', 'provider', 'baseline'],
62
+ session: ['run', 'provider', 'id', 'agent', 'log', 'phase', 'from', 'to', 'parent', 'parent-evidence', 'coverage-confirmed', 'configuration'],
63
+ 'visual-start': ['run', 'id', 'block', 'criteria', 'executor', 'principal-session', 'at'],
64
+ 'visual-finish': ['run', 'id', 'session-key', 'loaded', 'results', 'evidence', 'at'],
65
+ collect: ['run', 'coverage-confirmed'], result: ['run', 'criterion', 'status', 'observation', 'evidence'],
66
+ review: ['run', 'status', 'note'], environment: ['run', 'note'], intervention: ['run', 'kind', 'note'],
67
+ evaluate: ['run'], report: ['run'], compare: ['runs', 'out']
68
+ };
69
+ export function main(args) {
70
+ const [command, ...rest] = args;
71
+ if (!command || ['help', '--help'].includes(command)) return HELP;
72
+ if (!allowed[command]) throw new Error(`Unknown command: ${command}`);
73
+ const opts = {};
74
+ for (let i = 0; i < rest.length; i++) {
75
+ const key = rest[i].slice(2);
76
+ if (!rest[i].startsWith('--') || !allowed[command].includes(key) || key in opts) throw new Error(`Invalid/duplicate option ${rest[i]}`);
77
+ opts[key] = ['coverage-confirmed', 'save', 'check', 'clear', 'final'].includes(key) ? true : requireValue(rest[++i], key);
78
+ }
79
+ const need = name => requireValue(opts[name], `--${name}`);
80
+ if (command === 'track') return track({ handoff: opts.handoff, provider: opts.provider, phase: opts.phase,
81
+ root: opts.root, ticket: opts.ticket, from: opts.from, to: opts.to, parent: opts.parent,
82
+ parentEvidence: opts['parent-evidence'], configurationId: opts.configuration, skillsRoot: opts['skills-root'] });
83
+ if (command === 'planning-prompt') {
84
+ const result = planningPrompt({ handoff: need('handoff'), save: opts.save, check: opts.check });
85
+ if (result.valid === false) process.exitCode = 2;
86
+ return result;
87
+ }
88
+ if (command === 'open') {
89
+ const result = open({ handoff: need('handoff'), block: need('block'), phase: opts.phase, nonce: opts.nonce, provider: opts.provider });
90
+ if (result.continuation.valid === false) process.exitCode = 2;
91
+ return result;
92
+ }
93
+ if (command === 'close') {
94
+ const result = close({ handoff: need('handoff'), block: need('block'), implementation: need('implementation'),
95
+ nextBlock: opts['next-block'], final: opts.final ?? false, sessionKey: opts['session-key'],
96
+ sessionState: opts['session-state'], claim: opts.claim, ticket: opts.ticket });
97
+ if (!result.claimMatches || result.continuation?.valid === false) process.exitCode = 2;
98
+ return result;
99
+ }
100
+ if (command === 'whoami') return whoami({ provider: need('provider'), project: opts.project, nonce: need('nonce'), root: opts.root });
101
+ if (command === 'bind') return bind({ handoff: need('handoff'), run: need('run'), task: opts.task, plan: opts.plan, state: opts.state });
102
+ if (command === 'checkpoint') {
103
+ const result = checkpoint({ handoff: need('handoff'), block: need('block'), implementation: need('implementation'), sessionKey: opts['session-key'], sessionState: opts['session-state'], claim: opts.claim, nextBlock: opts['next-block'] });
104
+ if (!result.claimMatches || result.continuation?.valid === false) process.exitCode = 2;
105
+ return result;
106
+ }
107
+ if (command === 'next-prompt') {
108
+ const result = nextPrompt({ handoff: need('handoff'), block: opts.clear ? opts.block : need('block'), save: opts.save, check: opts.check, clear: opts.clear });
109
+ if (result.valid === false) process.exitCode = 2;
110
+ return result;
111
+ }
112
+ if (command === 'configuration') return captureConfiguration(need('run'), { skillsRoot: need('skills-root'), note: need('note'), loaded: opts.loaded?.split(',') ?? [] });
113
+ if (command === 'session-config') return attachConfiguration(need('run'), { sessionKey: need('session-key'), configurationId: need('configuration'), note: need('note') });
114
+ if (command === 'discover') return discover({ provider: need('provider'), project: need('project'), root: opts.root, limit: opts.limit === undefined ? 20 : Number(opts.limit) });
115
+ if (command === 'init') return createRun({ out: need('out'), task: need('task'), project: need('project'), variant: need('variant'), criteria: readJSON(need('criteria')) });
116
+ if (command === 'prepare') return prepare({ out: need('out'), caseId: need('case'), variant: need('variant'), provider: need('provider'), baseline: opts.baseline });
117
+ if (command === 'session') return registerSession(need('run'), { provider: need('provider'), sessionId: need('id'), agentId: opts.agent, log: need('log'), phase: need('phase'), from: opts.from, to: opts.to, parent: opts.parent, parentEvidence: opts['parent-evidence'], coverageConfirmed: opts['coverage-confirmed'] ?? false, configurationId: opts.configuration });
118
+ if (command === 'visual-start') return startVisualCheck(need('run'), { id: need('id'), block: need('block'), criteria: need('criteria').split(','), executor: opts.executor, principalSession: opts['principal-session'], at: opts.at });
119
+ if (command === 'visual-finish') {
120
+ if (!['true', 'false'].includes(need('loaded'))) throw new Error('--loaded must be true or false');
121
+ return finishVisualCheck(need('run'), { id: need('id'), sessionKey: need('session-key'), skillLoaded: opts.loaded === 'true', results: opts.results ? readJSON(opts.results) : [], evidence: opts.evidence ? opts.evidence.split(',') : [], at: opts.at });
122
+ }
123
+ if (command === 'collect') return collect(need('run'), { coverageConfirmed: opts['coverage-confirmed'] ?? false });
124
+ if (command === 'result') return recordEvidence(need('run'), { criterion: need('criterion'), status: need('status'), observation: need('observation'), evidence: opts.evidence });
125
+ if (command === 'review') {
126
+ if (!['accepted', 'reopened', 'pending'].includes(need('status'))) throw new Error('Invalid review status');
127
+ return mutateRun(need('run'), run => {
128
+ const event = { type: opts.status, note: need('note'), at: now(), evidenceCount: run.evidence.length };
129
+ run.history.push(event); run.review = { status: opts.status, note: opts.note, at: event.at };
130
+ });
131
+ }
132
+ if (command === 'environment') return recordEnvironment(need('run'), need('note'));
133
+ if (command === 'intervention') {
134
+ if (!['clarification', 'correction', 'approval', 'environment'].includes(need('kind'))) throw new Error('Invalid intervention kind');
135
+ return mutateRun(need('run'), run => { run.interventions.push({ kind: opts.kind, note: need('note'), at: now() }); });
136
+ }
137
+ if (command === 'evaluate') {
138
+ const run = loadRun(need('run')), changed = checkProtected(run);
139
+ if (changed.length) throw new Error(`Benchmark contaminated: protected files changed: ${changed.join(', ')}`);
140
+ const result = evaluate(opts.run);
141
+ if (!result.passed) process.exitCode = 2;
142
+ return result;
143
+ }
144
+ if (command === 'report') return report(need('run'));
145
+ if (command === 'compare') {
146
+ const files = need('runs').split(',');
147
+ if (files.length < 2) throw new Error('At least two runs are required');
148
+ const result = compare(files);
149
+ if (opts.out) writeJSON(opts.out, result);
150
+ return result;
151
+ }
152
+ }
153
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
154
+ try { const result = main(process.argv.slice(2)); console.log(typeof result === 'string' ? result : JSON.stringify(result, null, 2)); }
155
+ catch (e) { console.error(e.message); process.exitCode = 1; }
156
+ }
@@ -0,0 +1,46 @@
1
+ export const CASES = {
2
+ 'order-filter': {
3
+ title: 'Bug: filtro ordini coerente tra dashboard ed esportazione',
4
+ criteria: [
5
+ { id: 'filter', expectation: 'Il filtro restituisce soltanto gli ordini dello stato richiesto.' },
6
+ { id: 'consumers', expectation: 'Dashboard ed esportazione usano la stessa regola corretta.' },
7
+ { id: 'regression', expectation: 'All, input vuoto, ordine originale e dati non mutati restano corretti.' }
8
+ ],
9
+ files: {
10
+ 'src/orders.mjs': `export function filterOrders(orders, status = 'all') {\n return status === 'all' ? [...orders] : orders.filter(order => order.status !== status);\n}\n`,
11
+ 'src/dashboard.mjs': `import { filterOrders } from './orders.mjs';\nexport const dashboardOrders = (orders, status) => filterOrders(orders, status);\n`,
12
+ 'src/export.mjs': `import { filterOrders } from './orders.mjs';\nexport const exportOrders = (orders, status) => filterOrders(orders, status);\n`
13
+ },
14
+ reference: {
15
+ 'src/orders.mjs': `export function filterOrders(orders, status = 'all') {\n return status === 'all' ? [...orders] : orders.filter(order => order.status === status);\n}\n`
16
+ },
17
+ points: `## P1 — Correggere il filtro condiviso\n\nReady. Il comportamento atteso è uguaglianza dello stato, non esclusione. Riproduzione: tre ordini pending/completed/cancelled, filtro completed: deve restare solo completed. La causa è l'operatore in src/orders.mjs, filterOrders.\n\nRiutilizzare filterOrders per entrambi i consumatori src/dashboard.mjs e src/export.mjs. Non mutare input o ordine degli elementi. All restituisce tutti gli ordini; uno stato assente restituisce un array vuoto. Nessuna modifica backend esterna, UI o nuovo requisito visuale. Difficoltà verde, dipendenze nessuna.\n`
18
+ },
19
+ 'order-summary': {
20
+ title: 'Feature: riepilogo ordini condiviso',
21
+ criteria: [
22
+ { id: 'summary', expectation: 'summarizeOrders restituisce total e byStatus per pending/completed/cancelled.' },
23
+ { id: 'consumers', expectation: 'dashboardSummary ed exportSummary restituiscono il riepilogo condiviso.' },
24
+ { id: 'regression', expectation: 'Zero per categorie vuote; filtri precedenti e input restano invariati.' }
25
+ ],
26
+ files: {
27
+ 'src/orders.mjs': `export function filterOrders(orders, status = 'all') {\n return status === 'all' ? [...orders] : orders.filter(order => order.status === status);\n}\n`,
28
+ 'src/dashboard.mjs': `import { filterOrders } from './orders.mjs';\nexport const dashboardOrders = (orders, status) => filterOrders(orders, status);\n`,
29
+ 'src/export.mjs': `import { filterOrders } from './orders.mjs';\nexport const exportOrders = (orders, status) => filterOrders(orders, status);\n`
30
+ },
31
+ reference: {
32
+ 'src/orders.mjs': `export function filterOrders(orders, status = 'all') {\n return status === 'all' ? [...orders] : orders.filter(order => order.status === status);\n}\nexport function summarizeOrders(orders) {\n const byStatus = { pending: 0, completed: 0, cancelled: 0 };\n for (const order of orders) byStatus[order.status]++;\n return { total: orders.length, byStatus };\n}\n`,
33
+ 'src/dashboard.mjs': `import { filterOrders, summarizeOrders } from './orders.mjs';\nexport const dashboardOrders = (orders, status) => filterOrders(orders, status);\nexport const dashboardSummary = orders => summarizeOrders(orders);\n`,
34
+ 'src/export.mjs': `import { filterOrders, summarizeOrders } from './orders.mjs';\nexport const exportOrders = (orders, status) => filterOrders(orders, status);\nexport const exportSummary = orders => summarizeOrders(orders);\n`
35
+ },
36
+ points: `## P1 — Aggiungere la regola pura\n\nReady. Esportare summarizeOrders(orders) da src/orders.mjs. Input: array di ordini con status tra pending, completed e cancelled; valori fuori contratto esclusi. Output esatto: { total: numero, byStatus: { pending: numero, completed: numero, cancelled: numero } }. Categorie assenti e input vuoto hanno zero; non mutare gli ordini. Il riepilogo riguarda tutti gli ordini passati, non un filtro implicito. Difficoltà gialla, nessuna dipendenza.\n\n## P2 — Collegare i consumatori\n\nReady. Esportare dashboardSummary da src/dashboard.mjs ed exportSummary da src/export.mjs; entrambe accettano orders e restituiscono il contratto di P1. Riutilizzare la regola condivisa. Conservare le funzioni filtro precedenti. Difficoltà verde, dipende da P1. Nessuna nuova UI, persistenza o servizio esterno.\n`
37
+ }
38
+ };
39
+
40
+ export function handoff(caseId) {
41
+ const c = CASES[caseId];
42
+ if (!c) throw new Error('Unknown case');
43
+ return `Execute this document with the installed handoff implementation skill. Visual checks are not applicable to this headless task.\n\n# ${c.title}\n\nTask: ${caseId}. This is decided target behavior; no product decisions remain.\n\nBaseline: src/orders.mjs contains a shared filter consumed by dashboard and export. Follow AGENTS.md.\n\n${c.points}\n## Acceptance\n\n${c.criteria.map(x => `- ${x.id}: ${x.expectation}`).join('\n')}\n\nVerification: run node smoke.mjs, plus focused tests you add for these acceptance rules. The operator runs the external fixed evaluator after implementation; do not inspect it or reference solutions. No browser or login is needed.\n`;
44
+ }
45
+
46
+ export const smoke = `import assert from 'node:assert/strict';\nimport { filterOrders } from './src/orders.mjs';\nassert.deepEqual(filterOrders([]), []);\nconsole.log('Existing smoke check passed; acceptance checks are additional.');\n`;