@harness-engineering/cli 2.0.0 → 2.1.1

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 (95) hide show
  1. package/dist/agents/skills/claude-code/harness-compound/SKILL.md +153 -0
  2. package/dist/agents/skills/claude-code/harness-compound/skill.yaml +60 -0
  3. package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +12 -1
  4. package/dist/agents/skills/claude-code/harness-observability/SKILL.md +2 -0
  5. package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +138 -0
  6. package/dist/agents/skills/claude-code/harness-pulse/references/interview.md +77 -0
  7. package/dist/agents/skills/claude-code/harness-pulse/skill.yaml +61 -0
  8. package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +25 -1
  9. package/dist/agents/skills/codex/harness-compound/SKILL.md +153 -0
  10. package/dist/agents/skills/codex/harness-compound/skill.yaml +60 -0
  11. package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +12 -1
  12. package/dist/agents/skills/codex/harness-observability/SKILL.md +2 -0
  13. package/dist/agents/skills/codex/harness-pulse/SKILL.md +138 -0
  14. package/dist/agents/skills/codex/harness-pulse/references/interview.md +77 -0
  15. package/dist/agents/skills/codex/harness-pulse/skill.yaml +61 -0
  16. package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +25 -1
  17. package/dist/agents/skills/cursor/harness-compound/SKILL.md +153 -0
  18. package/dist/agents/skills/cursor/harness-compound/skill.yaml +60 -0
  19. package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +12 -1
  20. package/dist/agents/skills/cursor/harness-observability/SKILL.md +2 -0
  21. package/dist/agents/skills/cursor/harness-pulse/SKILL.md +138 -0
  22. package/dist/agents/skills/cursor/harness-pulse/references/interview.md +77 -0
  23. package/dist/agents/skills/cursor/harness-pulse/skill.yaml +61 -0
  24. package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +25 -1
  25. package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +153 -0
  26. package/dist/agents/skills/gemini-cli/harness-compound/skill.yaml +60 -0
  27. package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +12 -1
  28. package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +2 -0
  29. package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +138 -0
  30. package/dist/agents/skills/gemini-cli/harness-pulse/references/interview.md +77 -0
  31. package/dist/agents/skills/gemini-cli/harness-pulse/skill.yaml +61 -0
  32. package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +25 -1
  33. package/dist/agents/skills/package.json +1 -0
  34. package/dist/agents/skills/tests/fixtures/harness-compound/bug-track-fixture/input.json +10 -0
  35. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/docs/solutions/bug-track/integration-issues/stalled-lease-cleanup.md +28 -0
  36. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/expected.json +4 -0
  37. package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/input.json +10 -0
  38. package/dist/agents/skills/tests/fixtures/harness-compound/knowledge-track-fixture/input.json +10 -0
  39. package/dist/agents/skills/tests/harness-compound.test.ts +86 -0
  40. package/dist/{agents-md-6SAMHE7H.js → agents-md-L4X4MOWT.js} +3 -3
  41. package/dist/{architecture-5YR52NSV.js → architecture-WJRIYQ45.js} +4 -4
  42. package/dist/{assess-project-5X7TYQFM.js → assess-project-PHC2HO3V.js} +1 -1
  43. package/dist/bin/harness-mcp.js +16 -15
  44. package/dist/bin/harness.js +26 -25
  45. package/dist/{check-phase-gate-PLV2VDOL.js → check-phase-gate-646KY67H.js} +5 -4
  46. package/dist/{chunk-ZYLBIDLE.js → chunk-224XKVLP.js} +1 -1
  47. package/dist/{chunk-ELJ2MXMO.js → chunk-2QPHCR22.js} +9 -9
  48. package/dist/{chunk-SITVRO7K.js → chunk-2ZURBWEV.js} +9 -9
  49. package/dist/{chunk-2IBXUG6J.js → chunk-36X7TRUI.js} +2 -2
  50. package/dist/{chunk-6L2BU4BP.js → chunk-4HKVZO7R.js} +1 -1
  51. package/dist/{chunk-74YADB7T.js → chunk-7XQYOXTB.js} +96 -94
  52. package/dist/{chunk-S2WB4LE7.js → chunk-BMM3GXV6.js} +384 -28
  53. package/dist/{chunk-BYS3TQOO.js → chunk-CPYL6LJR.js} +1 -1
  54. package/dist/{chunk-QDRBWVVY.js → chunk-E7W3YOXL.js} +2 -2
  55. package/dist/{chunk-MTPHNOJW.js → chunk-FNCJ3UCU.js} +507 -244
  56. package/dist/{chunk-RYDPFIJY.js → chunk-JPWICLXQ.js} +1 -1
  57. package/dist/{chunk-RFYF7TJS.js → chunk-K4WRQTEF.js} +1835 -805
  58. package/dist/{chunk-DXGI7DL3.js → chunk-KTWKW6KJ.js} +1 -1
  59. package/dist/{chunk-JNKALGRM.js → chunk-KV7A5OZT.js} +5 -5
  60. package/dist/{chunk-KJD4J54I.js → chunk-LO7ZLV7S.js} +6 -1
  61. package/dist/{chunk-XHOGGZS7.js → chunk-QTUPHTTK.js} +1 -1
  62. package/dist/{chunk-R2CBBZA6.js → chunk-SVC7THFK.js} +1 -1
  63. package/dist/{chunk-PFM3BVVU.js → chunk-TNZYQTEE.js} +1 -1
  64. package/dist/{chunk-HT7HYYGT.js → chunk-VJQLUS5I.js} +1 -1
  65. package/dist/{chunk-6GXSJGSW.js → chunk-VKDBM3RX.js} +3 -3
  66. package/dist/{chunk-JZST2WZS.js → chunk-W4AFGFFB.js} +3 -3
  67. package/dist/{chunk-YJE23GRZ.js → chunk-YG4ZBGEH.js} +39 -5
  68. package/dist/{chunk-5ME3WG3G.js → chunk-YLOAQZLA.js} +5 -5
  69. package/dist/{chunk-7JQ7HLM4.js → chunk-YLTRFZAT.js} +8 -8
  70. package/dist/{chunk-BFBZK6BC.js → chunk-YWD6WXXM.js} +6 -6
  71. package/dist/chunk-ZCZD7FHU.js +16 -0
  72. package/dist/{ci-workflow-7T2XKB3P.js → ci-workflow-TOS5X527.js} +3 -3
  73. package/dist/{dist-77VNTHWV.js → dist-6RT2AVYU.js} +78 -2
  74. package/dist/{dist-42AYXCAK.js → dist-D27LJR2T.js} +7 -1
  75. package/dist/{docs-MSIJ6SDO.js → docs-HOJMQYDZ.js} +5 -4
  76. package/dist/{engine-F4P5RBE4.js → engine-D6UYH6DB.js} +3 -3
  77. package/dist/{entropy-GSUTIORR.js → entropy-LZ4IDGZR.js} +3 -3
  78. package/dist/{feedback-NST6T4HI.js → feedback-WV7EQVK7.js} +1 -1
  79. package/dist/{generate-agent-definitions-BOUGQIPA.js → generate-agent-definitions-ZHFFS6YG.js} +4 -4
  80. package/dist/glob-helper-FZPPTGJN.js +62 -0
  81. package/dist/{graph-loader-IAUPGLRV.js → graph-loader-5MKHAJJF.js} +1 -1
  82. package/dist/index.d.ts +44 -6
  83. package/dist/index.js +26 -25
  84. package/dist/{loader-6QTJC7IN.js → loader-Z23JTXIZ.js} +3 -3
  85. package/dist/{mcp-QFBGTUX6.js → mcp-QFZDPFNP.js} +16 -15
  86. package/dist/{performance-B2NAAIE5.js → performance-7ATLHTQU.js} +5 -4
  87. package/dist/{review-pipeline-6EYMBS6B.js → review-pipeline-COBGGXHJ.js} +3 -3
  88. package/dist/{runtime-VFZK6BCZ.js → runtime-KCBNQUXK.js} +3 -3
  89. package/dist/{scan-DB6FNYGG.js → scan-BIJYHOE7.js} +2 -1
  90. package/dist/{security-HRNTYHPM.js → security-MAPRIFQT.js} +1 -1
  91. package/dist/templates/orchestrator/harness.orchestrator.md +9 -1
  92. package/dist/{validate-YEMAFWJE.js → validate-74XTQR7Q.js} +4 -4
  93. package/dist/{validate-cross-check-DGHTJFVH.js → validate-cross-check-MI5MZ7MB.js} +3 -3
  94. package/package.json +7 -6
  95. package/dist/glob-helper-VCQXK5XY.js +0 -54
