@rallycry/conveyor-agent 10.13.72 → 11.0.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/{boot-ZNL7X5LQ.js → boot-PKQ2I66D.js} +49 -2
- package/dist/{chunk-WMMBAKPE.js → chunk-GL2DIQEQ.js} +121 -8
- package/dist/{chunk-2L5THOWD.js → chunk-JQVAWRVL.js} +2 -2
- package/dist/{chunk-LSZ2KLJY.js → chunk-N4WSUTGV.js} +40 -3
- package/dist/{chunk-2SN32LM6.js → chunk-QETNCXIR.js} +2177 -1267
- package/dist/cli.js +257 -100
- package/dist/index.d.ts +170 -3
- package/dist/index.js +3 -3
- package/dist/{serve-boot-4N3FRXVQ.js → serve-boot-YPAUKENG.js} +3 -3
- package/package.json +5 -4
- package/skills/conveyor-build/SKILL.md +262 -0
- package/skills/conveyor-build/references/pack-path.md +224 -0
- package/skills/conveyor-build/references/task-path.md +67 -0
- package/skills/conveyor-consensus/SKILL.md +99 -0
- package/skills/conveyor-consensus/references/doc-template.html +204 -0
- package/skills/conveyor-local-loop/SKILL.md +258 -0
- package/skills/conveyor-meeting-review/SKILL.md +72 -0
- package/skills/conveyor-plan/SKILL.md +177 -0
- package/skills/conveyor-plan/references/plan-format.md +134 -0
- package/skills/conveyor-review/SKILL.md +161 -0
- package/skills/conveyor-start/SKILL.md +106 -0
- package/skills/conveyor-triage/SKILL.md +174 -0
- package/skills/conveyor-workflows/SKILL.md +195 -0
- package/skills/conveyor-workflows/references/mcp-setup.md +43 -0
|
@@ -0,0 +1,224 @@
|
|
|
1
|
+
# Pack path — a feature-branch pack, start to one PR
|
|
2
|
+
|
|
3
|
+
The pack execution core of [conveyor-build](../SKILL.md). Read that first;
|
|
4
|
+
ground rules, environment differences, verification, and aftercare live there.
|
|
5
|
+
|
|
6
|
+
A feature-branch pack is a parent card whose children branch from and PR into a
|
|
7
|
+
shared **pack branch**, and which lands on `dev` as ONE final PR. Children that
|
|
8
|
+
PR straight into `dev` are not this — build those individually via the task
|
|
9
|
+
path.
|
|
10
|
+
|
|
11
|
+
**You are both the coordinator and every child's implementer.** One session
|
|
12
|
+
owns all the state, serially. That is the whole point: the distributed
|
|
13
|
+
alternative is where pack incidents come from.
|
|
14
|
+
|
|
15
|
+
## Setup (first iteration only)
|
|
16
|
+
|
|
17
|
+
1. Confirm the shape: the parent has (or will have) children, its status is
|
|
18
|
+
neither InProgress nor ReviewPR, and no agent session is live on it. A
|
|
19
|
+
parent already InProgress/ReviewPR has a coordinator — report and stop
|
|
20
|
+
rather than compete.
|
|
21
|
+
2. **Ensure the pack branch exists on origin.** Use the card's `githubBranch`
|
|
22
|
+
if set; otherwise cut `ft/<parent-slug>` from `origin/dev`, push `-u`, and
|
|
23
|
+
IMMEDIATELY record it: `mcp__conveyor__update_task` with
|
|
24
|
+
`githubBranch: <branch>`.
|
|
25
|
+
|
|
26
|
+
That write is load-bearing, not bookkeeping. Identification mints a
|
|
27
|
+
competing `conveyor/*` branch name onto any branchless card it processes,
|
|
28
|
+
and every pack-child merge handler keys on the card's recorded branch
|
|
29
|
+
matching the PRs' real base — a drifted record strands merged children in
|
|
30
|
+
ReviewPR.
|
|
31
|
+
|
|
32
|
+
**Push the branch first, and verify it yourself.** The local surface
|
|
33
|
+
validates the ref; the pod's `update_task` does NOT — it writes the value
|
|
34
|
+
through with no check that the branch exists on origin, and no guard
|
|
35
|
+
against repointing the record away from the branch the pod is actually
|
|
36
|
+
running on, which strands the compute. A successful write is not evidence
|
|
37
|
+
the branch is pushed.
|
|
38
|
+
3. **No children yet?** Break the work down first: explore, save the
|
|
39
|
+
parent-level plan with `mcp__conveyor__update_task`, then
|
|
40
|
+
`mcp__conveyor__create_subtask` each child with a standalone plan
|
|
41
|
+
(file:line citations, verification steps) and `dependsOn` edges wherever one
|
|
42
|
+
blocks another.
|
|
43
|
+
4. Post the claim marker to the PARENT chat, naming the pack branch. Use a
|
|
44
|
+
stable, greppable prefix — `[pack] claimed — …` — because the Recover tier
|
|
45
|
+
above finds your in-flight work by searching for it. (Under
|
|
46
|
+
`conveyor-local-loop`, its `[local-loop]` prefix replaces this.)
|
|
47
|
+
|
|
48
|
+
**Locally, the parent card stays PARKED for the entire run** — never set it
|
|
49
|
+
InProgress, never Build it; the final `create_pull_request` is what moves it to
|
|
50
|
+
ReviewPR. The mechanism: the pack watchdog and the child-event notifier both
|
|
51
|
+
ignore a parked parent, whereas an ACTIVE parent treats a headless InProgress
|
|
52
|
+
child as a dead agent environment and "recovers" it onto a cloud pod —
|
|
53
|
+
duplicate implementation of a child you are already writing (observed
|
|
54
|
+
2026-07-28).
|
|
55
|
+
|
|
56
|
+
> **Environment.** In a pod the parent is honestly InProgress with a live
|
|
57
|
+
> session, and this parking rule does not apply — it exists specifically to
|
|
58
|
+
> dodge cloud recovery of headless children, which is not a risk when the
|
|
59
|
+
> parent's own session is the thing implementing them.
|
|
60
|
+
|
|
61
|
+
## Each iteration: re-derive, then do the first thing that applies
|
|
62
|
+
|
|
63
|
+
Re-read the parent and `mcp__conveyor__list_subtasks` every time. Never trust
|
|
64
|
+
session memory for pack state.
|
|
65
|
+
|
|
66
|
+
1. **Recover** — a child carrying your claim marker, InProgress, with no PR.
|
|
67
|
+
Its branch may already exist locally or on origin; audit what landed before
|
|
68
|
+
re-implementing.
|
|
69
|
+
2. **Merge** — a child in ReviewPR. See "Merge a child" below.
|
|
70
|
+
3. **Promote** — a `Planning` child whose plan is solid →
|
|
71
|
+
`mcp__conveyor__update_subtask` to `Open`. Genuinely not plannable →
|
|
72
|
+
escalate to parent chat.
|
|
73
|
+
4. **Implement** — the next `Open` child whose dependencies are all met.
|
|
74
|
+
**A dependency counts as met only at ReviewDev/Complete (merged into the
|
|
75
|
+
pack) or Cancelled. ReviewPR is NOT met** — an unmerged PR is not a
|
|
76
|
+
foundation to build on. No `dependsOn` anywhere → ordinal order.
|
|
77
|
+
5. **Finale** — every child at ReviewDev/Complete. See below.
|
|
78
|
+
6. **Babysit** — the final PR is open. Fix red CI and answer review comments
|
|
79
|
+
directly on the pack branch; never claim or work children once the parent is
|
|
80
|
+
in ReviewPR. **Green and quiet is the end:** post the wrap-up to parent chat
|
|
81
|
+
and stop.
|
|
82
|
+
|
|
83
|
+
Work children **one at a time**. Interleaving them thrashes branch state.
|
|
84
|
+
|
|
85
|
+
## Pacing — a pack is many turns, not one
|
|
86
|
+
|
|
87
|
+
A pack of any size will not fit in a single turn, and there is no partial
|
|
88
|
+
credit for a tree left mid-child. Treat **one iteration = one step** (implement
|
|
89
|
+
one child, or merge one, or the finale) and end the turn on a committed,
|
|
90
|
+
pushed state.
|
|
91
|
+
|
|
92
|
+
- Driven by `/loop` (locally) or woken by events (in a pod): do one step, then
|
|
93
|
+
end the turn. Under `/loop` with no interval, arm the next wake before
|
|
94
|
+
ending — short (60–90s) while there is a child ready to work, long
|
|
95
|
+
(1200–1800s) while waiting on something external, and **stop the loop** once
|
|
96
|
+
the finale PR is open and quiet.
|
|
97
|
+
- Invoked bare with no loop: do one step, report where the pack stands and what
|
|
98
|
+
the next step is, and stop. Do not self-schedule.
|
|
99
|
+
|
|
100
|
+
`conveyor-local-loop` supplies its own pacing table and its own claim-marker
|
|
101
|
+
prefix; when running under it, those govern.
|
|
102
|
+
|
|
103
|
+
## Implement a child
|
|
104
|
+
|
|
105
|
+
Reload the child's full plan fresh every time (`mcp__conveyor__get_task` +
|
|
106
|
+
`read_task_chat`) — never work from what you remember of it. **A plan too thin
|
|
107
|
+
for a context-free reader does not halt the pack:** post what is missing to
|
|
108
|
+
parent chat, skip that child, and take the next ready one.
|
|
109
|
+
|
|
110
|
+
Then follow [task-path.md](task-path.md), with one substitution: the child
|
|
111
|
+
branches from the **pack branch**, not `dev`, and its PR's base is the pack
|
|
112
|
+
branch. Name that base explicitly — the default is `dev`, and the two surfaces
|
|
113
|
+
spell the argument differently (`base:` locally, `baseBranch:` in a pod).
|
|
114
|
+
|
|
115
|
+
> **Environment — a pod has no child branches and no child PRs.** Everything
|
|
116
|
+
> above describes the LOCAL model, where each child gets its own branch and its
|
|
117
|
+
> own PR into the pack branch. A pod pack runner has one checkout and one
|
|
118
|
+
> branch, so a child's work is simply **commits on the pack branch**. Do not cut
|
|
119
|
+
> a child branch, and do not call `create_pull_request` for a child: that call
|
|
120
|
+
> always opens the PR for the card the session is bound to, so mid-pack it would
|
|
121
|
+
> open the PARENT's PR early and strand every remaining child. One commit per
|
|
122
|
+
> child keeps the single final PR reviewable child by child.
|
|
123
|
+
|
|
124
|
+
Before opening the child's PR, re-check the parent's status. If it went
|
|
125
|
+
InProgress or ReviewPR, a coordinator took over: post that you are yielding,
|
|
126
|
+
leave the branch pushed, and stop.
|
|
127
|
+
|
|
128
|
+
## Merge a child
|
|
129
|
+
|
|
130
|
+
> **Environment — in a pod there is nothing to merge.** The child's commits are
|
|
131
|
+
> already on the pack branch, so "merge a child" collapses to one write:
|
|
132
|
+
> `update_task(task_id: <child>, status: "ReviewDev")`. Nothing else moves a
|
|
133
|
+
> child — no per-child build fires — so a status you do not write is a board
|
|
134
|
+
> that silently lies. The reviewer-of-record pass below still applies to the
|
|
135
|
+
> commits; only the PR mechanics drop away.
|
|
136
|
+
|
|
137
|
+
1. **Reviewer-of-record pass.** The automated reviewer skips PRs that target a
|
|
138
|
+
pack branch, so this is the only review that child gets — the independent
|
|
139
|
+
review happens later, on the pack's final PR into `dev`. Re-read the child's
|
|
140
|
+
plan, then read the FULL diff (`git diff origin/<pack>...origin/<child>`)
|
|
141
|
+
with reviewer eyes: plan coverage, stray files, pattern consistency, and
|
|
142
|
+
whether the gate pass actually happened. Found a real problem → fix it on
|
|
143
|
+
the child branch (you are also the implementer), re-gate, then continue.
|
|
144
|
+
|
|
145
|
+
You wrote this code, which makes self-review the weak point. Consider an
|
|
146
|
+
independent reviewer — a subagent with the diff and no memory of writing it
|
|
147
|
+
catches what you cannot.
|
|
148
|
+
2. **Merge** via `mcp__conveyor__approve_and_merge_pr`. That call QUEUES the
|
|
149
|
+
merge (a ~30s sweep) rather than merging inline, so always confirm the PR
|
|
150
|
+
actually landed instead of trusting the response.
|
|
151
|
+
|
|
152
|
+
A pack-branch PR carries no CI checks. **Give the queue a couple of minutes
|
|
153
|
+
before concluding it cannot land it** — `passesCheckGate` treats
|
|
154
|
+
`zero_checks` as passed once past `isPastZeroCheckSettling`, and the
|
|
155
|
+
pack-child skip writes a terminal `approved` that satisfies the review gate.
|
|
156
|
+
Confirmed 2026-08-20 (PR #4224, ~4 min) and again 2026-08-27 (two pack-child
|
|
157
|
+
PRs, ~2 min each). The `merge-queue` tag has carried this since 2026-08-20;
|
|
158
|
+
this file and the `pack` tag were the surfaces still asserting otherwise. Checking once
|
|
159
|
+
and falling back immediately costs a rejected push and a reset.
|
|
160
|
+
|
|
161
|
+
Note the merge is a **squash**, so the child's commits are not ancestors of
|
|
162
|
+
the pack branch: `git merge-base --is-ancestor` reads false even when every
|
|
163
|
+
byte landed. Compare content (`git diff origin/<pack> <child-head>`), not
|
|
164
|
+
ancestry.
|
|
165
|
+
|
|
166
|
+
If it genuinely stalls, merge locally instead — GitHub then marks the PR
|
|
167
|
+
merged:
|
|
168
|
+
|
|
169
|
+
```bash
|
|
170
|
+
git checkout <pack> && git pull && git merge --no-ff <child-branch> && git push
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
(Observed 2026-08-26: zero-check pack-branch PRs merged straight through the
|
|
174
|
+
queue twice in a row, so treat the fallback as a fallback, not the expected
|
|
175
|
+
path. Older guidance said the queue could NEVER merge them; that is not what
|
|
176
|
+
the queue does today.)
|
|
177
|
+
|
|
178
|
+
Either way, CONFIRM the child reached ReviewDev (allow ~1 minute for the
|
|
179
|
+
merge webhook). **Still ReviewPR after a local merge is an escalation, not a
|
|
180
|
+
shrug** — it means the parent's recorded `githubBranch` has drifted from the
|
|
181
|
+
real pack branch. Fix the parent record (`mcp__conveyor__update_task` with
|
|
182
|
+
`githubBranch: <pack>`), advance the child by hand (`update_task` →
|
|
183
|
+
`ReviewDev`), and post the drift to parent chat so it is visible.
|
|
184
|
+
3. **Sync `dev` into the pack branch — yours to do, in either environment.**
|
|
185
|
+
(The fan-out cloud path had the server do this before each child launch; a
|
|
186
|
+
session driving the pack itself gets no such help and resolves conflicts
|
|
187
|
+
in-session.)
|
|
188
|
+
|
|
189
|
+
```bash
|
|
190
|
+
git checkout <pack> && git pull && git fetch origin dev && git merge origin/dev --no-edit && git push
|
|
191
|
+
```
|
|
192
|
+
|
|
193
|
+
**Merge, never rebase** — the pack branch is shared with open child PRs and
|
|
194
|
+
WIP refs, and rewriting it breaks them. Conflicts are yours to resolve
|
|
195
|
+
properly; you wrote the code.
|
|
196
|
+
|
|
197
|
+
If the merge drags in unrelated changes or errors, `dev` may have been
|
|
198
|
+
rewound (a revert or force-push). Verify the previous sync point is still an
|
|
199
|
+
ancestor of `origin/dev` (`git merge-base --is-ancestor`); if it is not,
|
|
200
|
+
abort the merge and escalate rather than chasing the noise.
|
|
201
|
+
4. Report the merge to parent chat in a line or two.
|
|
202
|
+
|
|
203
|
+
## Finale
|
|
204
|
+
|
|
205
|
+
1. **Cross-reference.** For the parent plan and EVERY child plan, check the
|
|
206
|
+
pack branch's actual state against the acceptance and verification criteria
|
|
207
|
+
— a real checklist pass, not a vibe. Small gap → fix on the pack branch.
|
|
208
|
+
Substantial gap → a new child card with a plan, and the loop continues.
|
|
209
|
+
2. Pre-PR protocol on the pack branch: sync `origin/dev` FIRST, then ONE
|
|
210
|
+
verification pass scoped to the pack's cumulative diff against `dev`
|
|
211
|
+
(cross-package packs earn the full suite).
|
|
212
|
+
3. `mcp__conveyor__create_pull_request` on the PARENT: `head:` the pack branch,
|
|
213
|
+
`base:` `dev`. The parent moves to ReviewPR. Post the pack summary: what
|
|
214
|
+
shipped per child, how it was verified, what reviewers should look at.
|
|
215
|
+
4. Confirm CI started. **Never approve or merge this PR** — it is the one that
|
|
216
|
+
gets independent review.
|
|
217
|
+
|
|
218
|
+
## Parked protocol
|
|
219
|
+
|
|
220
|
+
After two genuinely different failed approaches on a child, or a decision only
|
|
221
|
+
the user can make: post the reason and the specific question to the child AND
|
|
222
|
+
parent chats, set the child back to `Open`, restore the tree, and take the next
|
|
223
|
+
child whose dependency chain does not run through the parked one. Everything
|
|
224
|
+
remaining blocked → report and stop; the user's reply is the un-park signal.
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
# Task path — one card to a PR
|
|
2
|
+
|
|
3
|
+
The single-card execution core of [conveyor-build](../SKILL.md). Read that
|
|
4
|
+
first: ground rules, environment differences, routing, verification, and
|
|
5
|
+
aftercare all live there and are not repeated here.
|
|
6
|
+
|
|
7
|
+
Use this path when the card has no children, or when it is a pack child whose
|
|
8
|
+
parent is parked.
|
|
9
|
+
|
|
10
|
+
## 1. Claim
|
|
11
|
+
|
|
12
|
+
Re-confirm via `mcp__conveyor__get_task` that the card is still claimable —
|
|
13
|
+
`Open` (or whatever status the user explicitly overrode), no assignee you do
|
|
14
|
+
not expect, no active session. Then:
|
|
15
|
+
|
|
16
|
+
1. `mcp__conveyor__update_task` → `status: "InProgress"`
|
|
17
|
+
2. `mcp__conveyor__post_to_chat` → `[build] claimed — <where you are running>`
|
|
18
|
+
|
|
19
|
+
If the status changed under you between the read and the write, someone else
|
|
20
|
+
took it. Stop rather than compete.
|
|
21
|
+
|
|
22
|
+
## 2. Branch from the card's base — never blindly `dev`
|
|
23
|
+
|
|
24
|
+
The base is the card's `baseBranch`. **A pack child's base is the parent's
|
|
25
|
+
feature branch**, and getting this wrong silently retargets the whole PR.
|
|
26
|
+
|
|
27
|
+
```bash
|
|
28
|
+
git fetch origin <base>
|
|
29
|
+
git checkout -B <feat|fix|chore>/<slug> origin/<base>
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
Two cases that are not a fresh branch:
|
|
33
|
+
|
|
34
|
+
- **The card already has a `githubBranch` with commits on origin.** Resume THAT
|
|
35
|
+
branch. A prior session may have landed real work — audit what is there
|
|
36
|
+
before re-implementing anything.
|
|
37
|
+
- **The remote-tracking ref does not exist locally.** Many checkouts only
|
|
38
|
+
auto-fetch `dev` and the task branch, so `origin/<pack-branch>` may be
|
|
39
|
+
absent even though the branch exists on the remote. Fetch it explicitly
|
|
40
|
+
(`git fetch origin <branch>`) rather than concluding the branch is missing —
|
|
41
|
+
and note that a failed `checkout` leaves you on your PREVIOUS branch, where a
|
|
42
|
+
follow-up `git push` will push the wrong thing.
|
|
43
|
+
|
|
44
|
+
Reinstall dependencies if the lockfile changed.
|
|
45
|
+
|
|
46
|
+
## 3. Implement
|
|
47
|
+
|
|
48
|
+
**Read the plan's closing `## Builder briefing` first, when it has one.** The
|
|
49
|
+
planner wrote it for you specifically — where to start, what is already decided
|
|
50
|
+
and must not be reopened, which traps look correct and are not, and the order to
|
|
51
|
+
verify in. It is the only part of the planner's context that crosses the handoff,
|
|
52
|
+
so reading it after you have picked an approach wastes the thing it exists for.
|
|
53
|
+
A plan without one is not a blocker; just work the plan.
|
|
54
|
+
|
|
55
|
+
Work the plan. Push early — locally that is the only durable state, and in a
|
|
56
|
+
pod it is still what survives independent of WIP autosync.
|
|
57
|
+
|
|
58
|
+
Chat at milestones, not per step.
|
|
59
|
+
|
|
60
|
+
## 4. Gate, PR, aftercare
|
|
61
|
+
|
|
62
|
+
These are identical to the SKILL.md sections of the same names. In particular:
|
|
63
|
+
sync the base BEFORE the verification pass, pass `base:` explicitly to
|
|
64
|
+
`mcp__conveyor__create_pull_request`, and confirm CI started without waiting on
|
|
65
|
+
it.
|
|
66
|
+
|
|
67
|
+
Finish line: the card in ReviewPR with CI started. You do not merge it.
|
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: conveyor-consensus
|
|
3
|
+
description: Sweep everything a Conveyor project is connected to — work channels, cards, tags, meetings, Drive, logs, analytics, the repo — count what actually happened, score the proposals on the table against those counts, and ship an HTML verdict onto the card. Use when the user says "/conveyor-consensus <question>", "get us to consensus on X", "what are people actually complaining about", "does this plan hit the pain", "score these proposals", or when a new stakeholder proposal lands mid-thread and needs scoring against the evidence. Discovers its sources at runtime, so it runs the same in a pod and in a local MCP session.
|
|
4
|
+
---
|
|
5
|
+
|
|
6
|
+
# Conveyor Consensus
|
|
7
|
+
|
|
8
|
+
The genre: **an uninterested observer pulls every relevant source, counts what actually happened, scores what people are proposing against those counts, and ships a doc the team can converge on.** The deliverable is a verdict with receipts, not a summary.
|
|
9
|
+
|
|
10
|
+
## The stance
|
|
11
|
+
|
|
12
|
+
- Write as the outside party with no stake. Verdicts, not summaries: "covered / partial / missed", "possible / not possible by design", "this suggestion doesn't solve anything, here's why."
|
|
13
|
+
- Every claim carries a count and a receipt (card link, permalink, PR link, quote with author + date). Approximate tallies are fine when labeled as such; fabricated precision is not.
|
|
14
|
+
- The evidence outranks the proposals: score proposals against the data, never the other way around.
|
|
15
|
+
- Counter-analysis is part of the job. When a stakeholder idea doesn't survive the data, say so plainly, show the receipt that kills it, and name the nearest thing that does work.
|
|
16
|
+
|
|
17
|
+
## Phase 0: discover what this project actually has
|
|
18
|
+
|
|
19
|
+
**Do this before planning the sweep.** Which sources exist is a per-project fact, not a constant, and guessing wrong wastes a phase either way — assuming a channel that isn't registered, or skipping a Drive that is.
|
|
20
|
+
|
|
21
|
+
1. `get_connection_context` — who and where. Same call in a pod and a local MCP session; it reports the card in a pod and the account/board in an MCP client.
|
|
22
|
+
2. `list_project_integrations` — which of repository, Slack/Discord, email, GCP, Grafana, Google Analytics, Drive, Cloudflare, incidents are configured, plus the registered work channels. Credential-free booleans; this is the map.
|
|
23
|
+
3. `list_project_channels` — the channels an admin registered as agent-reachable, each with a description of what happens there. **The description is the routing signal**: it is how you tell the channel where support complaints land from the one where releases are announced. Only registered channels are reachable at all; an unregistered channel does not exist for this skill regardless of what the bot can see.
|
|
24
|
+
4. **ToolSearch for the optional surfaces** rather than assuming them: `drive_*`, meeting tools, `query_gcp_logs` / `query_grafana_logs`, `get_analytics_summary`. Also probe for supplemental local MCPs the user may have connected — a user-token Slack MCP is the notable one, because it enables true keyword search that Conveyor's own tools cannot do (see the Slack search note below).
|
|
25
|
+
|
|
26
|
+
Announce a one-line plan naming the sources you found, then sweep. **Say what you did not find**, too: "no Drive, no meetings, no Grafana on this project" is information the reader needs to weigh the verdict.
|
|
27
|
+
|
|
28
|
+
## Phase 1: sweep every spot that exists
|
|
29
|
+
|
|
30
|
+
| Source | How |
|
|
31
|
+
| ------ | --- |
|
|
32
|
+
| Work channels | `read_channel_messages` per registered readable channel, paging back with `olderCursor` until the window covers the question. `authorIsBot` separates the team's discussion from Conveyor's own card feed — check it before treating a message as a teammate's. Read thread replies (`threadTs`) where a thread carries the argument. |
|
|
33
|
+
| Conveyor cards | `search_tasks` with ALL `typeFilters` (task, incident, suggestion) and several keyword variants — the term, the term plus symptom words, the adjacent nouns people actually use. Incidents carry fingerprint dedup, so an incident's upvote count is itself a frequency signal. Check whether a decision card already exists; the doc is usually its input. |
|
|
34
|
+
| Tags | `list_tags` then `get_tag` on the relevant ones — the overview is the project's own domain vocabulary, and it names the subsystems your categories should line up with. |
|
|
35
|
+
| Card chat | `read_task_chat` on the cards the search surfaced. The argument usually lives in the chat, not the description. |
|
|
36
|
+
| Meetings | If the meeting tools exist, list and read the ones in the window. A transcript is the densest source of "what people actually said" you will find. |
|
|
37
|
+
| Drive | `drive_list_files` + `drive_read_file` when the project has a folder — specs, retros, and operational sheets live there. A spreadsheet's columns are the incumbent data model and its stalest rows are the pain. |
|
|
38
|
+
| Logs | `query_gcp_logs` / `query_grafana_logs` when the question is about failures rather than opinions. A frequency count from logs outranks any recollection of how often something breaks. |
|
|
39
|
+
| Analytics | `get_analytics_summary` when the question touches traffic, adoption, or "did anyone use it". |
|
|
40
|
+
| Repo | Prior-art check: has someone already built or started this? `rg`, `git log`, branches, the knowledge graph when present. **A negative result — "no trace of X in the codebase" — is a finding worth printing.** |
|
|
41
|
+
| Vendor / product docs | Try `https://<docs-host>/llms.txt` first; many doc sites ship a full index. Verify every "possible / impossible" ruling against a primary page fetched THIS session and link it. Vendors often have more than one API surface (legacy + current); check both before ruling a capability gap. |
|
|
42
|
+
|
|
43
|
+
**Slack keyword search is not available to this skill.** `search.messages` requires a USER token; Conveyor's bot cannot call it. `read_channel_messages` is history paging, not search. So either page the relevant channels and filter model-side, or — when the user has a personal Slack MCP connected — use that for search and say so. **The method footnote must state which mode ran**, because "I read the last 300 messages in two channels" and "I searched the workspace" are different evidence bases and a reader deserves to know which one is behind the counts.
|
|
44
|
+
|
|
45
|
+
Sweep hygiene:
|
|
46
|
+
|
|
47
|
+
- **Delegate bulk sweeps to a subagent** so oversized pages never enter the main thread. Give it the exact queries, pagination instructions, the tool-loading line, and a strict deliverable spec: categorized findings with counts, sources, a few dated example incidents each with a link, a raw-coverage note (queries run, volume reviewed, date range, dominant sources), and a surprises section. Cap the report length.
|
|
48
|
+
- Record who reported each incident and when. Attribution splits ("their side vs ours", "process vs code defect") and time windows ("only since <month>") are the follow-up questions every single time; keep the underlying dated list so re-slicing is cheap.
|
|
49
|
+
|
|
50
|
+
## Phase 2: analysis
|
|
51
|
+
|
|
52
|
+
1. **Bin the evidence into named categories** with incident tallies — not message counts; one saga is one incident. Rank by frequency. Pull a few verbatim quotes that carry the tone, each with author, source, and date.
|
|
53
|
+
2. **Capability map** when a vendor or tool is involved: their surface versus our wants, item by item, each with a link.
|
|
54
|
+
3. **Score every stakeholder proposal** against the FULL category list: COVERED / PARTIAL (symptom yes, root cause no) / MISSED, with one sentence of why each. Score proposals separately, then head-to-head.
|
|
55
|
+
4. **Verify the ambitious claims.** Any proposal line hinging on "the API can (or can't) do X" gets a primary-source check before scoring — these checks regularly flip verdicts.
|
|
56
|
+
5. **Build the merged plan**: the union of the proposals plus the gaps nobody wrote down, every line carrying a feasibility ruling — supported by the vendor (with the doc link), configuration not code, our build, or not possible (say why, and whether the impossibility is actually desirable).
|
|
57
|
+
6. **Separate policy decisions from features.** Some open items are rulings only humans can make; flag them explicitly rather than designing around them silently.
|
|
58
|
+
|
|
59
|
+
## Phase 3: the doc
|
|
60
|
+
|
|
61
|
+
Build from `references/doc-template.html` — a working skeleton with three-state theme tokens, a div bar chart, stacked scoreboards, verdict-chip tables, quote blocks, and a method footnote already styled. In a local session, load `artifact-design` and `dataviz` first; in a pod those skills are absent, and the template carries enough of their decisions to stand alone.
|
|
62
|
+
|
|
63
|
+
**The template is self-contained on purpose and must stay that way.** The report is served from the API under a sandbox CSP (`sandbox; default-src 'none'; style-src 'unsafe-inline'; img-src data:`), which blocks external stylesheets, webfonts, scripts, and remote images. A JS charting library cannot run; a linked font fails silently and the doc renders in a fallback face with no error. Inline CSS, div/CSS charts, and `data:` URIs only.
|
|
64
|
+
|
|
65
|
+
Doc anatomy (drop sections that don't apply, keep the order):
|
|
66
|
+
|
|
67
|
+
1. **Eyebrow + name + dek + meta line** — corpus size, date range, source count, what decision it feeds.
|
|
68
|
+
2. **TL;DR box** — the verdict in 2-3 bold-led paragraphs. A reader who stops here can act.
|
|
69
|
+
3. **The evidence** — frequency bar chart (single hue, direct value labels), a note naming dominant sources, then the tone quotes.
|
|
70
|
+
4. **What already exists** — incumbent systems and prior art, including negative results.
|
|
71
|
+
5. **Proposal scorecards** — one chip table per proposal, then a head-to-head stacked scoreboard including a "merged" row; it sells the synthesis.
|
|
72
|
+
6. **The merged plan** with per-item feasibility chips and links.
|
|
73
|
+
7. **The two cents** — a handful of numbered recommendations, each an argument, not a platitude.
|
|
74
|
+
8. **Method footnote** — queries, counts, date range, which channels were paged versus searched, what was verified against primary sources, and **what was not done**.
|
|
75
|
+
|
|
76
|
+
Chart rules that repeatedly matter: plain div/CSS charts over JS; one strong chart beats two mushy ones; never chart numbers you had to invent — a chip list or prose with "~" beats a fake-precise graph; status chips always carry text, never colour alone.
|
|
77
|
+
|
|
78
|
+
## Phase 4: ship it onto the card
|
|
79
|
+
|
|
80
|
+
1. **`upload_attachment`** with the HTML file — title it with the question, so the card's attachment list reads as a question rather than a filename. This is the required path and the only one that works in a pod.
|
|
81
|
+
2. **Post the verdict to card chat**: the returned link plus a three-bullet summary. Someone scanning the card should get the answer without opening the report.
|
|
82
|
+
3. **When the ask was "align the channel"**, post the summary and link into the relevant work channel with `post_channel_message` — but only into a channel whose registration grants posting, and only when the user asked for that. A consensus doc arriving unbidden in a team's channel is a different act from one attached to a card.
|
|
83
|
+
4. **Local sessions may also publish an Artifact** for a shareable URL, and `SendUserFile` the HTML — both are additive. In a pod neither exists; the attachment is the deliverable.
|
|
84
|
+
5. **Revisions re-upload to the SAME card.** The doc is living; never fork it into `report-v2.html`.
|
|
85
|
+
|
|
86
|
+
## Phase 5: the consensus loop
|
|
87
|
+
|
|
88
|
+
The doc is the midpoint, not the end. Expect and serve:
|
|
89
|
+
|
|
90
|
+
- **Follow-up slices**: "table of every incident with details", "only this quarter", "how many were X versus Y", "day-by-day with what would have prevented it". Answer from the kept evidence list; fold into the doc only what changes a verdict.
|
|
91
|
+
- **Proposal reactions**: score new and revised proposals as they land, including negatively, with receipts, and re-upload.
|
|
92
|
+
|
|
93
|
+
## Environment honesty
|
|
94
|
+
|
|
95
|
+
State degradations in the method footnote rather than skipping them silently:
|
|
96
|
+
|
|
97
|
+
- **No WebSearch/WebFetch in a pod** — vendor-doc claims cannot be verified against primary sources there. Print "not verified against primary docs" on the affected lines instead of dropping the check quietly.
|
|
98
|
+
- **No registered work channels** — say so. It usually means the team's discussion is somewhere this skill cannot see, which is the single most important caveat a reader can have.
|
|
99
|
+
- **Bot-visible history only** — the bot reads channels it was invited to. A channel the team talks in but never invited the bot to is invisible, and that absence does not show up as an error anywhere.
|
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
<!-- Consensus doc skeleton. Proven treatment: observer-memo, banker-blue accent.
|
|
2
|
+
Swap palette/type ONLY if the subject demands a different identity.
|
|
3
|
+
Replace every UPPERCASE placeholder. Delete sections that don't apply; keep the order.
|
|
4
|
+
|
|
5
|
+
SELF-CONTAINED ON PURPOSE — do not add external references.
|
|
6
|
+
This file is uploaded to a card and served by the API under a sandbox CSP
|
|
7
|
+
("sandbox; default-src 'none'; style-src 'unsafe-inline'; img-src data:").
|
|
8
|
+
That blocks webfont stylesheets, external CSS/JS, and remote images. The
|
|
9
|
+
upstream grimoire template linked Google Fonts; it is replaced here with a
|
|
10
|
+
system stack, because under this CSP the link fails silently and the doc
|
|
11
|
+
renders in a fallback font with no error. Same rule for charts: the div/CSS
|
|
12
|
+
bars below work, a JS charting library cannot run at all.
|
|
13
|
+
|
|
14
|
+
Images, if you truly need one, must be data: URIs. -->
|
|
15
|
+
<title>DOC NAME</title>
|
|
16
|
+
<style>
|
|
17
|
+
:root {
|
|
18
|
+
--bg: #f7f9fb; --surface: #ffffff;
|
|
19
|
+
--ink: #16222e; --ink-2: #48596a; --ink-3: #74838f;
|
|
20
|
+
--line: #d9e0e7; --line-soft: #e8edf1;
|
|
21
|
+
--accent: #20618f; --accent-soft: #dcebf5;
|
|
22
|
+
--bar: #2e6f9e; --bar-hover: #20618f;
|
|
23
|
+
--good-ink: #176a44; --good-bg: #ddf0e5;
|
|
24
|
+
--warn-ink: #8a5d00; --warn-bg: #f6ecd2;
|
|
25
|
+
--miss-ink: #a13529; --miss-bg: #f9e2de;
|
|
26
|
+
--neutral-ink: #55606b; --neutral-bg: #e8ecf0;
|
|
27
|
+
--quote-rail: #c3d5e2;
|
|
28
|
+
}
|
|
29
|
+
/* Three-state theming: bare :root = light; media block guarded against explicit light; data-theme block wins for the toggle. */
|
|
30
|
+
@media (prefers-color-scheme: dark) {
|
|
31
|
+
:root:not([data-theme="light"]) {
|
|
32
|
+
--bg: #101720; --surface: #161f2a;
|
|
33
|
+
--ink: #e4ebf2; --ink-2: #a4b3c1; --ink-3: #7d8c9a;
|
|
34
|
+
--line: #2b3947; --line-soft: #223040;
|
|
35
|
+
--accent: #6fadd9; --accent-soft: #1d3346;
|
|
36
|
+
--bar: #4c88b4; --bar-hover: #6fadd9;
|
|
37
|
+
--good-ink: #57c48f; --good-bg: #16311f;
|
|
38
|
+
--warn-ink: #d9a94a; --warn-bg: #34290f;
|
|
39
|
+
--miss-ink: #e2857a; --miss-bg: #3a1d18;
|
|
40
|
+
--neutral-ink: #a4b3c1; --neutral-bg: #222d38;
|
|
41
|
+
--quote-rail: #33506a;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
:root[data-theme="dark"] {
|
|
45
|
+
--bg: #101720; --surface: #161f2a;
|
|
46
|
+
--ink: #e4ebf2; --ink-2: #a4b3c1; --ink-3: #7d8c9a;
|
|
47
|
+
--line: #2b3947; --line-soft: #223040;
|
|
48
|
+
--accent: #6fadd9; --accent-soft: #1d3346;
|
|
49
|
+
--bar: #4c88b4; --bar-hover: #6fadd9;
|
|
50
|
+
--good-ink: #57c48f; --good-bg: #16311f;
|
|
51
|
+
--warn-ink: #d9a94a; --warn-bg: #34290f;
|
|
52
|
+
--miss-ink: #e2857a; --miss-bg: #3a1d18;
|
|
53
|
+
--neutral-ink: #a4b3c1; --neutral-bg: #222d38;
|
|
54
|
+
--quote-rail: #33506a;
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
body { background: var(--bg); color: var(--ink); font-family: ui-sans-serif, system-ui, "Segoe UI", "Helvetica Neue", Arial, sans-serif; font-size: 16px; line-height: 1.6; margin: 0; padding: 3rem 1.25rem 5rem; }
|
|
58
|
+
.doc { max-width: 46rem; margin: 0 auto; }
|
|
59
|
+
|
|
60
|
+
.eyebrow { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.72rem; letter-spacing: 0.14em; text-transform: uppercase; color: var(--ink-3); margin: 0 0 0.75rem; }
|
|
61
|
+
h1 { font-family: ui-serif, Georgia, "Times New Roman", serif; font-weight: 700; font-size: 2.3rem; line-height: 1.15; margin: 0 0 0.5rem; text-wrap: balance; }
|
|
62
|
+
.dek { color: var(--ink-2); font-size: 1.05rem; max-width: 40rem; margin: 0 0 0.75rem; }
|
|
63
|
+
.meta { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.75rem; color: var(--ink-3); margin: 0 0 2.5rem; }
|
|
64
|
+
|
|
65
|
+
h2 { font-family: ui-serif, Georgia, "Times New Roman", serif; font-weight: 600; font-size: 1.45rem; line-height: 1.25; margin: 3rem 0 0.9rem; text-wrap: balance; }
|
|
66
|
+
h2 .sec { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-weight: 500; font-size: 0.78rem; color: var(--accent); display: block; letter-spacing: 0.12em; margin-bottom: 0.35rem; }
|
|
67
|
+
p { margin: 0 0 1rem; max-width: 42rem; }
|
|
68
|
+
a { color: var(--accent); text-decoration-thickness: 1px; text-underline-offset: 2px; }
|
|
69
|
+
strong { font-weight: 600; }
|
|
70
|
+
|
|
71
|
+
.tldr { background: var(--surface); border: 1px solid var(--line); border-left: 3px solid var(--accent); border-radius: 6px; padding: 1.15rem 1.35rem; margin: 0 0 1rem; }
|
|
72
|
+
.tldr p { margin: 0 0 0.7rem; }
|
|
73
|
+
.tldr p:last-child { margin: 0; }
|
|
74
|
+
|
|
75
|
+
figure { background: var(--surface); border: 1px solid var(--line); border-radius: 6px; margin: 1.5rem 0; padding: 1.25rem 1.35rem 1.1rem; }
|
|
76
|
+
figcaption { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.72rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); margin-bottom: 1rem; }
|
|
77
|
+
.fignote { font-size: 0.78rem; color: var(--ink-3); margin-top: 0.9rem; }
|
|
78
|
+
|
|
79
|
+
/* Frequency bar chart: single hue (one series = no legend), direct value labels, hover emphasis + title tooltips. */
|
|
80
|
+
.barchart { display: flex; flex-direction: column; gap: 7px; }
|
|
81
|
+
.barrow { display: grid; grid-template-columns: 13.5rem 1fr 2rem; gap: 0.7rem; align-items: center; font-size: 0.85rem; }
|
|
82
|
+
.barrow .lbl { color: var(--ink-2); text-align: right; line-height: 1.25; }
|
|
83
|
+
.track { background: var(--line-soft); border-radius: 3px; height: 16px; }
|
|
84
|
+
.fill { background: var(--bar); border-radius: 3px 4px 4px 3px; height: 100%; transition: background 120ms ease; }
|
|
85
|
+
.barrow:hover .fill { background: var(--bar-hover); }
|
|
86
|
+
.barrow .val { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-variant-numeric: tabular-nums; font-size: 0.8rem; color: var(--ink-2); }
|
|
87
|
+
.barrow:hover .lbl, .barrow:hover .val { color: var(--ink); }
|
|
88
|
+
|
|
89
|
+
/* Head-to-head stacked coverage bars, one row per proposal (+ a Merged row). */
|
|
90
|
+
.compare { display: flex; flex-direction: column; gap: 10px; }
|
|
91
|
+
.cmprow { display: grid; grid-template-columns: 8.5rem 1fr; gap: 0.8rem; align-items: center; }
|
|
92
|
+
.cmprow .who { font-size: 0.85rem; font-weight: 600; text-align: right; color: var(--ink-2); }
|
|
93
|
+
.scoreboard { display: flex; height: 26px; border-radius: 5px; overflow: hidden; gap: 2px; }
|
|
94
|
+
.scoreboard div { display: flex; align-items: center; justify-content: center; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.76rem; font-weight: 500; min-width: 1.4rem; }
|
|
95
|
+
.sc-good { background: var(--good-bg); color: var(--good-ink); }
|
|
96
|
+
.sc-warn { background: var(--warn-bg); color: var(--warn-ink); }
|
|
97
|
+
.sc-miss { background: var(--miss-bg); color: var(--miss-ink); }
|
|
98
|
+
.sc-legend { display: flex; gap: 1.4rem; margin-top: 0.8rem; font-size: 0.78rem; color: var(--ink-2); flex-wrap: wrap; }
|
|
99
|
+
.sc-legend span { display: inline-flex; align-items: center; gap: 0.4rem; }
|
|
100
|
+
.swatch { width: 10px; height: 10px; border-radius: 2px; display: inline-block; }
|
|
101
|
+
|
|
102
|
+
/* Verdict + feasibility chips: text always, never color alone. */
|
|
103
|
+
.chip { display: inline-block; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.68rem; font-weight: 500; letter-spacing: 0.06em; padding: 0.15rem 0.5rem; border-radius: 999px; white-space: nowrap; }
|
|
104
|
+
.chip-good { background: var(--good-bg); color: var(--good-ink); } /* COVERED / API ✓ */
|
|
105
|
+
.chip-warn { background: var(--warn-bg); color: var(--warn-ink); } /* PARTIAL */
|
|
106
|
+
.chip-miss { background: var(--miss-bg); color: var(--miss-ink); } /* MISSED / NOT POSSIBLE */
|
|
107
|
+
.chip-api { background: var(--accent-soft); color: var(--accent); } /* vendor-supported rulings */
|
|
108
|
+
.chip-ours { background: var(--neutral-bg); color: var(--neutral-ink); } /* OUR BUILD / CONFIG */
|
|
109
|
+
|
|
110
|
+
.tablewrap { overflow-x: auto; margin: 1.5rem 0; border: 1px solid var(--line); border-radius: 6px; background: var(--surface); }
|
|
111
|
+
table { border-collapse: collapse; width: 100%; font-size: 0.86rem; }
|
|
112
|
+
th { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.68rem; letter-spacing: 0.1em; text-transform: uppercase; color: var(--ink-3); font-weight: 500; text-align: left; padding: 0.7rem 1rem; border-bottom: 1px solid var(--line); }
|
|
113
|
+
td { padding: 0.6rem 1rem; border-bottom: 1px solid var(--line-soft); vertical-align: top; color: var(--ink-2); }
|
|
114
|
+
td:first-child { color: var(--ink); font-weight: 600; }
|
|
115
|
+
tr:last-child td { border-bottom: none; }
|
|
116
|
+
td.nowrap { white-space: nowrap; }
|
|
117
|
+
|
|
118
|
+
blockquote { border-left: 3px solid var(--quote-rail); margin: 1.1rem 0; padding: 0.1rem 0 0.1rem 1.1rem; color: var(--ink-2); font-style: italic; max-width: 40rem; }
|
|
119
|
+
blockquote cite { display: block; font-style: normal; font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; font-size: 0.72rem; color: var(--ink-3); margin-top: 0.35rem; }
|
|
120
|
+
|
|
121
|
+
ol.twocents { padding-left: 1.3rem; max-width: 42rem; }
|
|
122
|
+
ol.twocents li { margin-bottom: 0.85rem; }
|
|
123
|
+
ol.twocents li::marker { font-family: ui-monospace, SFMono-Regular, Menlo, Consolas, monospace; color: var(--accent); font-size: 0.85rem; }
|
|
124
|
+
|
|
125
|
+
.method { border-top: 1px solid var(--line); margin-top: 3.5rem; padding-top: 1.25rem; font-size: 0.8rem; color: var(--ink-3); max-width: 42rem; }
|
|
126
|
+
</style>
|
|
127
|
+
|
|
128
|
+
<div class="doc">
|
|
129
|
+
<p class="eyebrow">Outside read · No stake in the outcome</p>
|
|
130
|
+
<h1>DOC NAME</h1>
|
|
131
|
+
<p class="dek">ONE-SENTENCE FRAME: who pulled what, and what this doc rules on.</p>
|
|
132
|
+
<p class="meta">~N messages · DATE → DATE · N channels/sources · input to DECISION/CARD</p>
|
|
133
|
+
|
|
134
|
+
<div class="tldr">
|
|
135
|
+
<p><strong>VERDICT SENTENCE.</strong> Two to three bold-led paragraphs; a reader who stops here can act.</p>
|
|
136
|
+
</div>
|
|
137
|
+
|
|
138
|
+
<h2><span class="sec">01</span>What the evidence says</h2>
|
|
139
|
+
<p>How findings were binned; what a count means.</p>
|
|
140
|
+
<figure>
|
|
141
|
+
<figcaption>FINDINGS BY INCIDENT COUNT, RANGE</figcaption>
|
|
142
|
+
<div class="barchart">
|
|
143
|
+
<!-- width = count / max * 100%. title attr = one-line version for hover. -->
|
|
144
|
+
<div class="barrow" title="ONE-LINER"><span class="lbl">CATEGORY</span><div class="track"><div class="fill" style="width:100%"></div></div><span class="val">15</span></div>
|
|
145
|
+
<div class="barrow" title="ONE-LINER"><span class="lbl">CATEGORY</span><div class="track"><div class="fill" style="width:60%"></div></div><span class="val">9</span></div>
|
|
146
|
+
</div>
|
|
147
|
+
<div class="fignote">Dominant sources; hover note.</div>
|
|
148
|
+
</figure>
|
|
149
|
+
<blockquote>"VERBATIM QUOTE"<cite>WHO · #channel · DATE</cite></blockquote>
|
|
150
|
+
|
|
151
|
+
<h2><span class="sec">02</span>What already exists</h2>
|
|
152
|
+
<p>Incumbent system, prior art, negative results ("no payout code in the repo").</p>
|
|
153
|
+
|
|
154
|
+
<h2><span class="sec">03</span>PROPOSAL A, scored</h2>
|
|
155
|
+
<div class="tablewrap">
|
|
156
|
+
<table>
|
|
157
|
+
<thead><tr><th>Pain / need</th><th>Verdict</th><th>Why</th></tr></thead>
|
|
158
|
+
<tbody>
|
|
159
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-good">COVERED</span></td><td>ONE SENTENCE.</td></tr>
|
|
160
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-warn">PARTIAL</span></td><td>Symptom yes, root cause no.</td></tr>
|
|
161
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-miss">MISSED</span></td><td>ONE SENTENCE.</td></tr>
|
|
162
|
+
</tbody>
|
|
163
|
+
</table>
|
|
164
|
+
</div>
|
|
165
|
+
|
|
166
|
+
<!-- Repeat 03 per proposal, then head-to-head: -->
|
|
167
|
+
<figure>
|
|
168
|
+
<figcaption>Head to head: N items each</figcaption>
|
|
169
|
+
<div class="compare">
|
|
170
|
+
<div class="cmprow"><span class="who">Proposal A</span>
|
|
171
|
+
<div class="scoreboard"><div class="sc-good" style="flex:3">3</div><div class="sc-warn" style="flex:5">5</div><div class="sc-miss" style="flex:3">3</div></div>
|
|
172
|
+
</div>
|
|
173
|
+
<div class="cmprow"><span class="who">Merged</span>
|
|
174
|
+
<div class="scoreboard"><div class="sc-good" style="flex:10">10</div><div class="sc-warn" style="flex:1">1</div></div>
|
|
175
|
+
</div>
|
|
176
|
+
</div>
|
|
177
|
+
<div class="sc-legend">
|
|
178
|
+
<span><span class="swatch" style="background:var(--good-bg);border:1px solid var(--good-ink)"></span>covered</span>
|
|
179
|
+
<span><span class="swatch" style="background:var(--warn-bg);border:1px solid var(--warn-ink)"></span>symptom yes, root cause no</span>
|
|
180
|
+
<span><span class="swatch" style="background:var(--miss-bg);border:1px solid var(--miss-ink)"></span>not in the plan</span>
|
|
181
|
+
</div>
|
|
182
|
+
</figure>
|
|
183
|
+
|
|
184
|
+
<h2><span class="sec">04</span>The merged plan, with rulings</h2>
|
|
185
|
+
<div class="tablewrap">
|
|
186
|
+
<table>
|
|
187
|
+
<thead><tr><th>Blueprint item</th><th>Feasibility</th><th>Detail</th></tr></thead>
|
|
188
|
+
<tbody>
|
|
189
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-api">API ✓</span></td><td>Ruling + <a href="ENDPOINT-DOC-URL">endpoint link</a>.</td></tr>
|
|
190
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-ours">OUR BUILD</span></td><td>Ruling.</td></tr>
|
|
191
|
+
<tr><td>ITEM</td><td class="nowrap"><span class="chip chip-miss">NOT POSSIBLE</span></td><td>Why, and whether the impossibility is desirable.</td></tr>
|
|
192
|
+
</tbody>
|
|
193
|
+
</table>
|
|
194
|
+
</div>
|
|
195
|
+
|
|
196
|
+
<h2><span class="sec">05</span>The two cents</h2>
|
|
197
|
+
<ol class="twocents">
|
|
198
|
+
<li><strong>RECOMMENDATION.</strong> The argument for it.</li>
|
|
199
|
+
</ol>
|
|
200
|
+
|
|
201
|
+
<div class="method">
|
|
202
|
+
<p><strong>Method.</strong> Queries run, counts, date range, what was reviewed in full, what was verified against primary sources, and what was NOT done.</p>
|
|
203
|
+
</div>
|
|
204
|
+
</div>
|