@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.
- package/dist/agents/skills/claude-code/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/claude-code/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/claude-code/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/claude-code/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/claude-code/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/claude-code/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/claude-code/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/claude-code/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/codex/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/codex/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/codex/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/codex/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/codex/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/codex/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/codex/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/codex/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/cursor/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/cursor/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/cursor/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/cursor/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/cursor/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/cursor/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/cursor/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/cursor/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/gemini-cli/harness-compound/SKILL.md +153 -0
- package/dist/agents/skills/gemini-cli/harness-compound/skill.yaml +60 -0
- package/dist/agents/skills/gemini-cli/harness-knowledge-pipeline/SKILL.md +12 -1
- package/dist/agents/skills/gemini-cli/harness-observability/SKILL.md +2 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/SKILL.md +138 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/references/interview.md +77 -0
- package/dist/agents/skills/gemini-cli/harness-pulse/skill.yaml +61 -0
- package/dist/agents/skills/gemini-cli/harness-roadmap-pilot/SKILL.md +25 -1
- package/dist/agents/skills/package.json +1 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/bug-track-fixture/input.json +10 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/docs/solutions/bug-track/integration-issues/stalled-lease-cleanup.md +28 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/expected.json +4 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/duplicate-detection-fixture/input.json +10 -0
- package/dist/agents/skills/tests/fixtures/harness-compound/knowledge-track-fixture/input.json +10 -0
- package/dist/agents/skills/tests/harness-compound.test.ts +86 -0
- package/dist/{agents-md-6SAMHE7H.js → agents-md-L4X4MOWT.js} +3 -3
- package/dist/{architecture-5YR52NSV.js → architecture-WJRIYQ45.js} +4 -4
- package/dist/{assess-project-5X7TYQFM.js → assess-project-PHC2HO3V.js} +1 -1
- package/dist/bin/harness-mcp.js +16 -15
- package/dist/bin/harness.js +26 -25
- package/dist/{check-phase-gate-PLV2VDOL.js → check-phase-gate-646KY67H.js} +5 -4
- package/dist/{chunk-ZYLBIDLE.js → chunk-224XKVLP.js} +1 -1
- package/dist/{chunk-ELJ2MXMO.js → chunk-2QPHCR22.js} +9 -9
- package/dist/{chunk-SITVRO7K.js → chunk-2ZURBWEV.js} +9 -9
- package/dist/{chunk-2IBXUG6J.js → chunk-36X7TRUI.js} +2 -2
- package/dist/{chunk-6L2BU4BP.js → chunk-4HKVZO7R.js} +1 -1
- package/dist/{chunk-74YADB7T.js → chunk-7XQYOXTB.js} +96 -94
- package/dist/{chunk-S2WB4LE7.js → chunk-BMM3GXV6.js} +384 -28
- package/dist/{chunk-BYS3TQOO.js → chunk-CPYL6LJR.js} +1 -1
- package/dist/{chunk-QDRBWVVY.js → chunk-E7W3YOXL.js} +2 -2
- package/dist/{chunk-MTPHNOJW.js → chunk-FNCJ3UCU.js} +507 -244
- package/dist/{chunk-RYDPFIJY.js → chunk-JPWICLXQ.js} +1 -1
- package/dist/{chunk-RFYF7TJS.js → chunk-K4WRQTEF.js} +1835 -805
- package/dist/{chunk-DXGI7DL3.js → chunk-KTWKW6KJ.js} +1 -1
- package/dist/{chunk-JNKALGRM.js → chunk-KV7A5OZT.js} +5 -5
- package/dist/{chunk-KJD4J54I.js → chunk-LO7ZLV7S.js} +6 -1
- package/dist/{chunk-XHOGGZS7.js → chunk-QTUPHTTK.js} +1 -1
- package/dist/{chunk-R2CBBZA6.js → chunk-SVC7THFK.js} +1 -1
- package/dist/{chunk-PFM3BVVU.js → chunk-TNZYQTEE.js} +1 -1
- package/dist/{chunk-HT7HYYGT.js → chunk-VJQLUS5I.js} +1 -1
- package/dist/{chunk-6GXSJGSW.js → chunk-VKDBM3RX.js} +3 -3
- package/dist/{chunk-JZST2WZS.js → chunk-W4AFGFFB.js} +3 -3
- package/dist/{chunk-YJE23GRZ.js → chunk-YG4ZBGEH.js} +39 -5
- package/dist/{chunk-5ME3WG3G.js → chunk-YLOAQZLA.js} +5 -5
- package/dist/{chunk-7JQ7HLM4.js → chunk-YLTRFZAT.js} +8 -8
- package/dist/{chunk-BFBZK6BC.js → chunk-YWD6WXXM.js} +6 -6
- package/dist/chunk-ZCZD7FHU.js +16 -0
- package/dist/{ci-workflow-7T2XKB3P.js → ci-workflow-TOS5X527.js} +3 -3
- package/dist/{dist-77VNTHWV.js → dist-6RT2AVYU.js} +78 -2
- package/dist/{dist-42AYXCAK.js → dist-D27LJR2T.js} +7 -1
- package/dist/{docs-MSIJ6SDO.js → docs-HOJMQYDZ.js} +5 -4
- package/dist/{engine-F4P5RBE4.js → engine-D6UYH6DB.js} +3 -3
- package/dist/{entropy-GSUTIORR.js → entropy-LZ4IDGZR.js} +3 -3
- package/dist/{feedback-NST6T4HI.js → feedback-WV7EQVK7.js} +1 -1
- package/dist/{generate-agent-definitions-BOUGQIPA.js → generate-agent-definitions-ZHFFS6YG.js} +4 -4
- package/dist/glob-helper-FZPPTGJN.js +62 -0
- package/dist/{graph-loader-IAUPGLRV.js → graph-loader-5MKHAJJF.js} +1 -1
- package/dist/index.d.ts +44 -6
- package/dist/index.js +26 -25
- package/dist/{loader-6QTJC7IN.js → loader-Z23JTXIZ.js} +3 -3
- package/dist/{mcp-QFBGTUX6.js → mcp-QFZDPFNP.js} +16 -15
- package/dist/{performance-B2NAAIE5.js → performance-7ATLHTQU.js} +5 -4
- package/dist/{review-pipeline-6EYMBS6B.js → review-pipeline-COBGGXHJ.js} +3 -3
- package/dist/{runtime-VFZK6BCZ.js → runtime-KCBNQUXK.js} +3 -3
- package/dist/{scan-DB6FNYGG.js → scan-BIJYHOE7.js} +2 -1
- package/dist/{security-HRNTYHPM.js → security-MAPRIFQT.js} +1 -1
- package/dist/templates/orchestrator/harness.orchestrator.md +9 -1
- package/dist/{validate-YEMAFWJE.js → validate-74XTQR7Q.js} +4 -4
- package/dist/{validate-cross-check-DGHTJFVH.js → validate-cross-check-MI5MZ7MB.js} +3 -3
- package/package.json +7 -6
- 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. **
|
|
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.
|
|
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
|
|