@xingular/harness 1.3.1-unstable.9 → 1.4.0-unstable

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 (69) hide show
  1. package/CHANGELOG.md +42 -6
  2. package/NOTICE.md +26 -0
  3. package/builtin/skills/plugin-authoring/SKILL.md +20 -1
  4. package/dist/cli.js +1035 -163
  5. package/dist/sdk/commands/registry.d.ts +7 -0
  6. package/dist/sdk/commands/registry.js +1 -0
  7. package/dist/sdk/plugins/discover.js +80 -4
  8. package/dist/sdk/plugins/install.js +26 -0
  9. package/dist/sdk/plugins/manifest.d.ts +2 -0
  10. package/dist/sdk/plugins/manifest.js +20 -1
  11. package/dist/sdk/prompt/compile.d.ts +4 -3
  12. package/dist/sdk/prompt/compile.js +4 -18
  13. package/dist/sdk/prompt/reporting.d.ts +2 -0
  14. package/dist/sdk/prompt/reporting.js +282 -0
  15. package/dist/sdk/prompt/standard.d.ts +12 -0
  16. package/dist/sdk/prompt/standard.js +53 -2
  17. package/dist/sdk/sessions/completionCheck.d.ts +8 -10
  18. package/dist/sdk/sessions/completionCheck.js +21 -26
  19. package/dist/sdk/sessions/contracts.d.ts +90 -0
  20. package/dist/sdk/sessions/contracts.js +13 -0
  21. package/dist/sdk/sessions/goal.d.ts +145 -0
  22. package/dist/sdk/sessions/goal.js +405 -0
  23. package/dist/sdk/sessions/goalLoop.d.ts +37 -0
  24. package/dist/sdk/sessions/goalLoop.js +125 -0
  25. package/dist/sdk/sessions/goalPrompts.d.ts +4 -0
  26. package/dist/sdk/sessions/goalPrompts.js +93 -0
  27. package/dist/sdk/sessions/runStore.js +1 -0
  28. package/dist/sdk/sessions/runner.d.ts +7 -1
  29. package/dist/sdk/sessions/runner.js +13 -12
  30. package/dist/sdk/sessions/sessions.d.ts +10 -0
  31. package/dist/sdk/sessions/sessions.js +44 -4
  32. package/dist/sdk/sessions/stopGate.d.ts +16 -27
  33. package/dist/sdk/sessions/stopGate.js +32 -60
  34. package/dist/sdk/sessions/workSlice.d.ts +2 -0
  35. package/dist/sdk/sessions/workSlice.js +1 -1
  36. package/dist/sdk/terminal/piExtension.d.ts +2 -1
  37. package/dist/sdk/terminal/piExtension.js +68 -13
  38. package/dist/sdk/terminal/terminals.js +2 -1
  39. package/dist/sdk/terminal/tui/commands.d.ts +2 -0
  40. package/dist/sdk/terminal/tui/commands.js +3 -0
  41. package/dist/sdk/terminal/tui/goalCommand.d.ts +19 -0
  42. package/dist/sdk/terminal/tui/goalCommand.js +83 -0
  43. package/dist/sdk/terminal/tui/runtime.d.ts +3 -0
  44. package/dist/sdk/terminal/tui/runtime.js +7 -1
  45. package/dist/sdk/tools/askUser.js +1 -1
  46. package/dist/sdk/tools/todo.js +2 -1
  47. package/dist/terminal-opentui.js +839 -119
  48. package/dist/terminal-pi.js +387 -80
  49. package/docs/architecture/02-sessions-and-runs.md +28 -20
  50. package/docs/architecture/04-prompt.md +73 -52
  51. package/docs/architecture/06-plugins-and-capabilities.md +16 -1
  52. package/docs/architecture/07-tools-and-gate.md +7 -4
  53. package/docs/architecture/10-server-protocol.md +13 -6
  54. package/docs/architecture/11-terminal.md +23 -7
  55. package/docs/decisions/ADR-0008-flexible-reporting-guidance.md +3 -0
  56. package/docs/decisions/ADR-0068-scope-and-completion-report.md +5 -2
  57. package/docs/decisions/ADR-0071-completion-check.md +3 -2
  58. package/docs/decisions/ADR-0072-codex-prompts-and-goal.md +105 -0
  59. package/docs/decisions/ADR-0073-large-reporting-prompt.md +84 -0
  60. package/docs/parity.md +4 -1
  61. package/docs/releases/1.4.0-unstable.md +18 -0
  62. package/docs/releases/README.md +1 -1
  63. package/docs/study/00-index.md +7 -0
  64. package/docs/study/STUDY-0004-turn-end-controls.md +2 -0
  65. package/docs/study/STUDY-0005-skill-installation-guidance.md +146 -0
  66. package/docs/study/STUDY-0006-reporting-prompts.md +81 -0
  67. package/docs/study/arms/04-skills.md +6 -0
  68. package/docs/terminal-tui.md +17 -7
  69. package/package.json +1 -1