@@ -0,0 +1,153 @@
1
+ # Harness Compound
2
+
3
+ > 5-phase post-mortem capture. When a problem is solved, distill it into a structured doc at `docs/solutions/<track>/<category>/<slug>.md` so the next person (or agent) finds the playbook before re-deriving it.
4
+
5
+ ## When to Use
6
+
7
+ - Manually, after solving a non-trivial problem (a bug fix that took >1 commit, a debugging session, an architectural decision worth preserving)
8
+ - When the orchestrator's step 6b mechanical triggers fire (deferred until Phase 7 wires it)
9
+ - When the weekly `compound-candidates` scanner surfaces a candidate (deferred until Phase 5)
10
+ - NOT for trivial fixes (typos, lint, one-line obvious)
11
+ - NOT for facts that belong in `docs/knowledge/` (use `harness-knowledge-pipeline`; the boundary is: knowledge-pipeline extracts structural facts FROM CODE; compound captures post-mortem playbooks WRITTEN AFTER A FIX)
12
+ - NOT for ephemeral session notes (`.harness/learnings.md` still exists for that)
13
+
14
+ ## Process
15
+
16
+ ### Iron Law
17
+
18
+ **One problem, one canonical doc.** Phase 3 (overlap-check) is mandatory. If overlap is high, UPDATE the existing doc (bump `last_updated`, append to relevant section). Do not create a duplicate.
19
+
20
+ ---
21
+
22
+ ### Phase 1: IDENTIFY
23
+
24
+ Extract the problem and the solution from available context:
25
+
26
+ 1. Read recent conversation, the active debug session at `.harness/debug/active/*.md` if present, and recent `git log --oneline -20`.
27
+ 2. Distill into a 1-2 sentence problem statement and 1-2 sentence solution statement.
28
+ 3. Note the affected `module` (package name or area, e.g. `orchestrator`, `cli/validate`).
29
+
30
+ **Output:** `{ problem, solution, module, candidateTags }`.
31
+
32
+ ---
33
+
34
+ ### Phase 2: CLASSIFY
35
+
36
+ Read `docs/solutions/references/schema.yaml` for the authoritative track/category enum and `docs/solutions/references/category-mapping.md` for examples of which problems land in which category.
37
+
38
+ 1. Choose `track`:
39
+ - `bug-track` — a thing was broken; you fixed it. Concrete failure, concrete cause.
40
+ - `knowledge-track` — a pattern, convention, or decision worth preserving as guidance, not a fix.
41
+ 2. Choose `category` from the enum for that track. If nothing fits, **stop and escalate** — adding categories requires a PR (Decision 8).
42
+
43
+ **Output:** `{ track, category }`.
44
+
45
+ ---
46
+
47
+ ### Phase 3: OVERLAP-CHECK
48
+
49
+ 1. List every existing `docs/solutions/<track>/<category>/*.md`.
50
+ 2. For each, read the `# <Title>` heading and the first 200 chars of the `## Problem` (bug-track) or `## Context` (knowledge-track) section.
51
+ 3. Compute overlap heuristically: shared `module`, shared `problem_type`, similar problem statement (Jaccard on bag-of-words >= 0.5 is a reasonable threshold; the agent uses judgment).
52
+ 4. If high overlap with one existing doc:
53
+ - Open the existing doc.
54
+ - Update relevant sections (append a new bullet under `## Solution` or `## Guidance` describing the new instance, bump `last_updated`).
55
+ - Skip Phase 4 and most of Phase 5 (only the write-with-lock step runs).
56
+ 5. If no overlap, proceed to Phase 4.
57
+
58
+ ---
59
+
60
+ ### Phase 4: ASSEMBLE
61
+
62
+ 1. Copy `docs/solutions/assets/resolution-template.md` to a working buffer.
63
+ 2. Fill the frontmatter:
64
+
65
+ ```yaml
66
+ ---
67
+ module: <from Phase 1>
68
+ tags: [<from Phase 1 candidateTags, lowercase, hyphenated>]
69
+ problem_type: <short noun phrase, e.g. 'race-condition'>
70
+ last_updated: '<YYYY-MM-DD, today>'
71
+ track: <bug-track | knowledge-track>
72
+ category: <from Phase 2>
73
+ ---
74
+ ```
75
+
76
+ 3. Replace the `# <Title>` placeholder with a concise problem statement.
77
+ 4. For `bug-track`: fill `## Problem`, `## Root cause`, `## Solution`, `## Prevention`. Delete the knowledge-track sections (`## Context`, `## Guidance`, `## Applicability`).
78
+ 5. For `knowledge-track`: fill `## Context`, `## Guidance`, `## Applicability`. Delete the bug-track sections.
79
+ 6. Cite commit SHAs and `file:line` where helpful.
80
+
81
+ ---
82
+
83
+ ### Phase 5: WRITE (lock-protected)
84
+
85
+ 1. Compute slug: kebab-case from the title; if a file with that slug already exists in the target directory, append `-2`, `-3`, etc.
86
+ 2. **Acquire the per-category lock by shelling out.** Run a Node one-liner that imports `acquireCompoundLock` from `@harness-engineering/core`, holds the handle while you write the doc, then releases it. Example: `node -e "import('@harness-engineering/core').then(({ acquireCompoundLock }) => { const h = acquireCompoundLock('<category>'); /* write the doc here */ h.release(); }).catch(err => { console.error(err.message); process.exit(1); })"`. Lock path: `.harness/locks/compound-<category>.lock`. The MCP tool `acquire_compound_lock` is planned for a later phase — **do not attempt to call it yet**; the shell-out is the only supported path right now.
87
+ - On `CompoundLockHeldError`: report "compound lock for category `<category>` is held by pid `<N>` — wait for it to release or run `/harness:compound` for a different category" and stop. **Do not retry automatically.** A second invocation on the same problem after release will go through Phase 3 and find the doc the first invocation produced.
88
+ 3. Re-run a quick Phase 3 overlap-check inside the lock (defends against TOCTOU when the first overlap-check returned "no overlap" but another invocation completed in the meantime; the re-check is cheap).
89
+ 4. Write the file at `docs/solutions/<track>/<category>/<slug>.md`.
90
+ 5. Validate frontmatter against `SolutionDocFrontmatterSchema` by running `harness validate` (which runs `validateSolutionsDir`).
91
+ 6. Release the lock.
92
+ 7. Surface to chat: file path created (or updated), category, and a one-sentence summary.
93
+
94
+ ## Harness Integration
95
+
96
+ - **`harness validate`** — Run after writing the doc; the solutions validator catches frontmatter errors before commit.
97
+ - **`harness check-deps`** — Not required (no new module imports introduced by writing a doc).
98
+ - **`@harness-engineering/core` lock primitive** — `acquireCompoundLock(category, { cwd })` returns a release handle; throws `CompoundLockHeldError` on contention. See `packages/core/src/locks/compound-lock.ts`.
99
+ - **Schema authority** — `packages/core/src/solutions/schema.ts` is the single source of truth for tracks and categories. `docs/solutions/references/schema.yaml` mirrors it for human reading.
100
+ - **Boundary with `harness-knowledge-pipeline`** — Knowledge-pipeline extracts structural facts FROM CODE. Compound captures post-mortem playbooks WRITTEN AFTER A FIX. Compound's knowledge-track output is a _candidate input_ to the pipeline (Phase 7 of the spec wires this).
101
+ - **Boundary with `.harness/learnings.md`** — The file remains for ephemeral session notes. It is no longer the canonical sink for compounding knowledge — that's `docs/solutions/`.
102
+
103
+ ## Success Criteria
104
+
105
+ - A new solution doc is written to `docs/solutions/<track>/<category>/<slug>.md` with valid frontmatter (passes `validateSolutionsDir`).
106
+ - Two concurrent invocations on the same category cannot both succeed: one writes, the other returns `CompoundLockHeldError`.
107
+ - A second invocation on the same problem updates the existing doc instead of creating a duplicate.
108
+ - The skill never invents a new category — unknown categories are escalated.
109
+ - PII is not written into the doc (the agent reads from local conversation/commits; no remote queries).
110
+
111
+ ## Examples
112
+
113
+ ### Example: bug-track
114
+
115
+ Input: "Stalled lease cleanup in orchestrator caused stuck issues. Fix was to add a 5-minute lease TTL with a sweep at startup. Took 4 commits, debugged via `harness-debugging`."
116
+
117
+ - Phase 1: `module=orchestrator`, problem="stuck issues from stalled leases", solution="lease TTL + startup sweep".
118
+ - Phase 2: `track=bug-track`, `category=integration-issues` (lease coordination is integration-shaped).
119
+ - Phase 3: no overlap.
120
+ - Phase 4: fill Problem / Root cause / Solution / Prevention.
121
+ - Phase 5: write `docs/solutions/bug-track/integration-issues/stalled-lease-cleanup.md` under lock.
122
+
123
+ ### Example: knowledge-track
124
+
125
+ Input: "We standardized on `Result<T, E>` returns for all I/O paths in `packages/core`. Document the convention and when not to use it."
126
+
127
+ - Phase 1: `module=core`, problem="when to use Result vs throwing", solution="convention doc".
128
+ - Phase 2: `track=knowledge-track`, `category=conventions`.
129
+ - Phase 3: no overlap.
130
+ - Phase 4: fill Context / Guidance / Applicability.
131
+ - Phase 5: write `docs/solutions/knowledge-track/conventions/result-type-for-io.md` under lock.
132
+
133
+ ### Example: overlap-check updates existing doc
134
+
135
+ Input: "Hit the same stalled-lease bug today on a different code path."
136
+
137
+ - Phase 1, 2 produce the same `bug-track/integration-issues` target.
138
+ - Phase 3 finds `stalled-lease-cleanup.md` with high overlap.
139
+ - Append a bullet to `## Solution` describing the new instance, bump `last_updated`, do not create a new file.
140
+
141
+ ## Gates
142
+
143
+ - **Phase 3 is mandatory.** No exceptions. Skipping overlap-check produces duplicate docs and erodes the value of the corpus.
144
+ - **No invented categories.** Unknown categories require a PR to `packages/core/src/solutions/schema.ts`. Escalate.
145
+ - **Lock must wrap Phase 5.** Without the lock, two concurrent invocations on the same category race on overlap-check and produce duplicates.
146
+ - **`harness validate` must pass before exit.** Frontmatter errors silently break the corpus.
147
+
148
+ ## Escalation
149
+
150
+ - **Cannot decide track/category:** Escalate to the user. Show the candidate (track, category) pairs and the rationale for each. Wait for selection.
151
+ - **Lock held by another invocation:** Report and stop. Do not retry. The user re-runs after release.
152
+ - **`validateSolutionsDir` rejects the doc:** Show the validator error, fix the frontmatter, re-validate. Do not commit a doc that fails validation.
153
+ - **Problem does not fit any category:** Escalate. Adding a category requires a PR (Decision 8 of the feedback-loops spec).
@@ -0,0 +1,60 @@
1
+ name: harness-compound
2
+ version: "1.0.0"
3
+ description: 5-phase post-mortem capture. Writes a structured solution doc at docs/solutions/{track}/{category}/{slug}.md with frontmatter, overlap-detection, and per-category lock for concurrency safety.
4
+ stability: static
5
+ cognitive_mode: reflective-historian
6
+ triggers:
7
+ - manual
8
+ platforms:
9
+ - claude-code
10
+ - gemini-cli
11
+ - cursor
12
+ - codex
13
+ tools:
14
+ - Bash
15
+ - Read
16
+ - Write
17
+ - Edit
18
+ - Glob
19
+ - Grep
20
+ cli:
21
+ command: harness skill run harness-compound
22
+ args:
23
+ - name: context
24
+ description: Free-text problem context (e.g. 'stalled lease cleanup in orchestrator')
25
+ required: false
26
+ mcp:
27
+ tool: run_skill
28
+ input:
29
+ skill: harness-compound
30
+ context: string
31
+ type: rigid
32
+ tier: 2
33
+ phases:
34
+ - name: identify
35
+ description: Extract problem and solution from conversation, debug session, recent commits
36
+ required: true
37
+ - name: classify
38
+ description: Choose track and category from the v1 schema enums
39
+ required: true
40
+ - name: overlap-check
41
+ description: Scan existing docs/solutions/<track>/<category>/ for matching problems; update existing if high overlap
42
+ required: true
43
+ - name: assemble
44
+ description: Fill the resolution template with track-appropriate sections
45
+ required: true
46
+ - name: write
47
+ description: Write doc under per-category lock; validate frontmatter; release lock
48
+ required: true
49
+ state:
50
+ persistent: false
51
+ files:
52
+ - .harness/locks/compound-<category>.lock
53
+ depends_on: []
54
+ keywords:
55
+ - post-mortem
56
+ - solutions
57
+ - compound-learning
58
+ - playbook
59
+ - bug-track
60
+ - knowledge-track
@@ -68,7 +68,18 @@ Extract business knowledge from all available sources into a fresh snapshot.
68
68
  - Only runs if connectors are configured in `harness.config.json`
