@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.
Files changed (58) hide show
  1. package/README.md +5 -0
  2. package/package.json +1 -1
  3. package/skill/dev-architect/SKILL.md +7 -4
  4. package/skill/dev-architect/references/conventions.md +93 -0
  5. package/skill/dev-chronicle/SKILL.md +45 -0
  6. package/skill/dev-chronicle/agents/openai.yaml +4 -0
  7. package/skill/dev-chronicle/references/conventions.md +93 -0
  8. package/skill/dev-chronicle/refresh/REFRESH.md +3 -0
  9. package/skill/dev-chronicle/refresh/sources.json +6 -0
  10. package/skill/dev-debug/SKILL.md +43 -0
  11. package/skill/dev-debug/agents/openai.yaml +4 -0
  12. package/skill/dev-debug/references/conventions.md +93 -0
  13. package/skill/dev-debug/references/loop-ladder.md +20 -0
  14. package/skill/dev-debug/refresh/REFRESH.md +3 -0
  15. package/skill/dev-debug/refresh/sources.json +6 -0
  16. package/skill/dev-implement/SKILL.md +41 -36
  17. package/skill/dev-implement/references/conventions.md +93 -0
  18. package/skill/dev-implement/references/ledger-and-resume.md +27 -0
  19. package/skill/dev-implement/scripts/evidence-check.mjs +57 -0
  20. package/skill/dev-implement/scripts/lib/gh.mjs +93 -0
  21. package/skill/dev-implement/scripts/preflight.mjs +101 -0
  22. package/skill/dev-intake/SKILL.md +39 -33
  23. package/skill/dev-intake/references/brief-template.md +27 -12
  24. package/skill/dev-intake/references/conventions.md +93 -0
  25. package/skill/dev-intake/scripts/brief-lint.mjs +87 -0
  26. package/skill/dev-plan/SKILL.md +53 -0
  27. package/skill/dev-plan/agents/openai.yaml +4 -0
  28. package/skill/dev-plan/references/conventions.md +93 -0
  29. package/skill/dev-plan/references/plan-format.md +54 -0
  30. package/skill/dev-plan/refresh/REFRESH.md +3 -0
  31. package/skill/dev-plan/refresh/sources.json +6 -0
  32. package/skill/dev-plan/scripts/plan-lint.mjs +86 -0
  33. package/skill/dev-review/SKILL.md +69 -0
  34. package/skill/dev-review/agents/openai.yaml +4 -0
  35. package/skill/dev-review/assets/review-known-patterns.md.template +30 -0
  36. package/skill/dev-review/references/conventions.md +93 -0
  37. package/skill/dev-review/references/cross-agent.md +39 -0
  38. package/skill/dev-review/references/dispatch-prompts.md +104 -0
  39. package/skill/dev-review/references/security-axis.md +33 -0
  40. package/skill/dev-review/refresh/REFRESH.md +3 -0
  41. package/skill/dev-review/refresh/sources.json +6 -0
  42. package/skill/dev-setup/SKILL.md +7 -5
  43. package/skill/dev-setup/assets/agents-section.md.template +2 -2
  44. package/skill/dev-setup/assets/dev-profile.md.template +4 -3
  45. package/skill/dev-setup/references/conventions.md +93 -0
  46. package/skill/dev-ship/SKILL.md +14 -7
  47. package/skill/dev-ship/references/conventions.md +93 -0
  48. package/skill/dev-ship/references/runbook.md +1 -1
  49. package/skill/dev-ship/scripts/ship-gate.mjs +213 -0
  50. package/skill/dev-status/SKILL.md +45 -0
  51. package/skill/dev-status/agents/openai.yaml +4 -0
  52. package/skill/dev-status/references/conventions.md +93 -0
  53. package/skill/dev-status/refresh/REFRESH.md +3 -0
  54. package/skill/dev-status/refresh/sources.json +6 -0
  55. package/skill/dev-status/scripts/status.mjs +152 -0
  56. package/skill/skillify/SKILL.md +1 -1
  57. package/skill/skillify/references/eval-playbook.md +6 -0
  58. 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 - "do issue 12", "implement" plus an issue URL or number, "pick up the next ready issue", "go dark on" an issue - when returning to apply corrections the user left on a for-operator issue, or when the user directly asks in chat for a quick fix or small change. Runs preflight, claims the issue, builds on a task branch, tests, gets independent review, and posts one evidence comment in the issue. Not for writing or approving issues (dev-intake), not for creating PRs or merging (dev-ship).
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 in the issue → stop. The user reads the result in the issue on their own time; nothing here creates a PR or merges — those are `dev-ship`, on the user's word.
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 neighbor: `dev-intake` writes the brief this skill executes; if the issue turns out to need decisions, that's intake work — hand it back via `needs-operator`, don't guess. `.vegastack/dev.md` missing → run `dev-setup` first. Read dev.md before anything; its knobs (review, ui-evidence, tests, branch, stop-list) govern this whole skill, and when the issue touches stack surfaces (schema, auth, hosting, services, jobs) its `## Architecture` section governs those choices the way the knobs govern process.
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
- The gates exist to stop agent-invented authority, never to slow the user down. When the user directly asks in chat for a change ("fix this typo", "bump that timeout"), their words are the approval — do it, verify it, and report; no issue required. Branch as `<type>/<slug>` (no issue segment); the changelog rule below applies unchanged when the change is behavior-changing; shipping still goes through dev-ship's words, with the chat request standing in for the approval and the report for the evidence comment. Offer to record an issue when the change is material enough that its brief or evidence will matter later. Everything below is the path for issue-driven work.
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 auth status` works and the issue's repo matches dev.md.
19
- - The issue is open, labeled `ready`, and carries the recorded approval comment (`Approved by : "…"`). A label without the comment is not approval.
20
- - No open blockers (issue dependencies) and no other assignee an assigned or `working` issue belongs to someone else. A claim from a dead session is released only by the user: take over a `working` issue only when they explicitly hand it to you.
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 and branch
22
+ ## Claim
25
23
 
26
- Assign yourself, swap `ready` → `working`. Branch from the default branch per dev.md's `branch:` knob the knob is the only source of the pattern and its type list.
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 spike the brief flagged runs first — its result opens the evidence comment and shapes the rest of the build. Decide routine things yourself: file layout, helpers, fixtures, and root-cause fixes inside the issue's change areas. 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, not a judgment call. The brief's out-of-scope section and the dev.md stop-list bound you; hitting a stop condition (scope change, new dependency, spending, destructive/production action, unresolvable blocker) ends dark mode — post one `needs-operator` comment stating the smallest decision needed with your recommendation, and stop.
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 firstits 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
- Every behavior-changing branch carries its changelog entry per dev.md's `changelog:` knob; a ship-time guard catches misses, but don't rely on it. `changesets` → write `.changeset/<slug>.md` directly (frontmatter `"<package-name>": <bump>` from the brief's version-impact line, plus a one-paragraph summary — the changeset CLI prompt is interactive, never invoke it here). `keep-a-changelog` / `pubspec+changelog` → one bullet under `## [Unreleased]` (Added/Changed/Fixed/Removed subsection as fits); no CHANGELOG.md yet → create it in the same branch with the skeleton (`# Changelog` + `## [Unreleased]`). `none` → skip. Docs the brief names as affected get updated in the same branch.
43
+ ## Verify — the gate function
37
44
 
