@vegastack/skills 0.10.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -0
- package/package.json +1 -1
- package/skill/dev-architect/SKILL.md +7 -4
- package/skill/dev-architect/references/conventions.md +93 -0
- package/skill/dev-chronicle/SKILL.md +45 -0
- package/skill/dev-chronicle/agents/openai.yaml +4 -0
- package/skill/dev-chronicle/references/conventions.md +93 -0
- package/skill/dev-chronicle/refresh/REFRESH.md +3 -0
- package/skill/dev-chronicle/refresh/sources.json +6 -0
- package/skill/dev-debug/SKILL.md +43 -0
- package/skill/dev-debug/agents/openai.yaml +4 -0
- package/skill/dev-debug/references/conventions.md +93 -0
- package/skill/dev-debug/references/loop-ladder.md +20 -0
- package/skill/dev-debug/refresh/REFRESH.md +3 -0
- package/skill/dev-debug/refresh/sources.json +6 -0
- package/skill/dev-implement/SKILL.md +41 -36
- package/skill/dev-implement/references/conventions.md +93 -0
- package/skill/dev-implement/references/ledger-and-resume.md +27 -0
- package/skill/dev-implement/scripts/evidence-check.mjs +57 -0
- package/skill/dev-implement/scripts/lib/gh.mjs +93 -0
- package/skill/dev-implement/scripts/preflight.mjs +101 -0
- package/skill/dev-intake/SKILL.md +39 -33
- package/skill/dev-intake/references/brief-template.md +27 -12
- package/skill/dev-intake/references/conventions.md +93 -0
- package/skill/dev-intake/scripts/brief-lint.mjs +87 -0
- package/skill/dev-plan/SKILL.md +53 -0
- package/skill/dev-plan/agents/openai.yaml +4 -0
- package/skill/dev-plan/references/conventions.md +93 -0
- package/skill/dev-plan/references/plan-format.md +54 -0
- package/skill/dev-plan/refresh/REFRESH.md +3 -0
- package/skill/dev-plan/refresh/sources.json +6 -0
- package/skill/dev-plan/scripts/plan-lint.mjs +86 -0
- package/skill/dev-review/SKILL.md +69 -0
- package/skill/dev-review/agents/openai.yaml +4 -0
- package/skill/dev-review/assets/review-known-patterns.md.template +30 -0
- package/skill/dev-review/references/conventions.md +93 -0
- package/skill/dev-review/references/cross-agent.md +39 -0
- package/skill/dev-review/references/dispatch-prompts.md +104 -0
- package/skill/dev-review/references/security-axis.md +33 -0
- package/skill/dev-review/refresh/REFRESH.md +3 -0
- package/skill/dev-review/refresh/sources.json +6 -0
- package/skill/dev-setup/SKILL.md +7 -5
- package/skill/dev-setup/assets/agents-section.md.template +2 -2
- package/skill/dev-setup/assets/dev-profile.md.template +4 -3
- package/skill/dev-setup/references/conventions.md +93 -0
- package/skill/dev-ship/SKILL.md +14 -7
- package/skill/dev-ship/references/conventions.md +93 -0
- package/skill/dev-ship/references/runbook.md +1 -1
- package/skill/dev-ship/scripts/ship-gate.mjs +213 -0
- package/skill/dev-status/SKILL.md +45 -0
- package/skill/dev-status/agents/openai.yaml +4 -0
- package/skill/dev-status/references/conventions.md +93 -0
- package/skill/dev-status/refresh/REFRESH.md +3 -0
- package/skill/dev-status/refresh/sources.json +6 -0
- package/skill/dev-status/scripts/status.mjs +152 -0
- package/skill/skillify/SKILL.md +1 -1
- package/skill/skillify/references/eval-playbook.md +6 -0
- package/skill-integrity.json +78 -14
|
@@ -1,70 +1,75 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: dev-implement
|
|
3
|
-
description: Implement an approved GitHub issue end to end without further user input. Use when given an issue to build
|
|
3
|
+
description: Implement an approved GitHub issue end to end without further user input. Use when given an issue to build — "do issue 12", "implement" plus an issue URL or number, "pick up the next ready issue", "go dark on" an issue — when resuming a dead or compacted session's working issue the operator hands over, when returning to apply corrections the user left on a for-operator issue, or when the user directly asks in chat for a small fix. Not for writing or approving issues (dev-intake), planning them (dev-plan), reviewing finished work (dev-review), or creating PRs and merging (dev-ship).
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# dev-implement
|
|
7
7
|
|
|
8
|
-
One issue, one session, end to end: preflight → claim → build dark → verify → review → evidence
|
|
8
|
+
One issue, one session, end to end: preflight → claim → build dark → verify → review → evidence → stop. The operator reads the result in the issue on their own time; nothing here creates a PR or merges — those are `dev-ship`, on the operator's word. Artifact formats follow the `dev-setup` skill's `references/conventions.md`; the ledger discipline lives in [ledger-and-resume](references/ledger-and-resume.md).
|
|
9
9
|
|
|
10
|
-
Nearest
|
|
10
|
+
Nearest neighbors: `dev-plan` writes the plan this skill executes task by task; `dev-review` judges the result; issues that turn out to need decisions go back through `needs-operator`, never guessed. `.vegastack/dev.md` missing → run `dev-setup` first. Read dev.md before anything; its knobs govern this skill, and the `## Architecture` section governs stack-touching choices.
|
|
11
11
|
|
|
12
|
-
## Direct requests
|
|
12
|
+
## Direct requests — trivial only, tightly bounded
|
|
13
13
|
|
|
14
|
-
|
|
14
|
+
When the operator directly asks in chat for a change, their words are the approval — build, verify, report; no issue needed. The bound is **trivial**: the moment the work exceeds it — a behavior change beyond the asked words, a new dependency, more than 1–2 files — stop and route to `dev-intake` instead of continuing. Branch `<type>/<slug>`; the changelog and chronicle rules apply unchanged when behavior changes; shipping still goes through dev-ship's words.
|
|
15
15
|
|
|
16
16
|
## Preflight — all must hold, or stop and say which failed
|
|
17
17
|
|
|
18
|
-
- `gh
|
|
19
|
-
-
|
|
20
|
-
-
|
|
21
|
-
- Read the complete brief, plus parent issue and milestone for context. If the brief leaves a material decision open — including an unresolved Assumptions entry — do not start: label `needs-operator`, comment the smallest question that unblocks it, stop.
|
|
22
|
-
- Re-verify the brief against reality before coding: its cited touch points against the current code (things drift between approval and execution), and volatile dependency claims when stale or version-sensitive (per `dev-architect`'s verify protocol). Reality contradicting the brief is a stop — label `needs-operator` with the discrepancy; an approved brief is never a license to improvise past what's actually there.
|
|
18
|
+
- Run the deterministic guard first: `node <path-to-this-skill>/scripts/preflight.mjs --issue <n> --me $(gh api user -q .login) --json` (add `--repo <o/r> --dev-md <path>` when running outside the project root) — exit 2 stops you with its reasons (open + `ready` state, approval marker, scope label, plan approval on full-plan, Assumptions section, blockers, assignee, repo match); exit 1 passes with warnings — read them into the ledger. Resume and corrections runs pass `--expect working` / `--expect for-operator`.
|
|
19
|
+
- Then the judgment checks: read the complete brief plus parent issue and milestone for context; re-verify the brief's touch points against the current code (things drift between approval and execution) — including the version-impact line; volatile dependency claims per `dev-architect`'s verify protocol; a full-plan issue's plan still matches reality. A material decision left open — even outside a formal Assumptions section — or reality contradicting brief or plan is a stop: one `handback` comment with the smallest question, `needs-operator`.
|
|
20
|
+
- Resuming a dead session's issue: the operator's explicit handover is required; then follow the resume protocol in [ledger-and-resume](references/ledger-and-resume.md) — brief → plan → ledger → `git log`, nothing else.
|
|
23
21
|
|
|
24
|
-
## Claim
|
|
22
|
+
## Claim
|
|
25
23
|
|
|
26
|
-
Assign yourself, swap `ready` → `working
|
|
24
|
+
Assign yourself, swap `ready` → `working`, branch from the default branch per dev.md's `branch:` knob, and **create the ledger comment as your first write**. Record each task's base sha before starting it.
|
|
27
25
|
|
|
28
|
-
## Build — dark
|
|
26
|
+
## Build — dark, test-first, checkpointed
|
|
29
27
|
|
|
30
|
-
No progress updates, no questions. A
|
|
28
|
+
No progress updates, no questions. A `fix:` issue's diagnosis runs under the `dev-debug` skill — its phases govern the investigation inside this dark mode, and its winning suspect feeds the evidence comment. A spike the brief flagged runs first — its result opens the evidence comment and shapes the rest of the build. Then work the plan task by task:
|
|
29
|
+
|
|
30
|
+
- **Red before green.** Write the failing test first — at the seams the brief names, never elsewhere — watch it fail for the stated reason, implement the minimal code, watch it pass. One slice at a time. The tests-are-real rubric (implementation-coupled, tautological, horizontal-sliced — defined in `dev-review`'s dispatch prompts) applies to your own tests before a reviewer ever sees them.
|
|
31
|
+
- **Checkpoint the ledger** after every task (tick the plan checkbox in the same pass) and at every ruling, per the reference.
|
|
32
|
+
- **Transitory artifacts** — subagent reports, scratch diffs, drafts — live in `.vegastack/.tmp/<issue>-<slug>/`; subagents write full output to files there and return short status.
|
|
33
|
+
- Decide routine things yourself and ledger the rulings. A structural choice mid-build — a new dependency, table, or service — checks `dev-architect`'s trigger discipline first; a moving part with no named trigger is a stop condition.
|
|
34
|
+
- **The scope ratchet is a stop condition:** work revealed bigger than the issue's scope class (or plainly exceeding one session) → one `handback` comment proposing the upgrade or split (dev-plan's ratchet rules), `needs-operator`, stop.
|
|
35
|
+
- The brief's out-of-scope section and the dev.md stop-list bound you; hitting any stop condition ends dark mode with one `handback` comment stating the smallest decision needed, your recommendation attached.
|
|
31
36
|
|
|
32
37
|
Honesty over green: a failing test gets fixed at the root or reported as failing. Weakening a test, an assertion, or acceptance to pass is a cover-up, and cover-ups surface at review with interest.
|
|
33
38
|
|
|
34
|
-
## Changelog — before hand-back
|
|
39
|
+
## Changelog and chronicle — before hand-back
|
|
40
|
+
|
|
41
|
+
Every behavior-changing branch carries its changelog entry per dev.md's `changelog:` knob (`changesets` → write `.changeset/<slug>.md` directly, never the interactive CLI; `keep-a-changelog` / `pubspec+changelog` → one bullet under `## [Unreleased]`, creating CHANGELOG.md with the `# Changelog` + `## [Unreleased]` skeleton in the same branch when absent; `none` → skip) **and**, when dev.md says `chronicle: on`, its story entry prepended to `.vegastack/chronicle.md` (format: the `dev-chronicle` skill) — both on the branch, landing atomically with the merge. Docs the brief names as affected get updated in the same branch.
|
|
35
42
|
|
|
36
|
-
|
|
43
|
+
## Verify — the gate function
|
|
37
44
|
|
|
38
|
-
|
|
45
|
+
Before claiming ANY status: **identify** the command that proves it → **run** it fresh and complete → **read** the full output and exit code → only then claim, with the evidence. Tests pass ⇒ a fresh run with 0 failures — never "should pass", never a previous run. Build succeeds ⇒ exit 0. Bug fixed ⇒ the original symptom re-tested. A subagent finished ⇒ you inspected its diff or report file — never its say-so.
|
|
39
46
|
|
|
40
|
-
- Run
|
|
41
|
-
-
|
|
42
|
-
-
|
|
43
|
-
- UI changed and `ui-evidence: playwright` → capture screenshots of the key states and flows and upload them to the shared evidence repo (dev.md `evidence-repo`) under `<this-repo-name>/<issue-number>/<timestamp>-<name>.png` — via the contents API so the repo is never cloned: `base64 < <file> | tr -d '\n' | gh api -X PUT repos/<evidence-repo>/contents/<path> -f message="evidence #<issue>" -F content=@-` (piped stdin, so large screenshots never hit argv limits; timestamped names keep re-captures from colliding; a 409 from a concurrent upload just means retry). Link them in the evidence comment — links, not embeds; private-repo images don't render inline in issues. Evidence repo missing or unreachable → name the local file paths and say so; the hand-back never blocks on it.
|
|
44
|
-
- dev.md's Ship or Verify section is an empty TODO while release/deploy machinery visibly exists → finish this issue normally, then suggest re-running dev-setup so detection can fill it.
|
|
47
|
+
- Run what dev.md's `tests:` knob requires; a `risky` issue gets focused security, failure, and recovery checks on top. When dev.md has a `## Verify` runbook, run the app and smoke-check the flows it names. Verify is pre-merge only; post-release checks live in `## Ship` and belong to dev-ship.
|
|
48
|
+
- UI changed and `ui-evidence: playwright` → capture screenshots of the key states and upload to the shared evidence repo (dev.md `evidence-repo`) under `<this-repo-name>/<issue-number>/<timestamp>-<name>.png` via the contents API, never a clone: `base64 < <file> | tr -d '\n' | gh api -X PUT repos/<evidence-repo>/contents/<path> -f message="evidence #<issue>" -F content=@-` (piped stdin so large screenshots never hit argv limits; timestamped names avoid collisions; a 409 from a concurrent upload just means retry). Link them in the evidence comment — links, never embeds (private-repo images don't render inline). Evidence repo unreachable → name local paths and say so; the hand-back never blocks on it.
|
|
49
|
+
- dev.md's Ship or Verify section is an empty TODO next to visible machinery → finish normally, then suggest re-running dev-setup.
|
|
45
50
|
|
|
46
|
-
## Independent review —
|
|
51
|
+
## Independent review — invoke dev-review
|
|
47
52
|
|
|
48
|
-
|
|
49
|
-
- `cross-agent` (or `cross-agent-risky` on a `risky` issue): push the branch, add to the evidence comment "awaiting cross-agent review", keep `working`, and tell the user which agent to point at the issue. The reviewing session posts findings on the issue; you apply them.
|
|
50
|
-
- Fix real findings and rerun affected checks. Disagree with a finding → say why in the evidence comment rather than silently skipping it.
|
|
53
|
+
Run the `dev-review` skill per dev.md's `review:` knob — fresh subagent axes by default, cross-agent (Codex↔Claude, announced to the operator) per the knob's mapping; it owns the axes, severities, review comment, bounded fix loop, and adjudication rules. Apply its findings through its loop and re-run the affected checks. Disagree with a finding → adjudicate openly per its rules, never silently skip. In a harness without subagents, run dev-review's axis briefs yourself as a labeled self-review.
|
|
51
54
|
|
|
52
55
|
## The evidence comment — exactly one, edited in place
|
|
53
56
|
|
|
54
|
-
```
|
|
55
|
-
|
|
57
|
+
```markdown
|
|
58
|
+
<!-- vsk:v1 type=evidence rev=1 branch=<name> sha=<sha7> -->
|
|
59
|
+
## Result (v1)
|
|
56
60
|
**Done:** what changed, in behavior terms
|
|
57
|
-
**Tests:** <command> → <result
|
|
58
|
-
**Review:** <mode> — <
|
|
59
|
-
**Changelog:** <entry added / none, with reason>
|
|
61
|
+
**Tests:** <command> → <fresh result>
|
|
62
|
+
**Review:** <mode> — <verdict; adjudications and rulings surfaced, in order made>
|
|
63
|
+
**Changelog:** <entry added / none, with reason>
|
|
64
|
+
**Docs:** brief v<n>, plan v<n> — in sync | unchanged since approval
|
|
60
65
|
**UI evidence:** <links> (when applicable)
|
|
61
|
-
**Decision:** <
|
|
66
|
+
**Decision:** <register-format proposals> (only choices passing dev.md's Decisions test)
|
|
62
67
|
**Not done / limits:** the honest list
|
|
63
|
-
Branch: <name> @ <
|
|
68
|
+
Branch: <name> @ <sha7>
|
|
64
69
|
```
|
|
65
70
|
|
|
66
|
-
Post it, swap `working` → `for-operator`,
|
|
71
|
+
The `**Review:**` line is the one home of surfaced rulings: every ledger `Ruling:` appears there, in the order made. Run `node <path-to-this-skill>/scripts/evidence-check.mjs --file <draft> --json` before posting — exit 2 means the shape is incomplete; fix, don't post. Post it, swap `working` → `for-operator`, stop, and close with the plain-language summary (which repeats, never replaces, the evidence content): what was built, which paths were taken, the rulings, what's worth the operator double-checking.
|
|
67
72
|
|
|
68
|
-
## Corrections loop
|
|
73
|
+
## Corrections loop — code and docs move together
|
|
69
74
|
|
|
70
|
-
The
|
|
75
|
+
The operator's comments on a `for-operator` issue are the new frontier. Applying a correction is **one pass**: the code change + the affected brief/plan sections edited to match (revision markers bumped, `Revisions:` line appended) + a ledger line + the evidence comment updated in place — its `sha` to the new head and its `Docs:` line to the new revisions. Re-verify what the correction touched. An operator dismissal of a review finding gets appended to `.vegastack/review-known-patterns.md` with its mandatory "Still flag if:" clause. Then back to `for-operator`. Corrections never need re-approval ceremony unless they change scope — that's `needs-operator` and intake's recording rule.
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
# Workflow conventions
|
|
2
|
+
|
|
3
|
+
The single spec for the artifacts every dev-family skill reads and writes. One home per rule: skills cite this file, never restate it. Everything here is harness-neutral.
|
|
4
|
+
|
|
5
|
+
## Comment metadata markers
|
|
6
|
+
|
|
7
|
+
Every workflow-generated issue comment opens with an invisible HTML marker followed by a human heading:
|
|
8
|
+
|
|
9
|
+
```markdown
|
|
10
|
+
<!-- vsk:v1 type=<type> rev=<n> [key=value ...] -->
|
|
11
|
+
## <Human title> (v<n>)
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
| type | required keys | instances |
|
|
15
|
+
|---|---|---|
|
|
16
|
+
| `approval` | `scope=<brief\|brief+plan\|plan>` | one per approval event |
|
|
17
|
+
| `plan` | `rev` | one, edited in place |
|
|
18
|
+
| `ledger` | `branch` | one, edited in place |
|
|
19
|
+
| `evidence` | `rev branch sha` | one, edited in place |
|
|
20
|
+
| `review` | `round sha agent=<claude\|codex> verdict=<clean\|needs-fixes>` | one per review cycle, rounds appended inside |
|
|
21
|
+
| `decision` | — | one per decision proposal |
|
|
22
|
+
| `handback` | — | one per stop event |
|
|
23
|
+
|
|
24
|
+
`rev=<n>` and the matching `(v<n>)` heading suffix appear only on revisable artifacts — the brief (issue description), `plan`, and `evidence` — starting at `rev=1`/`(v1)`. Single-event comments (`approval`, `decision`, `handback`) and the `ledger` carry neither. Scripts and agents locate comments strictly by marker, never by heading text. A comment without its marker does not count as the artifact — there is no legacy fallback.
|
|
25
|
+
|
|
26
|
+
## Operator identity
|
|
27
|
+
|
|
28
|
+
Every human reference in every artifact — approvals, revisions, decisions, changelog attributions, review adjudications — is written `operator (<github-username>)`:
|
|
29
|
+
|
|
30
|
+
- Approval: `Approved by operator (<username>) on DD-MM-YYYY: "<their words>"`
|
|
31
|
+
- Register line: `- DD-MM-YYYY operator (<username>) — <decision>`
|
|
32
|
+
|
|
33
|
+
## Revision markers
|
|
34
|
+
|
|
35
|
+
Any artifact edited after its first approval: the heading gains `(v2)`, the marker gains `rev=2`, and a `Revisions:` line is appended at the bottom — `v2 — DD-MM-YYYY: <what changed>, per operator (<username>) correction`. Existing revision lines are never rewritten.
|
|
36
|
+
|
|
37
|
+
## Scope classes
|
|
38
|
+
|
|
39
|
+
Set at intake, applied as a label, announced with its reason (operator can override):
|
|
40
|
+
|
|
41
|
+
- **`research`** — a question to answer; throwaway code allowed, never merged. No branch/PR/changelog; findings + recommendation are the evidence comment.
|
|
42
|
+
- **`quick-build`** — small change and the flow being changed already exists in the repo to read. Brief (description) + plan (comment) are drafted in the same conversation; **one approval covers both**; then straight to `ready`.
|
|
43
|
+
- **`full-plan`** — big or new ground. Brief approval → `needs-plan` → a separate, fresh-grounded planning session posts the plan → `needs-operator` → "plan approved" → `ready`. Multi-deliverable work becomes an epic; each sub-issue is classified independently.
|
|
44
|
+
|
|
45
|
+
Scope calls are revisited through the one-way ratchet, whose rules and mechanics live in the `dev-plan` skill — the one home for upgrade/downgrade behavior.
|
|
46
|
+
|
|
47
|
+
## Labels
|
|
48
|
+
|
|
49
|
+
State — exactly one per issue (creation colors live in dev-setup's labels row, their one home):
|
|
50
|
+
|
|
51
|
+
| label | meaning |
|
|
52
|
+
|---|---|
|
|
53
|
+
| `needs-operator` | waiting on the operator: a question, a brief or plan to approve, a proposal |
|
|
54
|
+
| `needs-plan` | brief approved; waiting for the planning stage (full-plan only) |
|
|
55
|
+
| `ready` | fully approved — an agent may start |
|
|
56
|
+
| `working` | claimed, in progress; the ledger comment shows live progress |
|
|
57
|
+
| `for-operator` | done — evidence posted, awaiting operator review |
|
|
58
|
+
|
|
59
|
+
Modifiers (may coexist with the state label): `risky` · scope `research` / `quick-build` / `full-plan` · `epic` (map parents, only where the org has no native Epic issue type).
|
|
60
|
+
|
|
61
|
+
## Titles, types, hierarchy
|
|
62
|
+
|
|
63
|
+
- **Title prefixes** on issues, branches, and PRs identically: dev.md's `branch:` knob type list (that knob stays the list's one home) plus `research:` for research issues. PR title = issue title.
|
|
64
|
+
- **Native issue types** where the org defines them: Feature (feat) · Bug (fix) · Task (docs/chore/refactor/research) · Epic for parents (label fallback otherwise).
|
|
65
|
+
- **Hierarchy:** epic parent = map only (Destination · Decisions so far as one-line gists · Not clear yet · Out of scope), children attached as native sub-issues; issues = the unit of work (brief in description, own approvals/branch/PR/evidence); tasks = checkboxes **in the plan comment only**. Blockers use native issue dependencies; phases use milestones. Only issues — never epics — get `ready`. GitHub caps issue bodies and comments at ~65,536 characters; what a plan nearing that cap means is the `dev-plan` ratchet's call.
|
|
66
|
+
|
|
67
|
+
## The ledger
|
|
68
|
+
|
|
69
|
+
Maintained by the implement session as one comment, edited in place:
|
|
70
|
+
|
|
71
|
+
```markdown
|
|
72
|
+
<!-- vsk:v1 type=ledger branch=<branch> -->
|
|
73
|
+
## Ledger — <branch>
|
|
74
|
+
- Task <N>: complete (commits <base7>..<head7>[, review clean | K parked])
|
|
75
|
+
- Task <N>: fix round <R>/3 (<X> addressed, <Y> open — <one-liners>; commits <a>..<b>)
|
|
76
|
+
- Ruling: <what> — <why> — cost if wrong: <cost>
|
|
77
|
+
- Task <N>: parked — <finding> — Ruling: <why the code stands>
|
|
78
|
+
- Deferred minor: <one-liner>
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
**Resume protocol:** a fresh, compacted, or (operator-handed) takeover session reads, in order: the brief → the plan comment → the ledger → `git log` on the branch — **nothing else**. Tasks with a `complete` line are DONE, never re-executed; a task whose last line is a fix round resumes at the next round. After compaction, trust the ledger and `git log` over recollection. Every `Ruling:` line surfaces in the evidence comment — a ruling that dies with the session was a decision made in secret.
|
|
82
|
+
|
|
83
|
+
## `.vegastack/.tmp/` workspace
|
|
84
|
+
|
|
85
|
+
All transitory artifacts — subagent reports, review packages, plan drafts, extracted diffs — live at `.vegastack/.tmp/<issue-number>-<title-slug>/` (pre-issue intake drafts, which have no number yet: `.vegastack/.tmp/intake-<slug>/`), kept out of git by a self-ignoring `.gitignore` (`printf '*\n' > .vegastack/.tmp/.gitignore`, created on first use). Subagents write full reports to files there and return only short status — a dead subagent's findings survive on disk, and the primary session never holds full reports in context. The workspace lives in the working tree (never under `.git/`, which harnesses protect from writes).
|
|
86
|
+
|
|
87
|
+
## Verification gate
|
|
88
|
+
|
|
89
|
+
Before claiming any status: **IDENTIFY** the command that proves the claim → **RUN** it fresh and complete → **READ** the full output and exit code → only then claim, with the evidence. "Should pass", a previous run, or a subagent's say-so are never evidence. Guard scripts follow the same doctrine: machine-verifiable facts **block** (exit 2 with the reason); regex or judgment heuristics only **warn** — no AI inference inside guards, and an unverifiable state fails closed.
|
|
90
|
+
|
|
91
|
+
## Plain-language collaboration
|
|
92
|
+
|
|
93
|
+
Every skill run ends with a simple-language summary: what happened, which paths were taken — cross-agent invocations announced at trigger time AND summarized at the end — and what is worth the operator double-checking. Use mermaid or ASCII diagrams in issues wherever a picture beats prose. A vague or self-contradicting operator answer gets pushback with concrete options, never silent absorption.
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
# Using the ledger
|
|
2
|
+
|
|
3
|
+
The ledger comment's format, line vocabulary, and the resume read-order (brief → plan → ledger → `git log`, nothing else) live in the `dev-setup` skill's `references/conventions.md` — this file owns only how dev-implement **uses** them. The ledger is the build's recovery map and the operator's live progress view.
|
|
4
|
+
|
|
5
|
+
## When to checkpoint
|
|
6
|
+
|
|
7
|
+
Create the ledger comment as the session's **first write after claiming** — before any code — with just the marker and heading. Then checkpoint, editing in place:
|
|
8
|
+
|
|
9
|
+
- **After each plan task completes** — and tick the matching checkbox in the plan comment in the same pass. Record the task's base sha *before* starting it, so the `complete` line's commit range is exact.
|
|
10
|
+
- **After each review fix round**, with the addressed/open counts.
|
|
11
|
+
- **At every dark-mode judgment call.** A ruling is any decision the brief/plan didn't make for you that a reviewer or the operator could reasonably question. Rulings are cheap; unrecorded decisions are debt.
|
|
12
|
+
- **On findings deferred or parked at review**, per dev-review's adjudication lines.
|
|
13
|
+
|
|
14
|
+
Never batch checkpoints "for later" — the ledger's value is exactly that a crash between checkpoints loses one task, not the map. Under concurrent edits, last-writer-wins on one comment is accepted (single-operator workflow); note a clobber if you ever see one.
|
|
15
|
+
|
|
16
|
+
## Resuming — dev-implement's additions to the protocol
|
|
17
|
+
|
|
18
|
+
- The takeover of a `working` issue requires the operator's explicit handover word; the protocol never makes claiming automatic.
|
|
19
|
+
- Corroborate, don't re-verify: the commits the ledger names should exist in `git log` — reconcile the ranges; a mismatch is a `handback`, not a guess.
|
|
20
|
+
- A task whose last line is a fix round is mid-loop — resume at the next round with the open findings; a later `complete` line supersedes earlier rounds.
|
|
21
|
+
- Recorded rulings bind the resumed session: build on them, and surface disagreement in the evidence comment instead of re-litigating.
|
|
22
|
+
- The "nothing else" is literal: not the full issue thread, not review round history, not any transcript of the dead session — that is context spent re-reading what the ledger already distills.
|
|
23
|
+
- Re-executing work the ledger marks complete is the single most expensive failure this protocol exists to prevent — after compaction, trust the ledger and `git log` over recollection.
|
|
24
|
+
|
|
25
|
+
## Surfacing — rulings never die in the dark
|
|
26
|
+
|
|
27
|
+
Every `Ruling:` line lands on the evidence comment's `**Review:**` line at hand-back, in the order made. The operator reads that list and reverses anything wrong — a ruling that only ever lived in the ledger was a decision made in secret.
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// dev-implement guard: the evidence comment's required shape. Structure blocks;
|
|
3
|
+
// nothing here warns.
|
|
4
|
+
//
|
|
5
|
+
// Exit codes: 0 pass · 2 blocked (this guard has no warn class).
|
|
6
|
+
// Usage: node evidence-check.mjs --file <evidence.md> --json
|
|
7
|
+
import { readFileSync } from 'node:fs';
|
|
8
|
+
import { resolve } from 'node:path';
|
|
9
|
+
import { fileURLToPath } from 'node:url';
|
|
10
|
+
import { parseFlags, parseMarker, renderResult } from './lib/gh.mjs';
|
|
11
|
+
|
|
12
|
+
const REQUIRED_SECTIONS = [
|
|
13
|
+
[/\*\*Done:\*\*/, '**Done:** section'],
|
|
14
|
+
[/\*\*Tests:\*\*/, '**Tests:** section (command → fresh result)'],
|
|
15
|
+
[/\*\*Review:\*\*/, '**Review:** section (mode + verdict/adjudications)'],
|
|
16
|
+
[/\*\*Changelog:\*\*/, '**Changelog:** section (entry, or none with a holding reason)'],
|
|
17
|
+
[/\*\*Docs:\*\*/, '**Docs:** section (brief/plan revisions in sync, or unchanged)'],
|
|
18
|
+
[/\*\*Not done/, '**Not done / limits:** section (the honest list)'],
|
|
19
|
+
];
|
|
20
|
+
|
|
21
|
+
export function checkEvidence(text) {
|
|
22
|
+
const blocks = [];
|
|
23
|
+
|
|
24
|
+
const marker = parseMarker(text);
|
|
25
|
+
if (!marker || marker.keys.type !== 'evidence') {
|
|
26
|
+
blocks.push('missing evidence marker (<!-- vsk:v1 type=evidence rev=n branch=... sha=... -->)');
|
|
27
|
+
} else {
|
|
28
|
+
if (!marker.keys.branch) blocks.push('evidence marker missing branch=');
|
|
29
|
+
if (!/^[0-9a-f]{7,}$/.test(marker.keys.sha ?? '')) blocks.push('evidence marker missing a real sha=');
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
for (const [pattern, label] of REQUIRED_SECTIONS) {
|
|
33
|
+
if (!pattern.test(text)) blocks.push(`missing ${label}`);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
if (!/Branch:\s*\S+\s*@\s*[0-9a-f]{7,}/.test(text)) blocks.push('missing "Branch: <name> @ <sha7>" tail line');
|
|
37
|
+
|
|
38
|
+
return { blocks, warns: [] };
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const invokedDirectly = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
42
|
+
if (invokedDirectly) {
|
|
43
|
+
const flags = parseFlags(process.argv.slice(2));
|
|
44
|
+
let outcome;
|
|
45
|
+
if (!flags.file) {
|
|
46
|
+
outcome = { blocks: ['usage: evidence-check.mjs --file <evidence.md> [--json]'], warns: [] };
|
|
47
|
+
} else {
|
|
48
|
+
try {
|
|
49
|
+
outcome = checkEvidence(readFileSync(flags.file, 'utf8'));
|
|
50
|
+
} catch (error) {
|
|
51
|
+
outcome = { blocks: [`cannot read evidence: ${error.message}`], warns: [] };
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
const { exitCode, text } = renderResult('evidence-check', outcome, { json: Boolean(flags.json) });
|
|
55
|
+
console.log(text);
|
|
56
|
+
process.exit(exitCode);
|
|
57
|
+
}
|
|
@@ -0,0 +1,93 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// Shared plumbing for the workflow guard scripts: gh invocation, vsk:v1 comment
|
|
3
|
+
// marker parsing, and the block/warn result contract (facts block, heuristics
|
|
4
|
+
// warn — exit 0 pass · 1 warn-only · 2 block; unverifiable state fails closed).
|
|
5
|
+
import { execFileSync } from 'node:child_process';
|
|
6
|
+
|
|
7
|
+
export class GhUnavailable extends Error {
|
|
8
|
+
constructor(message, httpStatus = null) {
|
|
9
|
+
super(message);
|
|
10
|
+
this.httpStatus = httpStatus; // parsed from gh's "HTTP <code>" stderr, when present
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
// Run gh with explicit args (never a shell) and parse JSON output. Any failure
|
|
15
|
+
// to reach GitHub is a GhUnavailable — callers treat it as "cannot verify",
|
|
16
|
+
// which blocks (fail closed), never as a pass. VSK_GH is a TEST SEAM only
|
|
17
|
+
// (points unit tests at a stub binary); guards are enforcement infrastructure,
|
|
18
|
+
// so never set it in real runs.
|
|
19
|
+
export function ghJson(args, { gh = process.env.VSK_GH || 'gh' } = {}) {
|
|
20
|
+
let out;
|
|
21
|
+
try {
|
|
22
|
+
out = execFileSync(gh, args, { encoding: 'utf8', stdio: ['ignore', 'pipe', 'pipe'] });
|
|
23
|
+
} catch (error) {
|
|
24
|
+
const stderr = error.stderr?.toString().trim() || '';
|
|
25
|
+
const status = /HTTP (\d{3})/.exec(stderr)?.[1];
|
|
26
|
+
throw new GhUnavailable(`gh ${args.join(' ')} failed: ${stderr || error.message}`, status ? Number(status) : null);
|
|
27
|
+
}
|
|
28
|
+
try {
|
|
29
|
+
return JSON.parse(out);
|
|
30
|
+
} catch {
|
|
31
|
+
throw new GhUnavailable(`gh ${args.join(' ')} returned unparseable JSON`);
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// Parse a `<!-- vsk:v1 key=value ... -->` marker from a comment/body's first
|
|
36
|
+
// marker line. Returns { keys } or null when no marker exists — and per
|
|
37
|
+
// conventions, no marker means the artifact does not exist (no fallback).
|
|
38
|
+
export function parseMarker(body) {
|
|
39
|
+
const match = /<!--\s*vsk:v1\s+([^>]*?)\s*-->/.exec(body ?? '');
|
|
40
|
+
if (!match) return null;
|
|
41
|
+
const keys = {};
|
|
42
|
+
for (const pair of match[1].split(/\s+/)) {
|
|
43
|
+
const eq = pair.indexOf('=');
|
|
44
|
+
if (eq > 0) keys[pair.slice(0, eq)] = pair.slice(eq + 1);
|
|
45
|
+
}
|
|
46
|
+
return { keys };
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
// Find the last comment carrying a marker of the given type (last wins: for
|
|
50
|
+
// edited-in-place singletons there is one; for repeated types the newest is
|
|
51
|
+
// the operative one).
|
|
52
|
+
export function findMarkerComment(comments, type) {
|
|
53
|
+
let found = null;
|
|
54
|
+
for (const comment of comments ?? []) {
|
|
55
|
+
const marker = parseMarker(comment.body);
|
|
56
|
+
if (marker && marker.keys.type === type) found = { comment, keys: marker.keys };
|
|
57
|
+
}
|
|
58
|
+
return found;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// Render the guard result and compute the exit code. blocks/warns are arrays of
|
|
62
|
+
// human sentences; the caller passes process.argv-derived json flag.
|
|
63
|
+
export function renderResult(name, { blocks = [], warns = [] }, { json = false } = {}) {
|
|
64
|
+
const ok = blocks.length === 0;
|
|
65
|
+
const exitCode = blocks.length > 0 ? 2 : warns.length > 0 ? 1 : 0;
|
|
66
|
+
let text;
|
|
67
|
+
if (json) {
|
|
68
|
+
text = JSON.stringify({ guard: name, ok, blocks, warns }, null, 2);
|
|
69
|
+
} else {
|
|
70
|
+
const lines = [`${name}: ${ok ? (warns.length ? 'pass with warnings' : 'pass') : 'BLOCKED'}`];
|
|
71
|
+
for (const b of blocks) lines.push(` block: ${b}`);
|
|
72
|
+
for (const w of warns) lines.push(` warn: ${w}`);
|
|
73
|
+
text = lines.join('\n');
|
|
74
|
+
}
|
|
75
|
+
return { exitCode, text };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
// Minimal flag parser shared by the guards: --key value and boolean --flags.
|
|
79
|
+
export function parseFlags(argv, booleans = ['json']) {
|
|
80
|
+
const flags = {};
|
|
81
|
+
for (let i = 0; i < argv.length; i += 1) {
|
|
82
|
+
const arg = argv[i];
|
|
83
|
+
if (!arg.startsWith('--')) continue;
|
|
84
|
+
const key = arg.slice(2);
|
|
85
|
+
if (booleans.includes(key)) {
|
|
86
|
+
flags[key] = true;
|
|
87
|
+
} else {
|
|
88
|
+
flags[key] = argv[i + 1];
|
|
89
|
+
i += 1;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
return flags;
|
|
93
|
+
}
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
// dev-implement preflight guard: the deterministic facts that must hold before
|
|
3
|
+
// an agent may claim an issue. Facts block (exit 2 with reasons); nothing here
|
|
4
|
+
// warns — judgment checks stay in the skill prose.
|
|
5
|
+
//
|
|
6
|
+
// Exit codes: 0 pass · 1 pass-with-warnings · 2 blocked (reasons printed).
|
|
7
|
+
// Usage: node preflight.mjs --issue <n> [--repo owner/name] [--me <login>] [--dev-md <path>] --json
|
|
8
|
+
import { readFileSync } from 'node:fs';
|
|
9
|
+
import { resolve } from 'node:path';
|
|
10
|
+
import { fileURLToPath } from 'node:url';
|
|
11
|
+
import { GhUnavailable, findMarkerComment, ghJson, parseFlags, renderResult } from './lib/gh.mjs';
|
|
12
|
+
|
|
13
|
+
export function evaluatePreflight({ issue, comments, devMd, me, expect = 'ready' }) {
|
|
14
|
+
const blocks = [];
|
|
15
|
+
const warns = [];
|
|
16
|
+
const labels = (issue.labels ?? []).map((l) => l.name);
|
|
17
|
+
|
|
18
|
+
if (issue.state && issue.state !== 'open') blocks.push(`issue is ${issue.state} — only open issues are workable`);
|
|
19
|
+
const STATE_LABELS = ['needs-operator', 'needs-plan', 'ready', 'working', 'for-operator'];
|
|
20
|
+
const state = STATE_LABELS.filter((s) => labels.includes(s));
|
|
21
|
+
if (!state.includes(expect)) {
|
|
22
|
+
blocks.push(`issue state label is [${state.join(', ') || 'none'}], expected ${expect} (fresh start: ready · resume: working with the operator's handover · corrections: for-operator)`);
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const approval = findMarkerComment(comments, 'approval');
|
|
26
|
+
if (!approval) blocks.push('no recorded approval comment (marker type=approval) on the issue');
|
|
27
|
+
|
|
28
|
+
const scope = ['research', 'quick-build', 'full-plan'].filter((s) => labels.includes(s));
|
|
29
|
+
if (scope.length !== 1) blocks.push(`issue needs exactly one scope label (research | quick-build | full-plan), found: ${scope.join(', ') || 'none'}`);
|
|
30
|
+
|
|
31
|
+
if (scope[0] === 'full-plan') {
|
|
32
|
+
const planApproved = (comments ?? []).some((c) => {
|
|
33
|
+
const m = findMarkerComment([c], 'approval');
|
|
34
|
+
return m && ['plan', 'brief+plan'].includes(m.keys.scope);
|
|
35
|
+
});
|
|
36
|
+
if (!planApproved) blocks.push('full-plan issue without a recorded plan approval (marker type=approval scope=plan or brief+plan)');
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// The brief-template rule: a resolved Assumptions section is deleted, so the
|
|
40
|
+
// heading's presence at all means unresolved entries remain.
|
|
41
|
+
if (/^##\s+Assumptions\b/m.test(issue.body ?? '')) {
|
|
42
|
+
blocks.push('the brief still carries a "## Assumptions" section — resolve every entry (the section is deleted once resolved) before starting');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const openBlockers = issue.blockedBy ?? [];
|
|
46
|
+
if (openBlockers.length > 0) blocks.push(`open blockers: ${openBlockers.map((b) => `#${b.number}`).join(', ')}`);
|
|
47
|
+
|
|
48
|
+
const others = (issue.assignees ?? []).map((a) => a.login).filter((l) => l !== me);
|
|
49
|
+
if (others.length > 0) blocks.push(`already assigned to ${others.join(', ')} — a working issue belongs to its claimant`);
|
|
50
|
+
|
|
51
|
+
const repoLine = /^repo:\s*(\S+)/m.exec(devMd ?? '');
|
|
52
|
+
if (!repoLine) {
|
|
53
|
+
warns.push('dev.md has no repo: line — the issue-repo match could not be verified');
|
|
54
|
+
} else if (issue.repo && repoLine[1] !== issue.repo) {
|
|
55
|
+
blocks.push(`issue repo ${issue.repo} does not match dev.md repo ${repoLine[1]}`);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
return { blocks, warns };
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
export function gatherAndEvaluate(flags) {
|
|
62
|
+
const repo = flags.repo || ghJson(['repo', 'view', '--json', 'nameWithOwner']).nameWithOwner;
|
|
63
|
+
const issueNumber = flags.issue;
|
|
64
|
+
const raw = ghJson(['api', `repos/${repo}/issues/${issueNumber}`]);
|
|
65
|
+
const comments = ghJson(['api', `repos/${repo}/issues/${issueNumber}/comments`, '--paginate']);
|
|
66
|
+
let blockedBy = [];
|
|
67
|
+
try {
|
|
68
|
+
blockedBy = ghJson(['api', `repos/${repo}/issues/${issueNumber}/dependencies/blocked_by`])
|
|
69
|
+
.filter((b) => b.state === 'open');
|
|
70
|
+
} catch (error) {
|
|
71
|
+
// Only an HTTP 404 (host without the dependencies API) means "none
|
|
72
|
+
// recorded" — matched on the parsed status, never the message text, so a
|
|
73
|
+
// path containing "404" can't masquerade. Every other failure — auth,
|
|
74
|
+
// network, rate limit — is unverifiable state and fails closed.
|
|
75
|
+
if (error.httpStatus !== 404) throw error;
|
|
76
|
+
blockedBy = [];
|
|
77
|
+
}
|
|
78
|
+
const devMd = readFileSync(flags['dev-md'] || '.vegastack/dev.md', 'utf8');
|
|
79
|
+
const me = flags.me || ghJson(['api', 'user']).login;
|
|
80
|
+
return evaluatePreflight({
|
|
81
|
+
issue: { body: raw.body, state: raw.state, labels: raw.labels, assignees: raw.assignees, repo, blockedBy },
|
|
82
|
+
comments,
|
|
83
|
+
devMd,
|
|
84
|
+
me,
|
|
85
|
+
expect: flags.expect || 'ready',
|
|
86
|
+
});
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const invokedDirectly = process.argv[1] && resolve(process.argv[1]) === fileURLToPath(import.meta.url);
|
|
90
|
+
if (invokedDirectly) {
|
|
91
|
+
const flags = parseFlags(process.argv.slice(2));
|
|
92
|
+
let outcome;
|
|
93
|
+
try {
|
|
94
|
+
outcome = gatherAndEvaluate(flags);
|
|
95
|
+
} catch (error) {
|
|
96
|
+
outcome = { blocks: [error instanceof GhUnavailable ? `cannot verify: ${error.message}` : `preflight error: ${error.message}`], warns: [] };
|
|
97
|
+
}
|
|
98
|
+
const { exitCode, text } = renderResult('preflight', outcome, { json: Boolean(flags.json) });
|
|
99
|
+
console.log(text);
|
|
100
|
+
process.exit(exitCode);
|
|
101
|
+
}
|