69
69
  - Skip silently if no connectors
70
70
 
71
- 4. **Build Fresh Snapshot:** Collect all extraction results into a `KnowledgeSnapshot`.
71
+ 4. **Solutions Candidates** (if `docs/solutions/` exists): Run
72
+ `BusinessKnowledgeIngestor.ingestSolutions("docs/solutions")` to consume
73
+ knowledge-track post-mortems written by `harness-compound`.
74
+ - Only `docs/solutions/knowledge-track/<category>/*.md` files are candidates;
75
+ bug-track docs are skipped (they are fix playbooks, not structural facts).
76
+ - Each candidate must validate against `SolutionDocFrontmatterSchema` from
77
+ `@harness-engineering/core`. Invalid frontmatter is logged and skipped.
78
+ - Stable `last_updated` (older than configurable threshold; default: not gated
79
+ in Phase 7) is the promotion criterion for `business_concept` graph nodes.
80
+ - Candidates that pass become `business_concept` nodes in the snapshot.
81
+
82
+ 5. **Build Fresh Snapshot:** Collect all extraction results into a `KnowledgeSnapshot`.
72
83
  - Each entry has: `id`, `type`, `contentHash`, `source`, `name`
73
84
  - Each entry's `domain` is resolved via path-based inference (`packages/<dir>`, `apps/<dir>`, etc.) unless an explicit `metadata.domain` is set; projects can extend or override via `knowledge.domainPatterns` and `knowledge.domainBlocklist` in `harness.config.json` — see [`docs/knowledge/graph/node-edge-taxonomy.md`](../../../../docs/knowledge/graph/node-edge-taxonomy.md#domain-inference) for full precedence.