@@ -30,6 +30,13 @@ export declare const CORE_COMMANDS: readonly [{
30
30
  readonly group: "Run";
31
31
  readonly kind: "session";
32
32
  readonly methods: readonly ["sessions.run"];
33
+ }, {
34
+ readonly name: "goal";
35
+ readonly args: "[<objective>|edit|pause|resume|clear]";
36
+ readonly description: "Set or view a goal for a long-running task: /goal <objective>";
37
+ readonly group: "Run";
38
+ readonly kind: "session";
39
+ readonly methods: readonly ["sessions.run"];
33
40
  }, {
34
41
  readonly name: "btw";
35
42
  readonly aliases: readonly ["side"];
@@ -4,6 +4,7 @@
4
4
  export const CORE_COMMANDS = [
5
5
  { name: 'help', description: 'List the commands', group: 'Core', kind: 'local' },
6
6
  { name: 'plan', args: '[brief]', description: 'Plan one request, read-only: /plan <brief>', group: 'Run', kind: 'session', methods: ['sessions.run'] },
7
+ { name: 'goal', args: '[<objective>|edit|pause|resume|clear]', description: 'Set or view a goal for a long-running task: /goal <objective>', group: 'Run', kind: 'session', methods: ['sessions.run'] },
7
8
  { name: 'btw', aliases: ['side'], args: '[question]', description: 'Ask a side question without interrupting: /btw <question>', group: 'Run', kind: 'session', methods: ['sideQuestions.start', 'sideQuestions.cancel'] },
8
9
  { name: 'new', description: 'Start a new session', group: 'Session', kind: 'session' },
9
10
  { name: 'stop', description: 'Stop the current reply', group: 'Run', kind: 'session', methods: ['runs.interrupt'] },
@@ -2,7 +2,7 @@
2
2
  // rules. Resolving reads folders and manifests; it never runs an extension.
3
3
  import { existsSync, lstatSync, readdirSync, readFileSync } from 'node:fs';
4
4
  import { homedir } from 'node:os';
5
- import { join, sep } from 'node:path';
5
+ import { basename, join, sep } from 'node:path';
6
6
  import { log } from "../core/bus.js";
7
7
  import * as frontmatter from "../core/frontmatter.js";
8
8
  import { loadPi } from "../pi/runtime.js";
@@ -46,6 +46,50 @@ export function packageProfiles(root, folder) {
46
46
  return profiles;
47
47
  }
48
48
  const inside = (path, parent) => path === parent || path.startsWith(parent.endsWith(sep) ? parent : `${parent}${sep}`);
49
+ /** At most this many skills are read from one package's foreign manifest. */
50
+ const MAX_MANIFEST_SKILLS = 100;
51
+ /**
52
+ * The skills a package's foreign manifest declares: a folder with a `SKILL.md`, a folder of skill
53
+ * folders, or a `SKILL.md` file. Paths are already relative and inside the package.
54
+ */
55
+ function declaredSkillFiles(root, declared) {
56
+ const found = new Set();
57
+ const visit = (path, depth) => {
58
+ if (found.size >= MAX_MANIFEST_SKILLS)
59
+ return;
60
+ let stat;
61
+ try {
62
+ stat = lstatSync(path);
63
+ }
64
+ catch {
65
+ return;
66
+ }
67
+ if (stat.isSymbolicLink())
68
+ return;
69
+ if (stat.isFile()) {
70
+ if (basename(path) === 'SKILL.md')
71
+ found.add(path);
72
+ return;
73
+ }
74
+ if (!stat.isDirectory() || depth > 2)
75
+ return;
76
+ const own = join(path, 'SKILL.md');
77
+ try {
78
+ if (lstatSync(own).isFile()) {
79
+ found.add(own);
80
+ return;
81
+ }
82
+ }
83
+ catch {
84
+ // A folder without its own SKILL.md lists the skills inside it.
85
+ }
86
+ for (const name of readdirSync(path).sort())
87
+ visit(join(path, name), depth + 1);
88
+ };
89
+ for (const path of declared)
90
+ visit(join(root, path), 0);
91
+ return [...found];
92
+ }
49
93
  function origin(path, metadata) {
50
94
  if (path.startsWith(BUILTIN_PREFIX))
51
95
  return 'builtin';
@@ -122,11 +166,24 @@ export const piResolver = async ({ cwd, agentDir, install, packages }) => {
122
166
  // A package may name a hooks file for the harness. Only a package a person installed counts:
123
167
  // a working folder's package cannot put code into the harness's own process.
124
168
  const roots = new Map();
125
- for (const configured of manager().listConfiguredPackages())
126
- if (configured.scope === 'user' && configured.installedPath)
169
+ // A foreign manifest's skills are text, so a project's package may bring them too.
170
+ const skillRoots = new Map();
171
+ const filtered = new Set();
172
+ for (const entry of [...(settings.getGlobalSettings().packages ?? []), ...(settings.getProjectSettings().packages ?? [])]) {
173
+ if (entry && typeof entry === 'object' && Array.isArray(entry.skills))
174
+ filtered.add(entry.source);
175
+ }
176
+ for (const configured of manager().listConfiguredPackages()) {
177
+ if (!configured.installedPath)
178
+ continue;
179
+ if (configured.scope === 'user')
127
180
  roots.set(configured.installedPath, configured.source);
128
- for (const folder of packages ?? [])
181
+ skillRoots.set(configured.installedPath, { source: configured.source, project: configured.scope === 'project' });
182
+ }
183
+ for (const folder of packages ?? []) {
129
184
  roots.set(folder, folder);
185
+ skillRoots.set(folder, { source: folder, project: false });
186
+ }
130
187
  for (const [root, source] of roots) {
131
188
  const manifest = packageManifest(root);
132
189
  for (const path of packageProfiles(root, manifest.agents))
@@ -141,6 +198,25 @@ export const piResolver = async ({ cwd, agentDir, install, packages }) => {
141
198
  if (fallback !== -1)
142
199
  found.splice(fallback, 1);
143
200
  }
201
+ // Skills a foreign manifest declares load with the package, under its own switch. A package
202
+ // whose settings entry names skill patterns has expressed a filter, so nothing is added there.
203
+ for (const [root, meta] of skillRoots) {
204
+ if (filtered.has(meta.source))
205
+ continue;
206
+ let added = false;
207
+ for (const path of declaredSkillFiles(root, packageManifest(root).skills)) {
208
+ if (found.some((item) => item.kind === 'skill' && item.path === path))
209
+ continue;
210
+ found.push({ kind: 'skill', path, origin: 'package', project: meta.project, packageRoot: root, source: meta.source, enabled: true });
211
+ added = true;
212
+ }
213
+ if (!added)
214
+ continue;
215
+ // The package has resources of its own, so it is not itself an extension.
216
+ const fallback = found.findIndex((item) => item.kind === 'extension' && item.path === root);
217
+ if (fallback !== -1)
218
+ found.splice(fallback, 1);
219
+ }
144
220
  // Pi reads these itself, whatever else is switched off. They are text, so they are only listed.
145
221
  const context = [
146
222
  ...pi.loadProjectContextFiles({ cwd, agentDir }).map((file) => file.path),
@@ -12,11 +12,37 @@ import { loadPi } from "../pi/runtime.js";
12
12
  import { ensureAdopted, list } from "./api.js";
13
13
  import * as attachments from "./attachments.js";
14
14
  import { withoutInstallScripts } from "./discover.js";
15
+ /**
16
+ * A code-hosting page for a folder or file inside a repository. Pi installs a repository, so a
17
+ * pasted `tree` or `blob` address must not reach the package manager as one.
18
+ */
19
+ function repositoryPage(source) {
20
+ let url;
21
+ try {
22
+ url = new URL(source);
23
+ }
24
+ catch {
25
+ return null;
26
+ }
27
+ if (url.protocol !== 'https:' && url.protocol !== 'http:')
28
+ return null;
29
+ const host = url.hostname.replace(/^www\./, '');
30
+ const parts = url.pathname.split('/').filter(Boolean);
31
+ const repository = () => `${url.protocol}//${host}/${parts[0]}/${parts[1]}`;
32
+ if (host === 'github.com' && parts.length >= 4 && (parts[2] === 'tree' || parts[2] === 'blob'))
33
+ return repository();
34
+ if (host === 'gitlab.com' && parts.length >= 5 && parts[2] === '-' && (parts[3] === 'tree' || parts[3] === 'blob'))
35
+ return repository();
36
+ return null;
37
+ }
15
38
  /** A folder on this machine, or a package as Pi's settings name it. A bare name is an npm package. */
16
39
  export function normalizeSource(input) {
17
40
  const source = input.trim();
18
41
  if (!source)
19
42
  throw invalid('Name a package: a folder, an npm name, or a git address.');
43
+ const page = repositoryPage(source);
44
+ if (page)
45
+ throw invalid(`That address is a page inside a repository, not a package. Install ${page} as a package, or copy its skill folder into <project>/.agents/skills/ for one project or ~/.agents/skills/ for every project.`);
20
46
  if (source.startsWith('~/'))
21
47
  return join(process.env.HOME || homedir(), source.slice(2));
22
48
  if (isAbsolute(source))
@@ -13,6 +13,8 @@ export interface PackageManifest {
13
13
  hooks: string | null;
14
14
  /** The folder of subagent profiles, inside the package. `agents` unless the manifest names another. */
15
15
  agents: string;
16
+ /** Skill folders a foreign manifest declares, relative to the package root. */
17
+ skills: string[];
16
18
  /** Data-only remote connector forms. Invalid templates are ignored; none can carry credentials. */
17
19
  connectorTemplates: ConnectorTemplate[];
18
20
  }
@@ -35,7 +35,7 @@ function inside(path) {
35
35
  if (typeof path !== 'string' || !path.trim())
36
36
  return null;
37
37
  try {
38
- return safeRelative(path.replace(/^\.\//, ''));
38
+ return safeRelative(path.replace(/^\.\//, '').replace(/\/+$/, ''));
39
39
  }
40
40
  catch {
41
41
  return null;
@@ -52,6 +52,24 @@ function pluginJson(packageRoot) {
52
52
  }
53
53
  return {};
54
54
  }
55
+ /**
56
+ * The skill folders a foreign plugin manifest declares. Pi reads only a package's own `skills`
57
+ * folder and its `package.json` manifest, so a Claude- or Codex-layout package otherwise installs
58
+ * and contributes nothing.
59
+ */
60
+ function declaredSkills(packageRoot) {
61
+ for (const file of PLUGIN_MANIFESTS) {
62
+ const found = readJsonOr(join(packageRoot, file), {});
63
+ const listed = typeof found.skills === 'string' ? [found.skills] : Array.isArray(found.skills) ? found.skills : [];
64
+ const skills = listed.flatMap((entry) => {
65
+ const path = inside(entry);
66
+ return path ? [path] : [];
67
+ });
68
+ if (skills.length)
69
+ return skills;
70
+ }
71
+ return [];
72
+ }
55
73
  export function packageManifest(packageRoot) {
56
74
  const manifest = readJsonOr(join(packageRoot, 'package.json'), {});
57
75
  // Only a package without a name of its own is described by its plugin.json.
@@ -67,6 +85,7 @@ export function packageManifest(packageRoot) {
67
85
  defaultPrompt: typeof own.defaultPrompt === 'string' && /^[a-z][a-z0-9-]*$/.test(own.defaultPrompt) ? own.defaultPrompt : null,
68
86
  hooks: inside(own.hooks),
69
87
  agents: inside(own.agents) ?? 'agents',
88
+ skills: declaredSkills(packageRoot),
70
89
  connectorTemplates: (Array.isArray(own.connectors) ? own.connectors : []).flatMap((value) => {
71
90
  const parsed = connectorTemplateSchema.safeParse(value);
72
91
  return parsed.success ? [parsed.data] : [];
@@ -5,6 +5,7 @@ import { type SkillFile } from "../tools/defaultSkills.js";
5
5
  import type { TodoItem } from "../sessions/contracts.js";
6
6
  import type { SessionState } from "../sessions/sessionState.js";
7
7
  import { type StandardSession } from "./standard.js";
8
+ import { REPORTING_GUIDANCE } from "./reporting.js";
8
9
  export declare const REQUIRED_SKILLS_BUDGET = 12000;
9
10
  export declare const REFERENCES_BUDGET = 12000;
10
11
  /** An approval is a control the person clicks; the model never asks for permission in a message. */
@@ -152,10 +153,10 @@ export interface Launch {
152
153
  /** Recorded with the pack: the application's name for who it is for. Defaults to `agent` or `session`. */
153
154
  profile?: string;
154
155
  }
155
- /** What a person's own session is told about how the end of a reply is checked (ADR-0071). */
156
- export declare const STOP_GATE_GUIDANCE = "A reply that ends while its todo list has open items, or that a completion check finds unfinished, is sent back to you, so keep working until the request is done. A reply also has a work-slice budget: when it is reached the harness records a checkpoint from your todo list, files and checks, and the work continues from there.";
156
+ /** What a person's own session is told about how the end of a reply is checked (ADR-0071, ADR-0072). */
157
+ export declare const STOP_GATE_GUIDANCE = "A reply that ends while its todo list has open items is sent back to you, so keep working until the request is done. A reply also has a work-slice budget: when it is reached the harness records a checkpoint from your todo list, files and checks, and the work continues from there.";
157
158
  /** How a run's final reply is shaped. Fixed text: it sits in the stable prefix of every pack. */
158
- export declare const REPORTING_GUIDANCE = "When you finish work, write the final reply for a person who did not watch you work. Reporting is for work that is finished or genuinely blocked: do not end a turn to report work you can still do.\n\n- Lead with the outcome, not the steps taken to reach it. If the person asked a question, answer it first.\n- Cover the most important information and use as much structure as the answer needs. Use descriptive headings, lists, or tables when they help explain the outcome; do not force a fixed set of report headings apart from the two named below. Keep explanations concise and include technical detail where it helps.\n- When work involves files, identify the relevant paths and briefly state what changed or mattered. Use `path:line` references where useful, and a table (File | Change | Why) when there are three or more changed files.\n- Include blockers, risks, or next steps only when they are concrete and relevant.\n- When the work stopped for the person, state the concrete thing they must do or decide for each item, and its owner; do not merely restate the blocker or invent a human decision.\n- End the reply with `## Beyond what you asked` when you did anything beyond the request: name each piece and say why the work needed it.\n- End the reply with `## What would make this complete` only when the work stopped short \u2014 it needs something only the person can do, or the work-slice guard handed it back: list the concrete remaining steps in order, and name the ones only the person can do. Never stop early in order to write this section.\n- Use **Need human inspection** only when a concrete verification requires a person because you could not perform or observe it. State what remains unverified, why you could not verify it, and the specific check the person should make. Do not use this for checks you ran or generic risks.\n- Name only checks actually run and their results; never claim a check you did not run.\n- Use a Markdown table when comparing options, listing several items with the same fields, or reporting the results of several checks. Use prose or bullets otherwise.\n- A one-line question gets a one-line answer.\n- If the person, the application, or the current mode asks for a different format, follow that.\n";
159
+ export { REPORTING_GUIDANCE };
159
160
  export declare const HISTORY_GROUP: string[];
160
161
  export declare const WIKI_GROUP: string[];
161
162
  export declare const NATIVE_GROUP: string[];
@@ -17,10 +17,10 @@ import { isWorldTool, WORLD_GUIDANCE } from "../world/untrusted.js";
17
17
  import { TODO_GUIDANCE, TODO_WRITE, describeTodo } from "../tools/todo.js";
18
18
  import { SESSION_GUIDANCE, SESSION_NAME } from "../tools/sessionName.js";
19
19
  import { ANNOTATIONS_GUIDANCE, STANDARD_CONNECTIONS, standardSections } from "./standard.js";
20
+ import { REPORTING_BUDGET, REPORTING_GUIDANCE } from "./reporting.js";
20
21
  const IDENTITY_BUDGET = 1000;
21
22
  const WIKI_BUDGET = 2500;
22
23
  const TOOLS_BUDGET = 8000;
23
- const REPORTING_BUDGET = 2400;
24
24
  const SESSION_STATE_BUDGET = 1_000;
25
25
  export const REQUIRED_SKILLS_BUDGET = 12_000;
26
26
  const REQUIRED_SKILLS_POINTER = 'Some required skill text was omitted to fit. Load the rest with skill_load.';
@@ -32,24 +32,10 @@ const NOTES_LISTED = 25;
32
32
  export const APPROVAL_GUIDANCE = 'Approvals: when a call needs a person, the Harness pauses that exact call and shows them Allow and Deny. Never ask for permission in a message, never end your turn to wait for an answer, and never call a task blocked on approval before trying: a denial arrives as the tool result, and you continue from it.';
33
33
  /** Where the harness's own sections sit; an application's sections are placed among them by `order`. */
34
34
  export const SECTION_ORDER = { harnessIdentity: 5, system: 25, sessionState: 30, guardrails: 40, memory: 45, references: 50, requiredSkills: 52, subagents: 64, scripts: 66, wiki: 80, tools: 90, reporting: 95 };
35
- /** What a person's own session is told about how the end of a reply is checked (ADR-0071). */
36
- export const STOP_GATE_GUIDANCE = 'A reply that ends while its todo list has open items, or that a completion check finds unfinished, is sent back to you, so keep working until the request is done. A reply also has a work-slice budget: when it is reached the harness records a checkpoint from your todo list, files and checks, and the work continues from there.';
35
+ /** What a person's own session is told about how the end of a reply is checked (ADR-0071, ADR-0072). */
36
+ export const STOP_GATE_GUIDANCE = 'A reply that ends while its todo list has open items is sent back to you, so keep working until the request is done. A reply also has a work-slice budget: when it is reached the harness records a checkpoint from your todo list, files and checks, and the work continues from there.';
37
37
  /** How a run's final reply is shaped. Fixed text: it sits in the stable prefix of every pack. */
38
- export const REPORTING_GUIDANCE = `When you finish work, write the final reply for a person who did not watch you work. Reporting is for work that is finished or genuinely blocked: do not end a turn to report work you can still do.
39
-
40
- - Lead with the outcome, not the steps taken to reach it. If the person asked a question, answer it first.
41
- - Cover the most important information and use as much structure as the answer needs. Use descriptive headings, lists, or tables when they help explain the outcome; do not force a fixed set of report headings apart from the two named below. Keep explanations concise and include technical detail where it helps.
42
- - When work involves files, identify the relevant paths and briefly state what changed or mattered. Use \`path:line\` references where useful, and a table (File | Change | Why) when there are three or more changed files.
43
- - Include blockers, risks, or next steps only when they are concrete and relevant.
44
- - When the work stopped for the person, state the concrete thing they must do or decide for each item, and its owner; do not merely restate the blocker or invent a human decision.
45
- - End the reply with \`## Beyond what you asked\` when you did anything beyond the request: name each piece and say why the work needed it.
46
- - End the reply with \`## What would make this complete\` only when the work stopped short — it needs something only the person can do, or the work-slice guard handed it back: list the concrete remaining steps in order, and name the ones only the person can do. Never stop early in order to write this section.
47
- - Use **Need human inspection** only when a concrete verification requires a person because you could not perform or observe it. State what remains unverified, why you could not verify it, and the specific check the person should make. Do not use this for checks you ran or generic risks.
48
- - Name only checks actually run and their results; never claim a check you did not run.
49
- - Use a Markdown table when comparing options, listing several items with the same fields, or reporting the results of several checks. Use prose or bullets otherwise.
50
- - A one-line question gets a one-line answer.
51
- - If the person, the application, or the current mode asks for a different format, follow that.
52
- `;
38
+ export { REPORTING_GUIDANCE };
53
39
  export const HISTORY_GROUP = ['history_search', 'history_read', 'trajectory_search', 'trajectory_read'];
54
40
  export const WIKI_GROUP = ['wiki_search', 'wiki_read', 'wiki_list', 'wiki_write'];
55
41
  export const NATIVE_GROUP = ['read', 'grep', 'find', 'ls', 'edit', 'write', 'bash'];
@@ -0,0 +1,2 @@
1
+ export declare const REPORTING_GUIDANCE = "When you finish work, write the final reply for a person who did not watch you work. They have not seen your tool calls, the files you read, or the output you saw. The reply is the whole report: it must stand alone. Reporting is for work that is finished or genuinely blocked. Do not end a turn to report work you can still do.\n\n### What the reply is for\n\nThe person reads the reply to decide what to do next. Give them, in this order: what happened, what changed, how to try it, and what they should do. Everything else is optional.\n\nWrite for a competent colleague who was not in the session. Do not assume they know the plan, the files, the commands, or the attempts that failed. If they would have to ask a question to act on the reply, the reply is not finished.\n\nThe reply is not a transcript. Leave out what you tried and abandoned, the tool calls, your internal reasoning, and code the person can open. Keep what changes their decision or their next step. A person often reads only the first line and the test block, so both must stand alone.\n\n### The first line\n\n- State the outcome, not the activity. Write \"The importer now handles quoted fields; all 12 tests pass\", not \"I worked on the importer\".\n- Say whether the work is done. If it is unfinished, say so in the first line, then give the specific reason and the next step: \"Not finished: the API key is missing, so the sync path is untested. Everything else is in place.\"\n- If the person asked a question, answer it in the first line, then support the answer.\n- No preamble (\"Great question\", \"Let me explain\"), no restating the request, no closing recap of what you already said.\n\n### Choose the shape\n\n| The task was | The shape |\n|---|---|\n| A change to code, files, configuration, a capability, a setting or a document the person will use | Outcome, what changed, how to test, what to do next. These four are required. |\n| An answer or research with no files changed | Outcome, then the findings with sources. Add a test block only if the person can run something. Add a next step only if one follows. |\n| A question | One to three sentences and no headings. |\n| A blocked or unfinished change | First line: unfinished and why. Then what is done, what is blocked and who decides, how to test what exists, and the next step. |\n\nA change means the person can do something different after it. If the only change is a file they will never run or read, say what it is for and how to inspect it.\n\n### What to leave out\n\n- Your narration of the work: which tools you called, which files you opened, which attempt failed first.\n- Code the person can open. Quote a line only when the exact text is the point.\n- Praise, apologies and remarks about the reply itself.\n- Speculative risks with no action. A real risk belongs in the unverified block with a check.\n- The plan and the request restated. The person wrote them.\n\n### Required blocks for a change\n\n#### Outcome\n\nOne or two sentences. Done or unfinished, and the effect in plain words. Name the capability, not the file: \"The tree view now loads children on demand\", not \"discover.ts changed\".\n\n#### What changed\n\n- Name the relevant paths and say what changed in each and why. Link a file when the person will open it, for example `src/plugins/install.ts`.\n- Use a table with the columns File, Change, Why when three or more files changed. Use bullets for one or two.\n- Group related changes. Do not paste diffs, whole functions or file contents the person can open.\n- Mention a deleted, renamed or moved file explicitly, and why.\n- If the change is invisible in the product yet, say that: infrastructure, a migration, a guard, a test seam.\n- If nothing changed, say so plainly and say why the request needed no change.\n\n#### How to test\n\nThis block is the one the person will follow. Make it runnable without reading the rest of the reply.\n\n- Number the steps in the order the person performs them.\n- Give the exact command, the exact URL, or the exact UI path. A step that says \"run the app\" is not a step; \"run `npm run dev` and open http://localhost:5173\" is.\n- Say what the person should see after each step. \"The list shows three rows, newest first\" is a result; \"it works\" is not.\n- Say what you already ran and what it returned, so the person does not repeat it.\n- Say which step the person must do that you could not, and why you could not do it.\n- Say what is needed before the test: an account, a key, a fixture, a running service, a browser. If it is missing, give the fallback.\n- If the change has no user-visible effect, write one line: \"Nothing to run: the unit tests cover it\" or \"Nothing to run: the change is in the build, not the product\", and name the check that covers it.\n- Keep the steps in the report, not in a separate file.\n\nWhen several checks share fields, a table is shorter than a list:\n\n | Check | Command | Expected |\n |---|---|---|\n | Unit | `npm test` | 41 passed, 0 failed |\n | Types | `npm run typecheck` | no output |\n\n#### What to do next\n\n- State the person's next action, or say \"Nothing needed\" when the work is complete.\n- If a decision is needed, name the options and your recommendation first, then the choice the person owns.\n- If a follow-up is needed, name who does it and when. Do not assign work to the person without saying why it is theirs.\n- If the next step depends on a missing secret, account, remote data or approval, say exactly what is needed; do not ask for it as a question at the end of the report.\n- Do not use this block to offer optional extras the person did not ask for.\n\n### Optional blocks\n\nInclude a block only when it has content. Never add a heading and leave it empty.\n\n#### Beyond the request\n\nWhen you did something the request did not name, list each piece and why the work needed it. Examples: a test that would have caught the bug, a fix to the adjacent broken call, a doc line that would otherwise be wrong. This block makes the extra work auditable; it is not a place to list routine steps.\n\n#### Departures from the plan\n\nWhen you departed from a plan, a handoff or the steps the request gave, say what you did instead and why, and what effect it has. This includes a skipped step, a reordered step, a replaced tool, a narrower scope or a wider one. Do not hide a departure inside the summary.\n\n#### Unverified or needs a person\n\nWhen a concrete check requires a person because you could not perform or observe it, say what is unverified, why you could not check it, and the exact check to make. Examples: a native dialog, a device, a signed-in account, a visual judgement, a deploy. Use this block for a real check, not for generic risk. If you ran the check, report the result instead; do not call it unverified.\n\n#### Open questions\n\nOnly when a question blocks a decision the person must make. State the question, the options and your recommendation. Do not end the report with an open-ended invitation to ask more.\n\n### Headings are yours\n\nUse headings that name the content. The names in this guidance describe blocks; they are not required wording.\n\n- \"How to test\", \"Test it\", \"Try it\", \"Verify\" and \"Test steps\" all name the same block.\n- \"What changed\", \"Changes\", \"What I changed\" and \"Files\" all name the same block.\n- \"What to do next\", \"Next step\" and \"Over to you\" all name the same block.\n- Order the blocks: outcome, changes, test, next, then the optional blocks. Put the test block before a long list of changes if the person will run it first.\n- Do not use a heading that says nothing: \"Summary\", \"Notes\", \"Worth a look\", \"Additional information\". A heading must tell the reader what they will find.\n- No fixed heading list is required. The requirement is the information, not the label. A short change may need only two headings; a large one may use six.\n- If a block is one sentence, a bold lead-in is enough; a heading is not required.\n\n### Lists and tables\n\n- Bullets for parallel items: files, options, checks.\n- Numbered lists for anything the person performs in order: test steps, next steps, a migration.\n- A Markdown table when several items share fields: File | Change | Why, Check | Command | Result, Option | Cost | Recommendation.\n- Prose for a single point that needs explanation. Do not wrap one sentence in a table.\n- Keep a bullet to one line where possible. If it needs a paragraph, use a paragraph.\n\n### Length\n\n- A change: about 20 to 60 lines. A large change may need more, but the outcome, the test block and the next step come first and stay short.\n- A question: one to three sentences.\n- Research: as long as the evidence needs. Do not pad, and do not repeat the sources in the answer and in a list.\n- A blocked task: as long as the person needs to understand the block and act; usually under 30 lines.\n- Never repeat the request, the plan or the previous message to fill space. The person can see them.\n\nFor a long command output or log, give the first useful line, the last line, and the path to the full output. Do not paste a hundred lines; quote the part that supports the conclusion.\n\n### Honesty\n\n- Name only checks you actually ran, with their results. \"All 12 tests pass\" without the command or the count is a claim, not a report.\n- If a check failed, say so, give the failing output or its first useful lines, and say what it means for the change.\n- If you did not run a check, say \"not run\" and why. Do not imply coverage you do not have.\n- If you could not verify something, put it in the unverified block with the exact check the person should make.\n- If the work is partial, say what is safe to use and what is not.\n- Do not describe a file as working because it compiles. Say what you observed.\n\nA claim becomes a report when it carries its evidence:\n\n | Claim | What makes it a report |\n |---|---|\n | \"All tests pass\" | `npm test`: 41 passed, 0 failed |\n | \"The endpoint works\" | `curl` returned 200 with the expected body |\n | \"The page looks right\" | not a report; name the screen, size and what to look for |\n\n### Tone\n\nWrite directly and concretely. No apologies, no self-congratulation, no filler. Technical detail belongs in the reply when it helps the person act; leave out the reasoning that only mattered while you worked. If the person's language is not English, reply in their language; this guidance describes the shape, not the language.\n\n### Anti-patterns\n\n| Do not | Do instead |\n|---|---|\n| Open with \"I've implemented...\" and no outcome | Say what now works, in the first line |\n| Restate the request before the answer | Drop the restatement; answer first |\n| \"Worth a look\" or \"Notes\" with no action | Name the thing and the action, or drop it |\n| \"All tests pass\" with no command or count | Give the command, the count and the result |\n| No test step for a change | Add numbered steps, or one line saying nothing to run and why |\n| No next step | Say \"Nothing needed\", or name the person's action |\n| A departure hidden in the middle | Give it its own block with the reason |\n| A wall of prose for a multi-file change | Use a table and bullets |\n| A long report for a one-line question | One to three sentences |\n| \"If you want, I can...\" as the ending | Only suggest a follow-up that builds on the request; otherwise stop |\n\n### Examples\n\nThese examples show the shape, not the words. Copy the structure and the level of detail, then write for the task in front of you.\n\n#### Example 1 \u2014 a small change\n\n The settings page now remembers the last selected tab, and the choice survives a reload. Two\n tests cover it and the full suite passes.\n\n ## What changed\n\n - `src/settings/tabs.ts` stores the tab in local storage and restores it on mount.\n - `src/settings/tabs.test.ts` covers the restore and the fallback when storage is empty.\n\n ## How to test\n\n 1. Run `npm run dev` and open http://localhost:5173/settings.\n 2. Select the \"Billing\" tab, then reload the page.\n 3. The Billing tab is selected again. With storage cleared, the page falls back to \"General\".\n\n I ran `npm test`: 41 passed, including the new tests. The reload itself is yours to check; the\n storage path is covered by the unit test.\n\n Nothing needed next: the change is complete.\n\n#### Example 2 \u2014 a multi-file change with extra work\n\n The export job now retries a failed upload twice and records the last error. The retry path is\n covered by a new test; the full suite passes.\n\n ## What changed\n\n | File | Change | Why |\n |---|---|---|\n | `src/export/retry.ts` | Retries an upload twice with a short backoff | A flaky network no longer fails the whole export |\n | `src/export/report.ts` | Stores the last error on the job | The next attempt can show what went wrong |\n | `src/export/retry.test.ts` | New tests for success, retry and give-up | The retry path had no coverage |\n\n ## How to test\n\n 1. Run `npm run test:export`. Expect all tests to pass, including the three retry cases.\n 2. In the app, start an export with the network disconnected after the first step.\n 3. The job shows \"retrying\" once, then the last error instead of a silent failure.\n\n I ran `npm run test:export`: 18 passed. I did not test the disconnected-network case in the app;\n that is the manual step above.\n\n ## Beyond the request\n\n I added the error field to the job record. The retry alone would have hidden the cause, and the\n field is what the next attempt reads.\n\n Nothing needed next.\n\n#### Example 3 \u2014 a blocked or unfinished change\n\n Not finished: the sync path is implemented and tested, but the API key is missing, so I could not\n run it against the live service.\n\n ## What is done\n\n - `src/sync/client.ts` sends the new payload and retries once on a 5xx.\n - `src/sync/client.test.ts` covers the payload and the retry; 9 tests pass.\n\n ## How to test what exists\n\n 1. Run `npm test -- sync`. Expect 9 passing tests.\n 2. Add `SYNC_API_KEY` to `.env`, then run `npm run sync:check`.\n 3. Expect \"sync ok\" and one record in the staging table.\n\n I could not run step 2 or 3: the key is not in this environment, and I did not have the\n account. Do not use this against production until step 3 passes.\n\n ## What to do next\n\n Add `SYNC_API_KEY` to `.env.local` and run `npm run sync:check`. If it fails on auth, the\n key belongs to the staging workspace, not the sandbox one. Nothing else is blocked.\n\n#### Example 4 \u2014 a research answer\n\n The harness already reads Claude-layout skills from a package once its manifest declares them,\n but it does not scan `.claude/skills` directly.\n\n ## What I found\n\n - The resolver scans `.pi/skills`, `.agents/skills` and package `skills/` folders; it never\n reads a `.claude` folder.\n - A package can declare its skill folder in `.claude-plugin/plugin.json`; the harness reads\n that path and adds the folder as a package skill.\n\n Sources: `src/plugins/discover.ts` (the resolver) and `src/plugins/manifest.ts` (the\n manifest).\n\n Next step: if you want a pasted repository to contribute its skills, install the repository\n itself rather than a page URL.\n\n#### Example 5 \u2014 a one-line question\n\n It reads both: `plugin.json` first, then `.claude-plugin/plugin.json`, and the first one with\n a `skills` field wins.\n\n### Before you send\n\n- The first line says what happened and whether it is done.\n- A change has a what-changed block, a how-to-test block and a what-to-do-next block.\n- Paths are named; a table holds three or more files.\n- Every check you name was actually run, with its result.\n- A departure or extra piece of work has its own block.\n- Nothing is repeated from the request or the plan.\n- The headings fit the content; none is empty.\n- A person can act from the test block alone.\n- Nothing is claimed that the record does not support.\n";
2
+ export declare const REPORTING_BUDGET = 18000;
@@ -0,0 +1,282 @@
1
+ // How a run's final reply is shaped. Fixed text: it sits in the stable prefix of every pack, so it
2
+ // never carries state (INV-PRM-2). ADR-0073 replaces the short fixed-heading guidance with content
3
+ // blocks, required test and next-step blocks for changes, and worked examples.
4
+ export const REPORTING_GUIDANCE = `When you finish work, write the final reply for a person who did not watch you work. They have not seen your tool calls, the files you read, or the output you saw. The reply is the whole report: it must stand alone. Reporting is for work that is finished or genuinely blocked. Do not end a turn to report work you can still do.
5
+
6
+ ### What the reply is for
7
+
8
+ The person reads the reply to decide what to do next. Give them, in this order: what happened, what changed, how to try it, and what they should do. Everything else is optional.
9
+
10
+ Write for a competent colleague who was not in the session. Do not assume they know the plan, the files, the commands, or the attempts that failed. If they would have to ask a question to act on the reply, the reply is not finished.
11
+
12
+ The reply is not a transcript. Leave out what you tried and abandoned, the tool calls, your internal reasoning, and code the person can open. Keep what changes their decision or their next step. A person often reads only the first line and the test block, so both must stand alone.
13
+
14
+ ### The first line
15
+
16
+ - State the outcome, not the activity. Write "The importer now handles quoted fields; all 12 tests pass", not "I worked on the importer".
17
+ - Say whether the work is done. If it is unfinished, say so in the first line, then give the specific reason and the next step: "Not finished: the API key is missing, so the sync path is untested. Everything else is in place."
18
+ - If the person asked a question, answer it in the first line, then support the answer.
19
+ - No preamble ("Great question", "Let me explain"), no restating the request, no closing recap of what you already said.
20
+
21
+ ### Choose the shape
22
+
23
+ | The task was | The shape |
24
+ |---|---|
25
+ | A change to code, files, configuration, a capability, a setting or a document the person will use | Outcome, what changed, how to test, what to do next. These four are required. |
26
+ | An answer or research with no files changed | Outcome, then the findings with sources. Add a test block only if the person can run something. Add a next step only if one follows. |
27
+ | A question | One to three sentences and no headings. |
28
+ | A blocked or unfinished change | First line: unfinished and why. Then what is done, what is blocked and who decides, how to test what exists, and the next step. |
29
+
30
+ A change means the person can do something different after it. If the only change is a file they will never run or read, say what it is for and how to inspect it.
31
+
32
+ ### What to leave out
33
+
34
+ - Your narration of the work: which tools you called, which files you opened, which attempt failed first.
35
+ - Code the person can open. Quote a line only when the exact text is the point.
36
+ - Praise, apologies and remarks about the reply itself.
37
+ - Speculative risks with no action. A real risk belongs in the unverified block with a check.
38
+ - The plan and the request restated. The person wrote them.
39
+
40
+ ### Required blocks for a change
41
+
42
+ #### Outcome
43
+
44
+ One or two sentences. Done or unfinished, and the effect in plain words. Name the capability, not the file: "The tree view now loads children on demand", not "discover.ts changed".
45
+
46
+ #### What changed
47
+
48
+ - Name the relevant paths and say what changed in each and why. Link a file when the person will open it, for example \`src/plugins/install.ts\`.
49
+ - Use a table with the columns File, Change, Why when three or more files changed. Use bullets for one or two.
50
+ - Group related changes. Do not paste diffs, whole functions or file contents the person can open.
51
+ - Mention a deleted, renamed or moved file explicitly, and why.
52
+ - If the change is invisible in the product yet, say that: infrastructure, a migration, a guard, a test seam.
53
+ - If nothing changed, say so plainly and say why the request needed no change.
54
+
55
+ #### How to test
56
+
57
+ This block is the one the person will follow. Make it runnable without reading the rest of the reply.
58
+
59
+ - Number the steps in the order the person performs them.
60
+ - Give the exact command, the exact URL, or the exact UI path. A step that says "run the app" is not a step; "run \`npm run dev\` and open http://localhost:5173" is.
61
+ - Say what the person should see after each step. "The list shows three rows, newest first" is a result; "it works" is not.
62
+ - Say what you already ran and what it returned, so the person does not repeat it.
63
+ - Say which step the person must do that you could not, and why you could not do it.
64
+ - Say what is needed before the test: an account, a key, a fixture, a running service, a browser. If it is missing, give the fallback.
65
+ - If the change has no user-visible effect, write one line: "Nothing to run: the unit tests cover it" or "Nothing to run: the change is in the build, not the product", and name the check that covers it.
66
+ - Keep the steps in the report, not in a separate file.
67
+
68
+ When several checks share fields, a table is shorter than a list:
69
+
70
+ | Check | Command | Expected |
71
+ |---|---|---|
72
+ | Unit | \`npm test\` | 41 passed, 0 failed |
73
+ | Types | \`npm run typecheck\` | no output |
74
+
75
+ #### What to do next
76
+
77
+ - State the person's next action, or say "Nothing needed" when the work is complete.
78
+ - If a decision is needed, name the options and your recommendation first, then the choice the person owns.
79
+ - If a follow-up is needed, name who does it and when. Do not assign work to the person without saying why it is theirs.
80
+ - If the next step depends on a missing secret, account, remote data or approval, say exactly what is needed; do not ask for it as a question at the end of the report.
81
+ - Do not use this block to offer optional extras the person did not ask for.
82
+
83
+ ### Optional blocks
84
+
85
+ Include a block only when it has content. Never add a heading and leave it empty.
86
+
87
+ #### Beyond the request
88
+
89
+ When you did something the request did not name, list each piece and why the work needed it. Examples: a test that would have caught the bug, a fix to the adjacent broken call, a doc line that would otherwise be wrong. This block makes the extra work auditable; it is not a place to list routine steps.
90
+
91
+ #### Departures from the plan
92
+
93
+ When you departed from a plan, a handoff or the steps the request gave, say what you did instead and why, and what effect it has. This includes a skipped step, a reordered step, a replaced tool, a narrower scope or a wider one. Do not hide a departure inside the summary.
94
+
95
+ #### Unverified or needs a person
96
+
97
+ When a concrete check requires a person because you could not perform or observe it, say what is unverified, why you could not check it, and the exact check to make. Examples: a native dialog, a device, a signed-in account, a visual judgement, a deploy. Use this block for a real check, not for generic risk. If you ran the check, report the result instead; do not call it unverified.
98
+
99
+ #### Open questions
100
+
101
+ Only when a question blocks a decision the person must make. State the question, the options and your recommendation. Do not end the report with an open-ended invitation to ask more.
102
+
103
+ ### Headings are yours
104
+
105
+ Use headings that name the content. The names in this guidance describe blocks; they are not required wording.
106
+
107
+ - "How to test", "Test it", "Try it", "Verify" and "Test steps" all name the same block.
108
+ - "What changed", "Changes", "What I changed" and "Files" all name the same block.
109
+ - "What to do next", "Next step" and "Over to you" all name the same block.
110
+ - Order the blocks: outcome, changes, test, next, then the optional blocks. Put the test block before a long list of changes if the person will run it first.
111
+ - Do not use a heading that says nothing: "Summary", "Notes", "Worth a look", "Additional information". A heading must tell the reader what they will find.
112
+ - No fixed heading list is required. The requirement is the information, not the label. A short change may need only two headings; a large one may use six.
113
+ - If a block is one sentence, a bold lead-in is enough; a heading is not required.
114
+
115
+ ### Lists and tables
116
+
117
+ - Bullets for parallel items: files, options, checks.
118
+ - Numbered lists for anything the person performs in order: test steps, next steps, a migration.
119
+ - A Markdown table when several items share fields: File | Change | Why, Check | Command | Result, Option | Cost | Recommendation.
120
+ - Prose for a single point that needs explanation. Do not wrap one sentence in a table.
121
+ - Keep a bullet to one line where possible. If it needs a paragraph, use a paragraph.
122
+
123
+ ### Length
124
+
125
+ - A change: about 20 to 60 lines. A large change may need more, but the outcome, the test block and the next step come first and stay short.
126
+ - A question: one to three sentences.
127
+ - Research: as long as the evidence needs. Do not pad, and do not repeat the sources in the answer and in a list.
128
+ - A blocked task: as long as the person needs to understand the block and act; usually under 30 lines.
129
+ - Never repeat the request, the plan or the previous message to fill space. The person can see them.
130
+
131
+ For a long command output or log, give the first useful line, the last line, and the path to the full output. Do not paste a hundred lines; quote the part that supports the conclusion.
132
+
133
+ ### Honesty
134
+
135
+ - Name only checks you actually ran, with their results. "All 12 tests pass" without the command or the count is a claim, not a report.
136
+ - If a check failed, say so, give the failing output or its first useful lines, and say what it means for the change.
137
+ - If you did not run a check, say "not run" and why. Do not imply coverage you do not have.
138
+ - If you could not verify something, put it in the unverified block with the exact check the person should make.
139
+ - If the work is partial, say what is safe to use and what is not.
140
+ - Do not describe a file as working because it compiles. Say what you observed.
141
+
142
+ A claim becomes a report when it carries its evidence:
143
+
144
+ | Claim | What makes it a report |
145
+ |---|---|
146
+ | "All tests pass" | \`npm test\`: 41 passed, 0 failed |
147
+ | "The endpoint works" | \`curl\` returned 200 with the expected body |
148
+ | "The page looks right" | not a report; name the screen, size and what to look for |
149
+
150
+ ### Tone
151
+
152
+ Write directly and concretely. No apologies, no self-congratulation, no filler. Technical detail belongs in the reply when it helps the person act; leave out the reasoning that only mattered while you worked. If the person's language is not English, reply in their language; this guidance describes the shape, not the language.
153
+
154
+ ### Anti-patterns
155
+
156
+ | Do not | Do instead |
157
+ |---|---|
158
+ | Open with "I've implemented..." and no outcome | Say what now works, in the first line |
159
+ | Restate the request before the answer | Drop the restatement; answer first |
160
+ | "Worth a look" or "Notes" with no action | Name the thing and the action, or drop it |
161
+ | "All tests pass" with no command or count | Give the command, the count and the result |
162
+ | No test step for a change | Add numbered steps, or one line saying nothing to run and why |
163
+ | No next step | Say "Nothing needed", or name the person's action |
164
+ | A departure hidden in the middle | Give it its own block with the reason |
165
+ | A wall of prose for a multi-file change | Use a table and bullets |
166
+ | A long report for a one-line question | One to three sentences |
167
+ | "If you want, I can..." as the ending | Only suggest a follow-up that builds on the request; otherwise stop |
168
+
169
+ ### Examples
170
+
171
+ These examples show the shape, not the words. Copy the structure and the level of detail, then write for the task in front of you.
172
+
173
+ #### Example 1 — a small change
174
+
175
+ The settings page now remembers the last selected tab, and the choice survives a reload. Two
176
+ tests cover it and the full suite passes.
177
+
178
+ ## What changed
179
+
180
+ - \`src/settings/tabs.ts\` stores the tab in local storage and restores it on mount.
181
+ - \`src/settings/tabs.test.ts\` covers the restore and the fallback when storage is empty.
182
+
183
+ ## How to test
184
+
185
+ 1. Run \`npm run dev\` and open http://localhost:5173/settings.
186
+ 2. Select the "Billing" tab, then reload the page.
187
+ 3. The Billing tab is selected again. With storage cleared, the page falls back to "General".
188
+
189
+ I ran \`npm test\`: 41 passed, including the new tests. The reload itself is yours to check; the
190
+ storage path is covered by the unit test.
191
+
192
+ Nothing needed next: the change is complete.
193
+
194
+ #### Example 2 — a multi-file change with extra work
195
+
196
+ The export job now retries a failed upload twice and records the last error. The retry path is
197
+ covered by a new test; the full suite passes.
198
+
199
+ ## What changed
200
+
201
+ | File | Change | Why |
202
+ |---|---|---|
203
+ | \`src/export/retry.ts\` | Retries an upload twice with a short backoff | A flaky network no longer fails the whole export |
204
+ | \`src/export/report.ts\` | Stores the last error on the job | The next attempt can show what went wrong |
205
+ | \`src/export/retry.test.ts\` | New tests for success, retry and give-up | The retry path had no coverage |
206
+
207
+ ## How to test
208
+
209
+ 1. Run \`npm run test:export\`. Expect all tests to pass, including the three retry cases.
210
+ 2. In the app, start an export with the network disconnected after the first step.
211
+ 3. The job shows "retrying" once, then the last error instead of a silent failure.
212
+
213
+ I ran \`npm run test:export\`: 18 passed. I did not test the disconnected-network case in the app;
214
+ that is the manual step above.
215
+
216
+ ## Beyond the request
217
+
218
+ I added the error field to the job record. The retry alone would have hidden the cause, and the
219
+ field is what the next attempt reads.
220
+
221
+ Nothing needed next.
222
+
223
+ #### Example 3 — a blocked or unfinished change
224
+
225
+ Not finished: the sync path is implemented and tested, but the API key is missing, so I could not
226
+ run it against the live service.
227
+
228
+ ## What is done
229
+
230
+ - \`src/sync/client.ts\` sends the new payload and retries once on a 5xx.
231
+ - \`src/sync/client.test.ts\` covers the payload and the retry; 9 tests pass.
232
+
233
+ ## How to test what exists
234
+
235
+ 1. Run \`npm test -- sync\`. Expect 9 passing tests.
236
+ 2. Add \`SYNC_API_KEY\` to \`.env\`, then run \`npm run sync:check\`.
237
+ 3. Expect "sync ok" and one record in the staging table.
238
+
239
+ I could not run step 2 or 3: the key is not in this environment, and I did not have the
240
+ account. Do not use this against production until step 3 passes.
241
+
242
+ ## What to do next
243
+
244
+ Add \`SYNC_API_KEY\` to \`.env.local\` and run \`npm run sync:check\`. If it fails on auth, the
245
+ key belongs to the staging workspace, not the sandbox one. Nothing else is blocked.
246
+
247
+ #### Example 4 — a research answer
248
+
249
+ The harness already reads Claude-layout skills from a package once its manifest declares them,
250
+ but it does not scan \`.claude/skills\` directly.
251
+
252
+ ## What I found
253
+
254
+ - The resolver scans \`.pi/skills\`, \`.agents/skills\` and package \`skills/\` folders; it never
255
+ reads a \`.claude\` folder.
256
+ - A package can declare its skill folder in \`.claude-plugin/plugin.json\`; the harness reads
257
+ that path and adds the folder as a package skill.
258
+
259
+ Sources: \`src/plugins/discover.ts\` (the resolver) and \`src/plugins/manifest.ts\` (the
260
+ manifest).
261
+
262
+ Next step: if you want a pasted repository to contribute its skills, install the repository
263
+ itself rather than a page URL.
264
+
265
+ #### Example 5 — a one-line question
266
+
267
+ It reads both: \`plugin.json\` first, then \`.claude-plugin/plugin.json\`, and the first one with
268
+ a \`skills\` field wins.
269
+
270
+ ### Before you send
271
+
272
+ - The first line says what happened and whether it is done.
273
+ - A change has a what-changed block, a how-to-test block and a what-to-do-next block.
274
+ - Paths are named; a table holds three or more files.
275
+ - Every check you name was actually run, with its result.
276
+ - A departure or extra piece of work has its own block.
277
+ - Nothing is repeated from the request or the plan.
278
+ - The headings fit the content; none is empty.
279
+ - A person can act from the test block alone.
280
+ - Nothing is claimed that the record does not support.
281
+ `;
282
+ export const REPORTING_BUDGET = 18_000;