@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,184 @@
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+ import { fileURLToPath } from 'node:url';
4
+ import { hash, inside, readJSON, writeJSON, rejectSymlinkPath } from '../skills/denisco-workflow-benchmark/scripts/io.mjs';
5
+ import { ownership } from '../skills/denisco-handoff-implementation/scripts/ownership.mjs';
6
+ import { discoverSkills, repository } from './catalog.mjs';
7
+
8
+ export { repository } from './catalog.mjs';
9
+
10
+ // Managed skills installed under an earlier name. An unmodified old copy is removed when the
11
+ // renamed skill is installed in the same root; a modified one stops the installation.
12
+ export const renamedSkills = Object.freeze({
13
+ 'project-context': 'denisco-project-context',
14
+ 'interactive-spec-definition': 'denisco-interactive-spec-definition',
15
+ 'handoff-implementation': 'denisco-handoff-implementation',
16
+ 'implementation-coordinator': 'denisco-implementation-coordinator',
17
+ 'visual-check': 'denisco-visual-check',
18
+ 'workflow-benchmark': 'denisco-workflow-benchmark'
19
+ });
20
+
21
+ // Git with core.autocrlf rewrites line endings on checkout, stash or stage. A copy that differs from
22
+ // a recorded digest only in CRLF/LF is the same managed content, not a local modification.
23
+ const toLF = content => Buffer.from(content.toString('utf8').replace(/\r\n/g, '\n'));
24
+ const toCRLF = content => Buffer.from(toLF(content).toString('utf8').replace(/\n/g, '\r\n'));
25
+ function sameContent(content, digest) {
26
+ return typeof digest === 'string' && [content, toLF(content), toCRLF(content)].some(variant => hash(variant) === digest);
27
+ }
28
+
29
+ // Skills root inside the project for each provider. Codex, Antigravity (agy) and Gemini CLI all read
30
+ // <project>/.agents/skills, so they share one installation and one manifest.
31
+ export const providerRoots = Object.freeze({
32
+ claude: path.join('.claude', 'skills'),
33
+ codex: path.join('.agents', 'skills'),
34
+ agy: path.join('.agents', 'skills'),
35
+ gemini: path.join('.agents', 'skills')
36
+ });
37
+
38
+ // `external` holds skills already downloaded into memory ({ name, files: Map<relative, Buffer> }).
39
+ // They share the root's manifest and its update/conflict rules; in addition, an existing folder of
40
+ // the same name that the manifest does not manage is a conflict instead of being adopted.
41
+ export function planInstall({ target, provider, source = repository, withoutBenchmark = false, exclude = [], external = [] }) {
42
+ if (!Object.hasOwn(providerRoots, provider)) throw new Error(`provider must be one of: ${Object.keys(providerRoots).join(', ')}`);
43
+ const excluded = [...new Set([...(withoutBenchmark ? ['denisco-workflow-benchmark'] : []), ...exclude])];
44
+ const packaged = discoverSkills(source).filter(skill => !excluded.includes(skill.name));
45
+ for (const skill of external) {
46
+ if (packaged.some(p => p.name === skill.name)) throw new Error(`Downloaded skill ${skill.name} clashes with a packaged skill`);
47
+ }
48
+ const skills = [...packaged, ...external.map(({ name, files }) => ({ name, files: [...files.keys()], contents: files }))];
49
+ if (!fs.statSync(target).isDirectory()) throw new Error('Target project must exist');
50
+ const root = path.join(path.resolve(target), providerRoots[provider]);
51
+ rejectSymlinkPath(root);
52
+ const manifestPath = path.join(root, '.workflow-install.json');
53
+ rejectSymlinkPath(manifestPath);
54
+ const previous = fs.existsSync(manifestPath) ? readJSON(manifestPath) : { schemaVersion: 1, files: {} };
55
+ if (previous.schemaVersion !== 1 || !previous.files || typeof previous.files !== 'object') throw new Error('Invalid installation manifest');
56
+ const entries = [], unmanaged = [];
57
+ for (const { name, directory: sourceDir, files, contents } of skills) {
58
+ if (contents) {
59
+ const folder = inside(root, name);
60
+ rejectSymlinkPath(folder);
61
+ if (fs.existsSync(folder) && !Object.keys(previous.files).some(key => key.startsWith(`${name}/`))) unmanaged.push(name);
62
+ }
63
+ for (const relative of files) {
64
+ const key = `${name}/${relative}`, destination = inside(root, key);
65
+ rejectSymlinkPath(destination);
66
+ const content = contents ? contents.get(relative) : fs.readFileSync(path.join(sourceDir, relative)), wanted = hash(content);
67
+ const existing = fs.existsSync(destination) ? fs.readFileSync(destination) : null;
68
+ const current = existing ? hash(existing) : null;
69
+ const action = existing === null ? 'create' : sameContent(existing, wanted) ? 'unchanged'
70
+ : sameContent(existing, previous.files[key]) ? 'update' : 'conflict';
71
+ entries.push({ key, destination, wanted, current, action, content });
72
+ }
73
+ }
74
+ const installed = new Set(skills.map(skill => skill.name));
75
+ const removals = [], stale = [];
76
+ for (const key of Object.keys(previous.files)) {
77
+ if (entries.some(e => e.key === key)) continue;
78
+ const oldName = key.split('/')[0];
79
+ if (!installed.has(renamedSkills[oldName])) { stale.push(key); continue; }
80
+ const destination = inside(root, key);
81
+ rejectSymlinkPath(destination);
82
+ const existing = fs.existsSync(destination) ? fs.readFileSync(destination) : null;
83
+ removals.push({ key, destination, action: existing === null ? 'gone' : sameContent(existing, previous.files[key]) ? 'remove' : 'conflict' });
84
+ }
85
+ return { root, manifestPath, entries, removals, stale, excluded, unmanaged };
86
+ }
87
+
88
+ // A writer holding the workflow claim in the target or in any of its linked worktrees may still
89
+ // load the old package paths, because linked worktrees use the skills installed at the target.
90
+ function worktrees(target) {
91
+ const list = [path.resolve(target)], linked = path.join(target, '.git', 'worktrees');
92
+ if (fs.existsSync(linked) && fs.statSync(linked).isDirectory()) {
93
+ for (const entry of fs.readdirSync(linked)) {
94
+ const gitdir = path.join(linked, entry, 'gitdir');
95
+ if (!fs.existsSync(gitdir)) continue;
96
+ const directory = path.dirname(path.resolve(linked, entry, fs.readFileSync(gitdir, 'utf8').trim()));
97
+ if (fs.existsSync(directory)) list.push(directory);
98
+ }
99
+ }
100
+ return list;
101
+ }
102
+ function activeWriter(target) {
103
+ for (const worktree of worktrees(target)) {
104
+ let status;
105
+ try { status = ownership({ action: 'status', worktree }); }
106
+ catch (error) {
107
+ if (/initialized Git worktree/.test(error.message)) continue; // no worktree, no claim
108
+ throw error;
109
+ }
110
+ if (status.status === 'owned') return { worktree, owner: status.owner };
111
+ }
112
+ return null;
113
+ }
114
+
115
+ // Skill names recorded in a root's manifest, with pre-prefix names mapped to their current name.
116
+ export function manifestSkills(root) {
117
+ const manifestPath = path.join(root, '.workflow-install.json');
118
+ rejectSymlinkPath(manifestPath);
119
+ if (!fs.existsSync(manifestPath)) return new Set();
120
+ const manifest = readJSON(manifestPath);
121
+ if (manifest.schemaVersion !== 1 || !manifest.files || typeof manifest.files !== 'object') throw new Error('Invalid installation manifest');
122
+ return new Set(Object.keys(manifest.files).map(key => key.split('/')[0]).map(name => renamedSkills[name] ?? name));
123
+ }
124
+
125
+ export function summarizePlan(plan) {
126
+ return {
127
+ root: plan.root,
128
+ changes: [...plan.entries, ...plan.removals].map(({ key, action }) => ({ key, action })),
129
+ renamed: [...new Set(plan.removals.map(r => r.key.split('/')[0]))].map(from => ({ from, to: renamedSkills[from] })),
130
+ stale: plan.stale, excluded: plan.excluded, applied: false
131
+ };
132
+ }
133
+
134
+ // Every safety stop happens here, before any write. Callers installing several roots check all of
135
+ // their plans first, so a stop in one root cannot leave another root partially written.
136
+ export function checkPlan(plan, target) {
137
+ if ([...plan.entries, ...plan.removals].some(e => e.action === 'conflict') || plan.stale.length || plan.unmanaged?.length) {
138
+ throw new Error('Installation conflicts or stale managed files: review the dry-run; no files changed');
139
+ }
140
+ const writer = plan.removals.length ? activeWriter(target) : null;
141
+ if (writer) {
142
+ throw new Error(`A workflow writer holds the claim in ${writer.worktree}: rename migration deferred until it releases; no files changed`);
143
+ }
144
+ }
145
+
146
+ export function install(options) {
147
+ const plan = planInstall(options);
148
+ const summary = summarizePlan(plan);
149
+ if (!options.apply) return summary;
150
+ checkPlan(plan, options.target);
151
+ applyPlan(plan);
152
+ return { ...summary, applied: true };
153
+ }
154
+
155
+ export function applyPlan(plan) {
156
+ for (const entry of plan.entries) {
157
+ if (entry.action === 'unchanged') continue;
158
+ fs.mkdirSync(path.dirname(entry.destination), { recursive: true });
159
+ fs.writeFileSync(entry.destination, entry.content);
160
+ }
161
+ for (const removal of plan.removals) {
162
+ if (removal.action !== 'remove') continue;
163
+ fs.unlinkSync(removal.destination);
164
+ // Prune directories emptied by the removal, never the skills root or unmanaged content.
165
+ for (let dir = path.dirname(removal.destination); dir !== plan.root && dir.startsWith(plan.root); dir = path.dirname(dir)) {
166
+ if (fs.readdirSync(dir).length) break;
167
+ fs.rmdirSync(dir);
168
+ }
169
+ }
170
+ writeJSON(plan.manifestPath, { schemaVersion: 1, files: Object.fromEntries(plan.entries.map(e => [e.key, e.wanted])) });
171
+ }
172
+ if (process.argv[1] && path.resolve(process.argv[1]) === fileURLToPath(import.meta.url)) {
173
+ try {
174
+ const args = process.argv.slice(2), options = {};
175
+ for (let i = 0; i < args.length; i++) {
176
+ if (args[i] === '--apply') options.apply = true;
177
+ else if (args[i] === '--without-benchmark') options.withoutBenchmark = true;
178
+ else if (['--target', '--provider'].includes(args[i])) options[args[i].slice(2)] = args[++i];
179
+ else throw new Error('Usage: node scripts/install.mjs --target <project> --provider codex|claude [--without-benchmark] [--apply]');
180
+ }
181
+ if (!options.target) throw new Error('Explicit --target required');
182
+ console.log(JSON.stringify(install(options), null, 2));
183
+ } catch (e) { console.error(e.message); process.exitCode = 1; }
184
+ }
@@ -0,0 +1,102 @@
1
+ import { skillFrontmatter } from './catalog.mjs';
2
+
3
+ // Recommended third-party skills, downloaded at install time from the latest commit of `ref`.
4
+ // Adding one needs only a new entry here: `path` is the skill folder inside the repository and its
5
+ // last segment must equal `name`; `requires` lists catalog entries installed with it.
6
+ export const recommendedSkills = Object.freeze([
7
+ {
8
+ name: 'grill-me',
9
+ description: 'relentless interview about a plan or decision',
10
+ repo: 'mattpocock/skills', ref: 'main', path: 'skills/productivity/grill-me',
11
+ requires: ['grilling'], license: 'MIT', author: 'Matt Pocock'
12
+ },
13
+ {
14
+ name: 'grilling',
15
+ description: 'the grilling procedure used by grill-me',
16
+ repo: 'mattpocock/skills', ref: 'main', path: 'skills/productivity/grilling',
17
+ requires: [], license: 'MIT', author: 'Matt Pocock'
18
+ }
19
+ ]);
20
+
21
+ const byName = new Map(recommendedSkills.map(skill => [skill.name, skill]));
22
+ export const isRecommended = name => byName.has(name);
23
+
24
+ // The chosen names plus every recommended skill they require, transitively.
25
+ export function withDependencies(names) {
26
+ const result = new Set();
27
+ const visit = name => {
28
+ if (result.has(name)) return;
29
+ result.add(name);
30
+ for (const dependency of byName.get(name)?.requires ?? []) visit(dependency);
31
+ };
32
+ names.forEach(visit);
33
+ return [...result];
34
+ }
35
+
36
+ // Catalog entries that require `name` and are among `chosen`.
37
+ export const requiredBy = (name, chosen) => recommendedSkills.filter(s => chosen.includes(s.name) && s.requires.includes(name)).map(s => s.name);
38
+
39
+ export class DownloadError extends Error {}
40
+
41
+ const API = 'https://api.github.com', RAW = 'https://raw.githubusercontent.com';
42
+ const encodePath = value => value.split('/').map(encodeURIComponent).join('/');
43
+
44
+ async function request(fetchImpl, url, { token, accept } = {}) {
45
+ const headers = { 'User-Agent': 'deniscodp-skills-installer' };
46
+ if (accept) headers.Accept = accept;
47
+ // The token only goes to the GitHub API and never appears in messages.
48
+ if (token && url.startsWith(API)) headers.Authorization = `Bearer ${token}`;
49
+ let response;
50
+ try {
51
+ response = await fetchImpl(url, { headers, redirect: 'follow', signal: AbortSignal.timeout(30000) });
52
+ } catch (error) {
53
+ throw new DownloadError(`network error for ${url}: ${error.message}`);
54
+ }
55
+ if (!response.ok) {
56
+ const limited = [403, 429].includes(response.status) && response.headers?.get?.('x-ratelimit-remaining') === '0';
57
+ throw new DownloadError(`HTTP ${response.status} for ${url}${limited ? ' (GitHub rate limit reached; wait or set GITHUB_TOKEN)' : ''}`);
58
+ }
59
+ return response;
60
+ }
61
+
62
+ // Downloads every file of the named catalog skills into memory before any plan is computed, so a
63
+ // failure leaves the target untouched. Two GitHub API calls per repository (commit, recursive tree),
64
+ // then one raw download per file at that exact commit.
65
+ export async function downloadRecommended(names, { fetch: fetchImpl = globalThis.fetch, token = process.env.GITHUB_TOKEN } = {}) {
66
+ const wanted = names.map(name => {
67
+ const skill = byName.get(name);
68
+ if (!skill) throw new DownloadError(`not a recommended skill: ${name}`);
69
+ return skill;
70
+ });
71
+ const commits = new Map(), trees = new Map(), downloaded = [];
72
+ for (const skill of wanted) {
73
+ const origin = `${skill.repo}@${skill.ref}`;
74
+ if (!commits.has(origin)) {
75
+ const response = await request(fetchImpl, `${API}/repos/${skill.repo}/commits/${encodeURIComponent(skill.ref)}`, { token, accept: 'application/vnd.github.sha' });
76
+ const commit = (await response.text()).trim();
77
+ if (!/^[0-9a-f]{40}$/.test(commit)) throw new DownloadError(`unexpected commit id for ${origin}`);
78
+ const tree = await (await request(fetchImpl, `${API}/repos/${skill.repo}/git/trees/${commit}?recursive=1`, { token, accept: 'application/vnd.github+json' })).json();
79
+ if (!Array.isArray(tree?.tree)) throw new DownloadError(`unexpected tree listing for ${origin}`);
80
+ if (tree.truncated) throw new DownloadError(`tree listing of ${origin} is truncated`);
81
+ commits.set(origin, commit);
82
+ trees.set(origin, tree.tree);
83
+ }
84
+ const commit = commits.get(origin), prefix = `${skill.path}/`;
85
+ const items = trees.get(origin).filter(item => item.path.startsWith(prefix) && item.type !== 'tree');
86
+ if (!items.length) throw new DownloadError(`${skill.path} not found in ${origin} (commit ${commit})`);
87
+ const files = new Map();
88
+ for (const item of items.sort((a, b) => a.path < b.path ? -1 : 1)) {
89
+ const relative = item.path.slice(prefix.length);
90
+ if (item.mode === '120000') throw new DownloadError(`symlink refused: ${item.path} in ${origin}`);
91
+ if (item.type !== 'blob') throw new DownloadError(`unsupported entry ${item.type}: ${item.path} in ${origin}`);
92
+ if (relative.split('/').some(segment => !segment || segment === '.' || segment === '..')) throw new DownloadError(`unsafe path: ${item.path}`);
93
+ const response = await request(fetchImpl, `${RAW}/${skill.repo}/${commit}/${encodePath(item.path)}`);
94
+ files.set(relative, Buffer.from(await response.arrayBuffer()));
95
+ }
96
+ if (!files.has('SKILL.md')) throw new DownloadError(`${skill.path}/SKILL.md missing in ${origin} (commit ${commit})`);
97
+ try { skillFrontmatter(files.get('SKILL.md').toString('utf8'), skill.name, `${origin}:${skill.path}/SKILL.md`); }
98
+ catch (error) { throw new DownloadError(error.message); }
99
+ downloaded.push({ name: skill.name, files, source: { repo: skill.repo, ref: skill.ref, commit, license: skill.license, author: skill.author } });
100
+ }
101
+ return downloaded;
102
+ }
@@ -0,0 +1,254 @@
1
+ ---
2
+ name: denisco-handoff-implementation
3
+ description: "Plan and execute a decided handoff block by block until a real stop, verify acceptance and keep task state current. Use for planning a decided bug fix or feature and implementing or resuming its blocks; not for open-ended product design or repository onboarding."
4
+ metadata:
5
+ version: "8.3.0"
6
+ ---
7
+
8
+ # Handoff implementation
9
+
10
+ Input: handoff path, optional point/block assignment and existing plan/state paths.
11
+ Legacy `punto 3` inputs remain valid. Preserve approved behavior and report discrepancies.
12
+ For an explicit bounded assignment or a programmer dispatched by denisco-implementation-coordinator,
13
+ read [bounded implementation](references/delegated-implementation.md). Its explicit block list and
14
+ return boundary constrain the normal continue-until-stop loop; a starting block alone does not.
15
+ The programmer owns technical document updates through this skill's existing procedures.
16
+ COORDINATOR.md and COORDINATOR-HISTORY.md are read-only inputs owned by the coordinator;
17
+ their grouping proposals never replace HANDOFF, PLAN, STATE or the operational block map.
18
+ Even with only a handoff path, look for `<handoff-path>.workflow.json` and read
19
+ [the operational context contract](references/session-plan.md) before entering or resuming.
20
+ Before any task writes or measurement registration, follow [worktree ownership](references/ownership.md):
21
+ inspect peers and acquire the single-writer claim with the installed helper. A second top-level
22
+ instance stays read-only even when it shares this conversation ID. A delegated visual child has
23
+ only its assigned artifact scope. This gate also applies when measurement is disabled; a claim
24
+ coordinates execution and never supplies missing user authorization.
25
+ Recover measurement only from this context or explicitly referenced task documents, never from
26
+ unrelated registers. Measurement is disabled by default and `disabled` is authoritative across
27
+ sessions. Do not load, install or use denisco-workflow-benchmark, search for a run, or change that state
28
+ unless the current user request or applicable project instructions explicitly enable measurement
29
+ for this task. `Unknown` is inert legacy/interrupted state, not permission to enable; preserve or
30
+ resolve it to disabled unless explicit activation evidence exists. An unavailable measurement
31
+ helper never blocks independent planning or implementation.
32
+
33
+ In writable mode with measurement enabled and the helper available, at first entry (including
34
+ planning) load denisco-workflow-benchmark and run `track --handoff <file>
35
+ --provider <actual-provider> --phase <planning|implementation>`, then execute its returned
36
+ nextCall yourself in a separate tool call. This works before PLAN/STATE and the block map exist.
37
+ Keep technical identifiers internal; do not ask the user to assemble registration commands.
38
+ If registration remains pending after one later retry, record the gap and continue independent work.
39
+ Keep the returned ticket in this conversation. At closure pass that same ticket to
40
+ `close --ticket <ticket>` when available; this reuses the resolved identity and prevents an
41
+ `identity-pending` checkpoint. Do not copy it into the next session's prompt.
42
+
43
+ ## Enter at the right stage
44
+
45
+ The user's request selects the phase, or the user's yes to a phase question the agent asked; file
46
+ existence and tool mode do not. Read project instructions, handoff, operational context and
47
+ existing PLAN/STATE first. Read assigned points and dependencies in full; preserve settled product
48
+ decisions.
49
+
50
+ Require scoped evidence before classifying a request as implementation. Bare native transition
51
+ text such as `Implement the plan.`, `Start coding` or `Proceed` names neither a saved execution
52
+ prompt nor a handoff/block assignment, is not the user's answer to a phase question, and is not
53
+ implementation authorization. If it follows a specification or planning proposal whose artifacts
54
+ are still pending, finish that existing phase and ask its phase question; do not reinterpret the
55
+ verb "implement" as a new assignment.
56
+
57
+ - **Planning only:** the prompt from HANDOFF, the user's yes to the specification's planning
58
+ question, or a handoff path without an execution assignment creates/reconciles PLAN, STATE and
59
+ the complete planned block map. First check the handoff's blocker table and permission list.
60
+ A handoff without them, or with an unresolved item, gets the blocker hunt from
61
+ [the blocker checklist](references/blocker-checklist.md) now, before blocks are chosen. Select
62
+ the first dependency-ready block and save/check its execution prompt in STATE. Then choose the
63
+ execution path. **Coordinated** is the default when the plan has two or more implementation
64
+ blocks (final integration excluded), denisco-implementation-coordinator is installed and the runtime can
65
+ create fresh-context workers, unless the user asked for direct execution: release the planning
66
+ claim, read the installed
67
+ denisco-implementation-coordinator `SKILL.md` by its path and continue with its preparation in this
68
+ conversation. Preparation writes only coordination documents, launches nothing and ends with its
69
+ own start question instead of the one below. Otherwise the path is **direct**: ask "Plan ready.
70
+ Start <block>?", naming the reason when the coordinator or fresh workers are unavailable. The user's yes starts
71
+ implementation in this conversation; any other answer ends planning without application-code
72
+ edits, also in automatic mode. With the question, mention that the saved prompt can instead
73
+ start it in a fresh conversation with less context.
74
+ - **Implementation:** the user's yes to the start question, a user-submitted execution prompt or an
75
+ explicit scoped execution request authorizes implementing the plan from the named block. Record
76
+ that instruction, date and scope in STATE and proceed without another plan-approval question.
77
+ Generating or reading a prompt in a file is not a user request. Respect newer restrictions.
78
+ An authorized coordinator may dispatch a bounded assignment under the recorded user request;
79
+ preserve both that authorization and the narrower assignment in STATE.
80
+ - **Resume:** establish ownership, inspect the worktree, STATE, results and attributable logs.
81
+ Distinguish absent, unregistered, stale and failed evidence before repeating checks. Documents
82
+ left missing by older sessions are repaired without replanning; a STATE in an older format is
83
+ migrated as the session plan describes.
84
+
85
+ When a new human session starts on a task that already has STATE, go through **Your actions** first: ask
86
+ which items are done and, in the same message, whether the user changed anything else since the
87
+ last session (deploys, portals, configuration). Record the answers in STATE before continuing.
88
+ An internal programmer rotation uses the coordinator's recorded answers and routes new questions
89
+ through it, as the bounded implementation contract specifies.
90
+
91
+ Use [session planning](references/session-plan.md) for the split and document contracts. Small
92
+ tasks can have one block. A prepared block map does not claim that implementation was authorized.
93
+
94
+ In Plan Mode propose only the current phase's deliverables and name pending writes. Once writing
95
+ is permitted, materialize them and ask the phase question. Native plan approval or switching to
96
+ automatic mode never advances from specification to planning or from planning to implementation.
97
+ A private CLI plan does not replace the documents. Before asking the start question, verify actual
98
+ PLAN, STATE, the complete block/criterion map and the saved/checked first execution prompt. Record
99
+ the plan as prepared, preserving any historical approvals rather than inventing new ones.
100
+
101
+ ## Build the block
102
+
103
+ Recheck anchors by path and symbol before editing. Record meaningful drift or invalidated assumptions.
104
+ Reuse components, rules and endpoints. Respect rejected alternatives, exclusions and shared invariants.
105
+ Make routine technical decisions within agreed behavior.
106
+ Iterate with the fastest feedback loop the operating guide documents (incremental watch/serve, an
107
+ open browser session) and keep it running between edits. Reserve full builds and full probe runs
108
+ for the evidence the criteria require, once the code is stable. Send the full output of test suites
109
+ and builds to a log file and read only the summary and the failures; open the log around a failure
110
+ when its context is needed. A full log read into the conversation stays in context and is paid
111
+ again at every later step.
112
+
113
+ When something needs the user, ask at once in chat, in the format of the blocker checklist's Asking
114
+ section, and keep working on the parts it does not block. Once answered, record the answer and
115
+ continue in this session. If nothing else can proceed and the turn ends waiting, first checkpoint
116
+ STATE with progress, unrecorded evidence, running services and the open question, so a cleared or
117
+ interrupted conversation can resume from disk. Every change in scope stays visible in PLAN and STATE:
118
+
119
+ - **Blocker found late:** ask as above; record the answer where it applies and in STATE.
120
+ - **Split:** dividing a point needs no question when it adds no user action and no permission;
121
+ record it in PLAN. A part that needs either is a new point.
122
+ - **New point for the same goal:** run the blocker hunt for that point only, in chat. Edit the
123
+ handoff point in place, moving the replaced text to STATE-HISTORY.md, update PLAN and the block
124
+ map, then continue.
125
+ - **New goal:** a request that adds a goal instead of finishing a point belongs to a new task.
126
+ Propose closing or pausing this task, then use denisco-interactive-spec-definition for the new one.
127
+ - **Permissions:** act within the handoff's permission list; ask before any other action and before
128
+ every always-ask action in the blocker checklist.
129
+ - **Recurring tool permission:** the first time a command needs a tool permission or sandbox
130
+ escalation that the project does not grant yet, propose a permanent project entry: command
131
+ pattern, settings file and reason. After the user's yes, add it and record it in the operating
132
+ guide. Permission settings change only with that yes.
133
+
134
+ An excluded point elsewhere stops this block only if it is a dependency.
135
+
136
+ ## Verify by requirement
137
+
138
+ Run the mandatory project cycle and assigned acceptance criteria. Use the operating guide for
139
+ commands, environment, login, roles and data. Exercise the motivating edge case and related consumers.
140
+
141
+ - Technical behavior: focused tests, type/build checks or API checks.
142
+ - UI interaction: walk the flow and inspect effects and console after interaction. Explore
143
+ interactively when a browser tool is available and script only the final evidence. Before
144
+ rerunning a failed browser check, read its captured console and network errors; once a backend
145
+ failure is confirmed, use an authorized fallback or report the outage instead of retrying.
146
+ - Numeric visual requirements and parity with an existing element: measure resolved values and
147
+ rendered geometry in the browser.
148
+ - Perceptual requirements: use `denisco-visual-check` for the stated comparisons when available.
149
+
150
+ When criteria use denisco-visual-check, the implementation owner loads the skill and performs measurements/captures.
151
+ For a coordinated programmer, apply the bounded contract's visual roles and native measurement
152
+ attribution; the following default describes direct execution and separately delegated visual checks.
153
+ New sidecar entries use executor "principal". Delegate the whole check only when the user explicitly
154
+ requests it, recording executor "subagent". Keep existing historical attempts; an old delegated map
155
+ is not fresh user authorization to launch a child. Reconcile the future assignment with the current
156
+ request and preserve previous results. The only automatic visual delegation is a fresh blind judge
157
+ for perceptual criteria when available and permitted by the environment.
158
+ With measurement enabled, register visual-start with the explicit executor before the attempt,
159
+ refresh/register that executor's native log, then visual-finish with results and evidence. Register
160
+ the blind judge separately for usage; it does not replace the denisco-visual-check executor or load its skill.
161
+ For an explicitly delegated check, give the child an exclusive attempt directory, prohibit source,
162
+ PLAN/STATE, sidecar, register and shared-tooling writes, and merge returned index rows serially.
163
+ Finish pre-edit capture before source edits and keep source/tooling stable during post-edit capture.
164
+ Apply fixes after capture, then recheck affected criteria. Follow denisco-visual-check's resource cleanup
165
+ contract on success, failure and cancellation; return/close children before releasing ownership.
166
+
167
+ A UI file change alone does not require a blind judge. Mockups never verify implementation.
168
+ A criterion whose verifier is the user passes on the user's report, recorded as such; a waived
169
+ criterion stays listed with its reason. If a required check cannot run, complete the independent
170
+ checks and ask once: waive it with a reason, rewrite the criterion, or let the user verify it.
171
+ A perceptual criterion still failing after denisco-visual-check's single correction round gets the same
172
+ question, with the images and measurements.
173
+ Until answered the point is **implemented-unverified**. Rerun a failed check in a later session
174
+ only when a relevant fact has changed. Do not remove the criterion or claim weaker evidence proves it.
175
+ Failed measurements prevent visual sign-off, not reporting or independent diagnostics.
176
+
177
+ ## Run until a real stop
178
+
179
+ When a block is finished (verified, or implemented-unverified with its gaps named), update STATE and
180
+ continue with the next dependency-ready block in this conversation within the explicit assignment.
181
+ Stop when:
182
+
183
+ 1. the task's **Done when** is met;
184
+ 2. the remaining work waits on something outside this conversation: a your-action item not yet
185
+ done, a third-party review, propagation, or a check that needs time to pass;
186
+ 3. the conversation has grown too long to keep track of reliably;
187
+ 4. the user says stop.
188
+ 5. the explicitly bounded assignment ends, or its programmer checkpoints early for context pressure,
189
+ a blocker or an explicit limit and returns control under the bounded implementation contract.
190
+
191
+ "From B1" remains a starting point, not a one-block boundary. "Only B1-B2, then return" is bounded.
192
+ Do not start B3 after that boundary, even if it is ready. A worker does not enlarge its own assignment.
193
+
194
+ A question the user answers in this conversation is not a stop. Before stopping, persist and hand over.
195
+
196
+ ## Persist and hand over
197
+
198
+ Follow [the plan and state contract](references/session-plan.md): STATE is the current picture,
199
+ STATE-HISTORY.md receives each session's entry, PLAN is the block map. Record implementation status,
200
+ criterion results/evidence, blockers, structural changes and meaningful drift per point.
201
+ Preserve failures and reopenings. A verification gap is not a completed point.
202
+ Never label a point/block `verified except ...` or `complete and verified` while a required
203
+ criterion is unavailable. With finished code use `implemented-unverified` and name the gaps.
204
+ Report dependency readiness separately: explain why an unresolved check does or does not prevent
205
+ the next block; neither silently declare it ready nor block unrelated work.
206
+
207
+ End every session's final message with **Your actions**, the same list as in STATE, or "Nothing
208
+ needed from you."
209
+
210
+ When the session stops with task work remaining, save the short next prompt in STATE.md for the
211
+ next dependency-ready block or the unfinished one; a later user request or an already authorized
212
+ coordinator launches the next assignment. The current programmer never launches its successor. A
213
+ user stop with work remaining is a pause: save the prompt as for any other stop and record the stop
214
+ in STATE; the stop holds because only a later user request sends the prompt. Omit or clear it only
215
+ when the user ends the task or asks for no prompt, recording the reason. Keep environment facts in the
216
+ operating guide and evidence paths in STATE. Prompts relay decisions, never invent them.
217
+ Include native session registration when tracking is active.
218
+ When enabled, use denisco-workflow-benchmark to record actual evidence and an identifiable native session.
219
+ After agent-managed registration and once the block map exists, use `open --handoff <file>
220
+ --block <id>` to refresh registered usage and inspect the assignment. Its legacy identity hints
221
+ are internal; `track` handles identification and registration together.
222
+ Planning and implementation in one session count once. After each block's final validation and
223
+ evidence registration, update STATE and run the read-only `checkpoint` for that block. When the
224
+ session stops, use `next-prompt --save` for the proposed next assignment and run `checkpoint` for
225
+ the last block worked with `--next-block` naming that continuation; reconcile both technical status
226
+ and prompt delivery with STATE and the final response. A resume may assign the same unfinished
227
+ block. Missing identity stays pending, never guessed or counted as zero.
228
+ With measurement disabled/unknown, save and reread the prompt manually without loading or installing
229
+ denisco-workflow-benchmark. Automatic benchmark prompt checking is intentionally unavailable in that mode.
230
+ In Plan Mode propose the prompt and leave persistence pending; never claim it was saved.
231
+ Link the saved STATE section in the response; any prompt repeated in chat must be the saved text.
232
+ Further code/evidence changes require refreshed results and another checkpoint before closing.
233
+ `close --handoff <file> --block <id> --implementation <state> (--next-block <id>|--final)` performs
234
+ the save and the checkpoint in one step and refreshes usage; the separate commands stay equivalent.
235
+ Prefer `--ticket <ticket>` from the same conversation over repeating a native session key.
236
+ Live usage remains provisional. The agent refreshes it with `collect` and generates `report` at
237
+ closure; the next entry refreshes again. The last conversation's trailing usage remains pending
238
+ until an observer can refresh its closed log. Never require user-managed IDs or attest closure
239
+ just because another conversation has started.
240
+
241
+ When all blocks are implemented, check cross-block integration and complete acceptance.
242
+ In coordinated mode the coordinator assigns this to a fresh integration worker; an implementation
243
+ worker records that pending step and returns without exceeding its boundary or claiming task completion.
244
+ When no task work remains, or the user ends the task or asks for no prompt, use `next-prompt --clear` to remove the obsolete generated
245
+ section (or remove the old prompt manually without a helper), and checkpoint without `--next-block`.
246
+ Preserve pending verification, accounting and acceptance in STATE; clearing is not proof of completion.
247
+ Reconcile PLAN/STATE current status with the latest authorization, worktree and checkpoint;
248
+ move obsolete planning-only claims to STATE-HISTORY.md. Report passed, failed and
249
+ unavailable checks, pre-existing issues and evidence locations.
250
+ Technical verification permits `verified`; user acceptance/reopening is recorded separately,
251
+ never inferred from the agent's closing message.
252
+ After children have returned and all validation, state and reporting writes are finished, release
253
+ the execution claim. A conflict never resolves merely because the other worktree files went quiet;
254
+ use the explicit transfer/recovery procedure in the ownership contract.
@@ -0,0 +1,71 @@
1
+ # Blocker checklist
2
+
3
+ A **blocker** is anything the agent cannot settle alone that would stop or reshape the work: a fact
4
+ only the user knows, an action only the user or a third party can take, a permission, or a check
5
+ the agent cannot run. The **blocker hunt** finds them before the work they affect starts.
6
+
7
+ ## Probe first
8
+
9
+ Settle by looking whatever looking can settle, then ask about the rest:
10
+
11
+ - Read the official requirements of every external platform the task touches (submission, review,
12
+ publication, authentication, hosting limits). Record the links and each requirement found.
13
+ - Run the project's verification cycle once and record the failures that already exist.
14
+ - Validate a draft configuration or manifest when a validator exists.
15
+ - Check what you can operate here: portals and dashboards, an authenticated browser, GUI clients,
16
+ media files, push and deploy. Every step you cannot operate is a step for the user.
17
+
18
+ ## Categories
19
+
20
+ Name the concrete value for each category, or ask for it. Skip a category only when the task
21
+ cannot touch it.
22
+
23
+ | Category | Settle |
24
+ |---|---|
25
+ | Accounts and credentials | which account, who holds it, where the secret lives (never its value) |
26
+ | Console and portal access | which console, organization and project; who can sign in |
27
+ | Who operates each step | for every portal, GUI or physical step: the agent or the user |
28
+ | Permissions | the permission list below |
29
+ | Environments | staging or production, which domain, which data |
30
+ | Size facts | facts that change how much to build: how many users, existing data, whether data may be lost |
31
+ | Product decisions | choices only the user can make |
32
+ | Third-party waits | reviews, approvals, propagation; expected duration; what the plan does meanwhile |
33
+ | Paid services | which plan, who pays, limits that stop the work |
34
+ | Legal and content | texts, policies and media the user must supply or approve |
35
+ | Checks the agent cannot run | who verifies each one, below |
36
+
37
+ ## Close every item
38
+
39
+ Each item ends in exactly one state:
40
+
41
+ - **decided:** the answer is recorded where it applies;
42
+ - **your action:** the user agreed to do it; the item names the point it unblocks and how the agent
43
+ will check it, and it appears in **Your actions**;
44
+ - **not in this task:** moved out of the task with the user's yes.
45
+
46
+ An item never asked, or recorded as unknown, is still open, and the work that depends on it is not
47
+ ready.
48
+
49
+ ## Permission list
50
+
51
+ Ask once for the actions the agent may take during this task without asking again. Each entry
52
+ names the action, its target and its limit, for example: push to `auth/*` branches; deploy
53
+ `mcp-service`; write to the staging database.
54
+
55
+ These actions always need a fresh yes, even when listed: submitting or publishing to a store or
56
+ portal; DNS and certificates; deleting or migrating production data; spending money; messages to
57
+ real people. Ask before any action outside the list.
58
+
59
+ ## Who verifies
60
+
61
+ Every acceptance criterion names its verifier:
62
+
63
+ - **agent:** the agent runs the check;
64
+ - **user:** the user performs it and reports; the report is the evidence;
65
+ - **waived:** nobody runs it; record the reason and the user's yes.
66
+
67
+ ## Asking
68
+
69
+ Ask in rounds. Number each question and give your recommended answer, so that "yes" accepts it.
70
+ Put every question that is ready into the same round; a question that depends on another answer
71
+ waits for the next round.