38
- ## Verify
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 the tests dev.md requires (`tests: required` every changed behavior has a test that runs and passes; `logic-only` content/config tweaks may skip). Record commands and results for the evidence comment.
41
- - A `risky` issue gets focused security, failure, and recovery checks on top of the required tests.
42
- - When dev.md has a `## Verify` runbook, follow it run the app and smoke-check the flows it names; that live result belongs in the evidence comment alongside the test output. Verify is pre-merge only; post-release checks live in `## Ship` and belong to dev-ship.
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 — per the dev.md knob
51
+ ## Independent review — invoke dev-review
47
52
 
48
- - `subagent` (default): spawn a fresh reviewer subagent that gets the diff, the brief, and dev.md and no memory of writing the code. It checks: does the change do what the brief says, does anything break, are the tests real? In a harness without subagents, do a separate fresh-eyes review pass against the brief and label it a self-review in the evidence comment; prefer cross-agent there for `risky` work.
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
- ## Result
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 summary>
58
- **Review:** <mode> — <findings fixed / none / disputed with reason>
59
- **Changelog:** <entry added / none, with reason> (when the knob is not `none`)
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:** <one line in the register format> (only a dark-mode choice that passes dev.md's Decisions test — a proposal; dev-ship records it after naming it in the merge confirmation)
66
+ **Decision:** <register-format proposals> (only choices passing dev.md's Decisions test)
62
67
  **Not done / limits:** the honest list
63
- Branch: <name> @ <short-sha>
68
+ Branch: <name> @ <sha7>
64
69
  ```
65
70
 
66
- Post it, swap `working` → `for-operator`, unassign nothing, stop. Later corrections update this same comment a stack of stale result comments hides the current truth.
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 user's comments on a `for-operator` issue are the new frontier: apply them, re-verify what they touch, update the evidence comment, back to `for-operator`. Their corrections never need re-approval ceremony unless they change scope — then it's `needs-operator` and dev-intake's recording rule.
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
+ }