74
85
 
@@ -11,6 +11,8 @@
11
11
  - NOT for security-focused log analysis (use harness-security-review)
12
12
  - NOT for incident response procedures (use harness-incident-response)
13
13
 
14
+ > See also: `harness-pulse` is the read-side companion to this skill. Pulse READS observability data (and external analytics/error signals) to produce daily product-pulse reports; this skill DESIGNS the instrumentation that produces those signals in the first place.
15
+
14
16
  ## Process
15
17
 
16
18
  ### Phase 1: DETECT -- Identify Existing Instrumentation
@@ -0,0 +1,138 @@
1
+ # Harness Pulse
2
+
3
+ > Single-page time-windowed product pulse. **Phase 3 ships the first-run interview only**: it converts vague intent into a concrete `pulse:` block in `harness.config.json`, refuses read-write DB credentials, and seeds from `STRATEGY.md` when present. The actual `harness pulse run` (Phases 2-4 of the runtime) is deferred to spec Phase 4.
4
+
5
+ ## When to Use
6
+
7
+ - Manually, when a project wants to start receiving daily pulse reports
8
+ - When `harness.config.json` has no `pulse:` block and the user invokes `/harness:pulse`
9
+ - NOT for ad-hoc one-off metric queries (that's the analytics tool's job)
10
+ - NOT for replacing dashboards (pulse is a single-page summary, not a metrics platform)
11
+ - NOT for projects that have not yet decided what their key metrics are (run `harness-strategy` first to write `STRATEGY.md`; pulse seeds from it)
12
+
13
+ ## Process
14
+
15
+ ### Iron Law
16
+
17
+ **No PII reaches `harness.config.json` and no read-write DB credential is accepted.** Both are interview-time gates; both are hard refusals (no warnings, no overrides without an explicit user-typed escape).
18
+
19
+ ---
20
+
21
+ ### Phase 0: ROUTE BY CONFIG STATE
22
+
23
+ 1. Read `harness.config.json`.
24
+ 2. If `pulse.enabled` is set (true OR false), skip directly to "Phase 2: RUN" — **deferred to spec Phase 4**. For now, surface "pulse already configured; the run path ships in Phase 4" and stop.
25
+ 3. Otherwise enter Phase 1.
26
+
27
+ ---
28
+
29
+ ### Phase 1: FIRST-RUN INTERVIEW
30
+
31
+ Read `references/interview.md` for the SMART pushback rules and the READ-WRITE-DB rejection rule. Both are mandatory.
32
+
33
+ 1. **Seed from STRATEGY.md.** Shell out to a Node one-liner that imports `seedFromStrategy` from `@harness-engineering/core`. Capture `{ name, keyMetrics, warnings }`.
34
+ - Surface `warnings` to the user verbatim.
35
+ - If `name` is non-null, confirm it as the product name; otherwise prompt.
36
+ - For each `keyMetric`, walk it through the SMART bar in step 4.
37
+
38
+ 2. **Pick the lookback default.** Use `emit_interaction` (type: `question`) when in MCP mode; otherwise present numbered options in chat: `["24h", "7d", "30d", "custom"]`. Default `24h` per spec.
39
+
40
+ 3. **Identify the primary engagement event** (e.g. `session_started`). Apply the SMART bar. If the user can't name one, set `null` and add a pending entry. Record the event name in `primaryEvent`.
41
+
42
+ 4. **Identify the value-realization event** (e.g. `plan_completed`). SMART bar applies. Record in `valueEvent`.
43
+
44
+ 5. **Identify completion events** (zero or more). SMART bar per item. Record in `completionEvents`.
45
+
46
+ 6. **Quality scoring (optional).** Ask whether the user wants quality sampling on a single dimension (e.g. "did the plan deliver value"). Default off. If enabled, set `qualityScoring: true` and record `qualityDimension`.
47
+
48
+ 7. **Wire data sources.** Ask which providers are available:
49
+ - `analytics`: posthog, amplitude, mixpanel, custom — or null
50
+ - `tracing`: sentry, datadog, custom — or null
51
+ - `payments`: stripe, custom — or null
52
+ - `db`: opt-in only, with the **READ-WRITE-DB rejection rule** active
53
+ For each non-null choice, check `getPulseAdapter(name)`. If absent, surface the "Phase 4 will ship the adapter" warning from `references/interview.md`.
54
+
55
+ 8. **Walk every STRATEGY.md key metric** through SMART. Map to an event when wired; otherwise append to `pendingMetrics` (or `excludedMetrics` if explicitly skipped). Cite `STRATEGY.md` when seeding so the user understands provenance.
56
+
57
+ 9. **Confirm the assembled config.** Show the user the proposed `pulse:` block; ask for confirmation.
58
+
59
+ 10. **Write the config.** Shell out to a Node one-liner that imports `writePulseConfig` from `@harness-engineering/core`. Pipe the JSON through stdin so user-supplied event names, qualityDimension, and pendingMetrics never cross the shell tokenizer (quotes, backticks, `$()`, `$VAR` in user input would otherwise break the command or allow injection):
60
+
61
+ ```bash
62
+ echo '<json-blob>' | node -e "import('@harness-engineering/core').then(m => m.writePulseConfig(JSON.parse(require('fs').readFileSync(0, 'utf-8')), { configPath: 'harness.config.json' })).catch(err => { console.error(err.message); process.exit(1); })"
63
+ ```
64
+
65
+ The writer preserves all other config keys and writes a `.bak`.
66
+
67
+ 11. **Offer to register the `product-pulse` maintenance task.** Deferred: Phase 6 of the spec wires it. For now, surface "the daily 8am `product-pulse` task will be registered automatically once Phase 6 of the feedback-loops spec ships; you can also run pulse on demand with `/harness:pulse [window]` once Phase 4 ships."
68
+
69
+ 12. **Run `harness validate`** to confirm the new `pulse:` block parses.
70
+
71
+ ---
72
+
73
+ ### Phase 2: RUN — deferred to spec Phase 4
74
+
75
+ Stub: when `pulse.enabled === true`, this phase will dispatch analytics/tracing/payments queries in parallel, run the SanitizeFn for each provider's response, and stash sanitized results for Phase 3. NOT YET IMPLEMENTED. The skill exits early with a "deferred to Phase 4" message if it reaches this phase.
76
+
77
+ ### Phase 3: ASSEMBLE — deferred
78
+
79
+ ### Phase 4: SAVE — deferred
80
+
81
+ ## Harness Integration
82
+
83
+ - **`harness validate`** — Run after `writePulseConfig`; the existing pulse-schema validator catches malformed blocks.
84
+ - **`@harness-engineering/core`** primitives consumed by this skill:
85
+ - `writePulseConfig(config, { configPath })` — atomic config update with .bak.
86
+ - `seedFromStrategy({ cwd })` — defensive STRATEGY.md reader.
87
+ - `getPulseAdapter(name)` / `listPulseAdapters()` — adapter discovery (Phase 4 populates).
88
+ - `PulseConfigSchema` / `PII_FIELD_DENYLIST` — schema and PII contracts (already shipped Phase 1).
89
+ - **Boundary with `harness-strategy`** — Strategy writes `STRATEGY.md`; pulse reads it to seed. Pulse never writes to `STRATEGY.md`.
90
+ - **Boundary with `harness-observability`** — Observability designs _what_ to instrument; pulse is the read-side companion that surfaces what was instrumented.
91
+ - **Decision 6 (read-only)** — Pulse refuses read-write DB credentials. Documented in `references/interview.md`.
92
+ - **Decision 7 (PII contract)** — Every provider source must have a registered `SanitizeFn` adapter. Phase 3 ships the registry; Phase 4 ships the adapters.
93
+
94
+ ## Success Criteria
95
+
96
+ - On a project with no `pulse:` block, the interview produces a valid `pulse:` block in `harness.config.json` with all non-pulse keys preserved.
97
+ - A `harness.config.json.bak` is written before mutation.
98
+ - A read-write DB credential is refused; the interview either accepts a read-only credential or sets `sources.db.enabled: false`.
99
+ - When `STRATEGY.md` exists, `name` and `Key metrics` seed the interview; missing/malformed STRATEGY.md soft-fails with warnings.
100
+ - `harness validate` passes after the interview completes.
101
+
102
+ ## Examples
103
+
104
+ ### Example: greenfield (no STRATEGY.md, no existing pulse block)
105
+
106
+ - Phase 0: route to Phase 1.
107
+ - Phase 1.1: `seedFromStrategy` returns `{ name: null, keyMetrics: [], warnings: ['STRATEGY.md not found'] }`.
108
+ - Phase 1.2-7: prompt user; collect `lookbackDefault: '24h'`, `primaryEvent: 'session_started'`, `valueEvent: 'plan_completed'`, `sources.analytics: 'posthog'` (with adapter-availability warning), `sources.db.enabled: false`.
109
+ - Phase 1.10: `writePulseConfig` writes the block; `.bak` saved.
110
+ - Phase 1.12: `harness validate` passes.
111
+
112
+ ### Example: STRATEGY.md present with 3 Key metrics
113
+
114
+ - Phase 1.1: seed returns `{ name: 'Acme', keyMetrics: ['DAU', 'plans/week', 'p95 latency'] }`.
115
+ - Phase 1.8: walk each metric through SMART.
116
+ - "DAU" → mapped to `session_started` count over 24h, accepted.
117
+ - "plans/week" → mapped to `plan_completed` count over 7d, accepted (recorded as a future custom window).
118
+ - "p95 latency" → no tracing source wired yet; lands in `pendingMetrics`.
119
+
120
+ ### Example: user offers an admin DB credential
121
+
122
+ - Phase 1.7: user pastes `postgresql://admin:pwd@host/db`.
123
+ - Skill matches `admin` username against the rejection list; refuses; cites Decision 6.
124
+ - User declines to provide a read-only credential; skill writes `sources.db.enabled: false`.
125
+
126
+ ## Gates
127
+
128
+ - **READ-WRITE-DB rejection is non-negotiable.** No flag, no override, no "I know what I'm doing" path. Refuse and document.
129
+ - **SMART pushback is mandatory on every proposed metric/event.** Silently accepting a vague name pollutes the corpus.
130
+ - **`writePulseConfig` is the only sanctioned write path.** Do not hand-edit `harness.config.json`. The writer is the layer that preserves non-pulse keys and writes the .bak.
131
+ - **`harness validate` must pass before exit.** A malformed `pulse:` block silently breaks the daily task once Phase 4 ships.
132
+
133
+ ## Escalation
134
+
135
+ - **User insists on read-write DB credentials:** Refuse. Set `sources.db.enabled: false`. Stop.
136
+ - **Adapter not registered for a chosen provider:** Warn, record the choice, continue. The runtime gate (Phase 4) refuses to run until the adapter ships.
137
+ - **STRATEGY.md frontmatter is malformed but H1 is present:** Use H1 as `name` and surface a warning. If neither is parseable, prompt the user.
138
+ - **`writePulseConfig` throws:** Report the validator error verbatim. Do not retry without user fix.
@@ -0,0 +1,77 @@
1
+ # Pulse First-Run Interview Reference
2
+
3
+ The first-run interview converts vague intent ("I want to know how the product is doing") into a concrete `pulse:` block in `harness.config.json`. This document is the rule book the skill quotes when pushing back on input.
4
+
5
+ ## SMART Pushback Rules
6
+
7
+ For every metric or event the user proposes, the skill MUST evaluate it against the SMART bar and push back when it fails. SMART = **S**pecific, **M**easurable, **A**chievable, **R**elevant, **T**ime-bound.
8
+
9
+ | Test | Question | Reject when |
10
+ | ---------- | -------------------------------------------------------------------- | --------------------------------------------------- |
11
+ | Specific | Does the name uniquely identify one event/metric? | "engagement", "activity", "usage" without qualifier |
12
+ | Measurable | Is there a wired data source that emits this? | No analytics/tracing source covers it |
13
+ | Achievable | Can it be queried in <30s within the lookback window? | Requires a full-table scan over years of data |
14
+ | Relevant | Does it map to a `STRATEGY.md` Key metric or a documented user pain? | Vanity metric ("total signups ever") |
15
+ | Time-bound | Is the window declared (24h, 7d, 30d)? | Naked counter with no time scope |
16
+
17
+ ### Pushback script
18
+
19
+ When a proposed metric fails, the skill MUST:
20
+
21
+ 1. Quote the failing test and the rule.
22
+ 2. Suggest a concrete repair (example: "engagement" → "session_started events per active user per day").
23
+ 3. Ask the user to either accept the repair, propose a new name, or skip this metric (it lands in `pendingMetrics` if no source covers it).
24
+
25
+ The skill MUST NOT silently accept a metric that fails the bar. Pushback is mandatory.
26
+
27
+ ## READ-WRITE-DB Rejection Rule
28
+
29
+ When the user offers a database connection string for the `db` source:
30
+
31
+ 1. The skill MUST inspect the connection string and reject any of the following without exception:
32
+ - User has `INSERT`, `UPDATE`, `DELETE`, `DROP`, `TRUNCATE`, `ALTER`, `CREATE`, or `GRANT` privileges
33
+ - Connection string includes `?role=admin`, `?role=write`, `?ssl=disable` (the last is a separate red flag worth surfacing)
34
+ - The connection user is named `root`, `admin`, `postgres`, `mysql`, or any name matching `*_admin`, `*_write`, `*_rw`
35
+ 2. The skill MUST NOT attempt to "downgrade" the credentials silently. It MUST ask the user to provide a read-only credential.
36
+ 3. If the user insists, the skill MUST refuse and write `sources.db.enabled: false`. Pulse is read-only by contract (Decision 6).
37
+ 4. The rejection message MUST cite "Decision 6 of the feedback-loops spec: pulse refuses read-write DB credentials".
38
+
39
+ The skill cannot verify the privilege grant directly without connecting (and connecting is exactly what we're refusing for write creds). The signal-set above is heuristic; when in doubt, refuse and ask.
40
+
41
+ ## Adapter availability check
42
+
43
+ Before accepting a `sources.analytics` or `sources.tracing` value, the skill MUST verify a `SanitizeFn` adapter is registered for that name:
44
+
45
+ ```ts
46
+ import { getPulseAdapter } from '@harness-engineering/core';
47
+ if (!getPulseAdapter(value)) {
48
+ // refuse — Decision 7: pulse refuses to enable for a provider whose
49
+ // adapter has no `sanitize` implementation.
50
+ }
51
+ ```
52
+
53
+ In Phase 3 NO adapters are registered yet — Phase 4 ships them. So during the interview the skill warns:
54
+
55
+ > "No `SanitizeFn` adapters are currently registered. Recording your selection but pulse will refuse to run until Phase 4 ships adapters for posthog/sentry."
56
+
57
+ The selection is still written to `harness.config.json` (so re-running pulse later finds it); the runtime refusal is the safety gate.
58
+
59
+ ## Strategy seeding
60
+
61
+ If `STRATEGY.md` exists at repo root:
62
+
63
+ - The product `name` is read from frontmatter (`name: '<X>'`); fallback to the first `# <Title>` H1.
64
+ - The `## Key metrics` bullet list is treated as a list of REQUIRED metrics that the interview MUST walk through one-by-one.
65
+ - Each Key metric goes through the SMART bar; the user may map it to an event/source, defer it to `pendingMetrics`, or explicitly exclude it (which lands it in `excludedMetrics`).
66
+
67
+ If `STRATEGY.md` is absent, the skill proceeds with no seed and prompts the user from scratch.
68
+
69
+ ## Interview output
70
+
71
+ After the interview, the skill calls `writePulseConfig(config, { configPath })` from `@harness-engineering/core`:
72
+
73
+ - The full config matches `PulseConfigSchema`.
74
+ - All non-pulse keys in `harness.config.json` are preserved.
75
+ - A `harness.config.json.bak` is written before mutation.
76
+
77
+ Then the skill offers to register the `product-pulse` maintenance task (deferred — that wiring is Phase 6 of the spec; for now the skill notes the offer and explains the user can register it manually later).
@@ -0,0 +1,61 @@
1
+ name: harness-pulse
2
+ version: "0.1.0"
3
+ description: First-run pulse interview. Converts intent into a validated pulse config with SMART pushback, read-write-DB rejection, STRATEGY.md seeding. Phase 3 ships the interview; the run path is deferred to Phase 4.
4
+ stability: static
5
+ cognitive_mode: configuration-interviewer
6
+ triggers:
7
+ - manual
8
+ platforms:
9
+ - claude-code
10
+ - gemini-cli
11
+ - cursor
12
+ - codex
13
+ tools:
14
+ - Bash
15
+ - Read
16
+ - Write
17
+ - Edit
18
+ - Glob
19
+ - Grep
20
+ cli:
21
+ command: harness skill run harness-pulse
22
+ args:
23
+ - name: window
24
+ description: Lookback window for run mode (deferred until Phase 4); ignored during the interview
25
+ required: false
26
+ mcp:
27
+ tool: run_skill
28
+ input:
29
+ skill: harness-pulse
30
+ window: string
31
+ type: rigid
32
+ tier: 2
33
+ phases:
34
+ - name: route
35
+ description: Route by harness.config.json pulse state
36
+ required: true
37
+ - name: interview
38
+ description: SMART pushback + STRATEGY.md seeding + read-write-DB rejection; writes pulse block to harness.config.json
39
+ required: true
40
+ - name: run
41
+ description: Deferred to spec Phase 4
42
+ required: false
43
+ - name: assemble
44
+ description: Deferred to spec Phase 4
45
+ required: false
46
+ - name: save
47
+ description: Deferred to spec Phase 4
48
+ required: false
49
+ state:
50
+ persistent: true
51
+ files:
52
+ - harness.config.json
53
+ - harness.config.json.bak
54
+ depends_on: []
55
+ keywords:
56
+ - pulse
57
+ - product-pulse
58
+ - feedback-loops
59
+ - read-side
60
+ - smart-metrics
61
+ - strategy-seeding
@@ -54,6 +54,29 @@ Top candidates (scored by position 50%, dependents 30%, affinity 20%):
54
54
  - Read the spec's Success Criteria section
55
55
  - Assess effort and impact from the spec content
56
56
 
57
+ 1b. Read the most recent pulse report (if any):
58
+
59
+ - List entries in `docs/pulse-reports/` and **filter to those matching the
60
+ regex `/^\d{4}-\d{2}-\d{2}_\d{2}-\d{2}\.md$/`** (the canonical
61
+ `YYYY-MM-DD_HH-MM.md` pulse-report filename shape). Filtering before
62
+ sorting prevents non-conforming files (e.g. `README.md`, `NOTES.md`,
63
+ partial-timestamp drafts) from corrupting the signal.
64
+ - Lexical-sort the matched filenames (ISO timestamps sort
65
+ chronologically) and take the LAST entry as the most recent.
66
+ - If zero entries match the regex (directory empty, absent, or only
67
+ contains non-conforming files), soft-fail: skip this step and proceed
68
+ without pulse signal. Do not block recommendation.
69
+ - For each top-3 candidate, scan the most recent pulse report's Headlines and
70
+ Followups sections for keywords matching the candidate's name, milestone, or
71
+ spec keywords. Note any signal that elevates priority (top followup item
72
+ related to a candidate; an error spike in a candidate's area) or suppresses
73
+ it (recent stable signal in candidate's area).
74
+ - When pulse signal is found, cite it verbatim in the recommendation rationale
75
+ (e.g., "Pulse 2026-05-05_08-00 headline: 'auth errors up 30%' — elevates
76
+ Auth Hardening").
77
+ - Use ONLY the most recent file. If older reports conflict with the most
78
+ recent, ignore the older signal.
79
+
57
80
  2. Provide a recommendation with reasoning:
58
81
 
59
82
  ```
@@ -148,7 +171,8 @@ Proceed with Feature A? (y/n/pick another)
148
171
  5. Assignment updates feature field, appends history records, and syncs externally
149
172
  6. Reassignment produces two history records (unassigned + assigned)
150
173
  7. Transition routes to brainstorming (no spec) or autopilot (spec exists)
151
- 8. `harness validate` passes after all changes
174
+ 8. When a pulse report exists, the recommendation rationale cites pulse signal for any top-3 candidate whose area is referenced in the pulse Headlines or Followups.
175
+ 9. `harness validate` passes after all changes
152
176
 
153
177
  ## Rationalizations to Reject
154
178