Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 46 additions & 0 deletions .opencode/skill-assets/github-coordination/lib/attribution.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
#!/usr/bin/env node
/**
* Canonical attribution identity for github-coordination writes.
*
* @why One source for the reused dotfiles bot identity so every command, skill,
* and hook stamps the same Bot Attribution label and the same visible Agent
* Attribution Marker — inventing a second bot identity is exactly what the
* coordination contract forbids.
*
* The dotfiles bot identity is the Jakuta Review Bot GitHub App
* (manifests/dev-repos.toml -> jakuta-review-bot; app.yml "Jakuta Review Bot").
* Agent-filed issue plans use `jakuta-agent-issue apply-plan`, so issue writes
* are authored by the app bot. The visible marker remains the durable
* provenance line for readers.
*/

"use strict";

// The reused dotfiles bot identity. Not the operator's personal git user.
const BOT_ATTRIBUTION_LABEL = "Jakuta Review Bot";
const BOT_ATTRIBUTION_SLUG = "jakuta-review-bot[bot]";

// The literal text that tells a human reader an agent authored this record.
// Must stay visible in the Canonical Issue Body, never hidden metadata alone.
const AGENT_ATTRIBUTION_MARKER =
"> 🤖 **Agent-authored** — an agent wrote this coordination record. " +
"Attributed to the " + BOT_ATTRIBUTION_LABEL + " (`" + BOT_ATTRIBUTION_SLUG + "`), " +
"the reused dotfiles bot identity; no second bot identity is invented.";

/**
* @why Appends the visible marker to a body only when it is not already there,
* so re-running a guided flow over an existing body cannot stack duplicate
* markers.
*/
function withAgentMarker(body) {
const text = typeof body === "string" ? body : "";
if (text.includes("an agent wrote this")) return text;
return text.replace(/\s*$/, "") + "\n\n" + AGENT_ATTRIBUTION_MARKER + "\n";
}

module.exports = {
BOT_ATTRIBUTION_LABEL,
BOT_ATTRIBUTION_SLUG,
AGENT_ATTRIBUTION_MARKER,
withAgentMarker,
};
135 changes: 135 additions & 0 deletions .opencode/skill-assets/github-coordination/lib/pr-issue-link.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
#!/usr/bin/env node
/**
* Detects whether a `gh pr create` / `gh pr edit` command either closes a
* linked issue or proves an exact development-to-production promotion.
*
* @why Ordinary pull requests implement an issue and must close it. A direct
* development-to-production promotion implements no new issue, so it instead
* carries the evidence that makes that release move reviewable. Kept pure
* (string in, named outcome out) so the hook stays a thin adapter and the rule
* is unit-checkable.
*/

"use strict";

const fs = require("fs");

// GitHub's closing keywords, followed by an issue ref (#123 or a full URL).
const CLOSING_REFERENCE =
/\b(close|closes|closed|fix|fixes|fixed|resolve|resolves|resolved)\b\s*:?\s+(#\d+|https?:\/\/github\.com\/[^/\s]+\/[^/\s]+\/issues\/\d+)/i;

const PROMOTION_PROOF_LINES = [
/^##\s+Promotion proof\s*$/im,
/^- Development commit:\s+[0-9a-f]{7,40}\s*$/im,
/^- Production commit:\s+[0-9a-f]{7,40}\s*$/im,
/^- Production history:\s+\S.+$/im,
/^- Required checks:\s+\S.+$/im,
/^- Protection:\s+no force-push or bypass\.?\s*$/im,
];

/**
* Classifies a shell command string.
*
* Outcome kinds:
* NotPrWrite — not a `gh pr create`/`edit`; the hook ignores it.
* HasClosingReference — a closing keyword + issue ref was found in the body.
* HasPromotionProof — direct development-to-production PR with release proof.
* BodyUnreadable — a --body-file was named but could not be read.
* MissingPromotionProof — direct promotion missing its required proof body.
* MissingClosingRef — a PR write with no resolvable closing reference.
*/
function classifyPrCommand(command) {
const text = typeof command === "string" ? command : "";

const isPrWrite = /\bgh\s+pr\s+(create|edit)\b/.test(text);
if (!isPrWrite) return { kind: "NotPrWrite" };

const bodyTexts = [];
const inline = extractInlineBody(text);
if (inline !== null) {
bodyTexts.push({ source: "inline", text: inline });
if (CLOSING_REFERENCE.test(inline)) {
return { kind: "HasClosingReference", source: "inline" };
}
}

const bodyFile = extractBodyFilePath(text);
if (bodyFile !== null) {
let fileText;
try {
fileText = fs.readFileSync(bodyFile, "utf8");
} catch (err) {
return { kind: "BodyUnreadable", path: bodyFile, error: String(err && err.message ? err.message : err) };
}
bodyTexts.push({ source: "body-file", text: fileText });
if (CLOSING_REFERENCE.test(fileText)) {
return { kind: "HasClosingReference", source: "body-file" };
}
}

// Some callers put the closing ref in --title; honor it so a legitimately
// linked PR is never blocked on a body/title technicality.
const title = extractFlagValue(text, "title") || extractFlagValue(text, "t");
if (title !== null && CLOSING_REFERENCE.test(title)) {
return { kind: "HasClosingReference", source: "title" };
}

for (const bodyText of bodyTexts) {
if (isDevelopmentToProductionPromotion(text) && hasPromotionProof(bodyText.text)) {
return { kind: "HasPromotionProof", source: bodyText.source };
}
}

if (isDevelopmentToProductionPromotion(text)) {
return { kind: "MissingPromotionProof" };
}

return { kind: "MissingClosingRef" };
}

function isDevelopmentToProductionPromotion(text) {
return (
/\bgh\s+pr\s+create\b/.test(text) &&
extractFlagValue(text, "base") === "production" &&
extractFlagValue(text, "head") === "development"
);
}

function hasPromotionProof(bodyText) {
return PROMOTION_PROOF_LINES.every((requirement) => requirement.test(bodyText));
}

function extractInlineBody(text) {
const long = extractFlagValue(text, "body");
if (long !== null) return long;
const short = extractFlagValue(text, "b");
if (short !== null) return short;
return null;
}

function extractBodyFilePath(text) {
return extractFlagValue(text, "body-file") || extractFlagValue(text, "F");
}

/**
* Pulls the value of --flag / -f from a command string, handling single
* quotes, double quotes, and bare tokens. Returns null when the flag is absent.
*/
function extractFlagValue(text, flag) {
const dash = flag.length === 1 ? "-" : "--";
const head = dash + escapeForRegex(flag);
const re = new RegExp(
head + "(?:=|\\s+)(?:'([^']*)'|\"((?:[^\"\\\\]|\\\\.)*)\"|(\\S+))"
);
const m = text.match(re);
if (!m) return null;
if (m[1] !== undefined) return m[1];
if (m[2] !== undefined) return m[2].replace(/\\(["\\])/g, "$1");
return m[3];
}

function escapeForRegex(s) {
return s.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
}

module.exports = { classifyPrCommand, CLOSING_REFERENCE };
84 changes: 84 additions & 0 deletions .opencode/skill-assets/github-coordination/lib/resolve-template.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
#!/usr/bin/env node
/**
* Resolve the issue-body template for a Work Type, letting a repo override the
* plugin default.
*
* @why A venture repo often has its own issue format. This is the single lookup
* both guided-issue-filing and manage-announcement use, so the override rule
* cannot drift between them: a committed repo-local template at
* <repo>/.github-coordination/templates/<work-type>.md wins; otherwise the
* plugin's bundled templates/ default is used.
*
* CLI: `node resolve-template.js <work-type>` prints the resolved absolute path
* on stdout and the source (RepoOverride | PluginDefault) on stderr. Exits 2 on
* an unknown Work Type so a caller never silently files against the wrong shape.
*/

"use strict";

const fs = require("fs");
const path = require("path");
const { execFileSync } = require("child_process");

// Work Type -> template filename. parent uses parent-issue.md; the rest match.
const WORK_TYPE_FILE = {
bug: "bug.md",
"ui-bug": "ui-bug.md",
"ux-issue": "ux-issue.md",
feature: "feature.md",
task: "task.md",
parent: "parent-issue.md",
announcement: "announcement.md",
};

// A venture drops its own templates here, committed and shared with the repo.
const REPO_OVERRIDE_DIR = path.join(".github-coordination", "templates");

function repoRoot() {
try {
return execFileSync("git", ["rev-parse", "--show-toplevel"], {
encoding: "utf8",
stdio: ["ignore", "pipe", "ignore"],
}).trim();
} catch {
// @why Outside a git repo there is no override location; fall back to the
// plugin default rather than failing.
return null;
}
}

/**
* @why Returns a tagged outcome (UnknownWorkType | RepoOverride | PluginDefault)
* so the CLI and any future caller branch on the source explicitly instead of
* guessing from a bare path string.
*/
function resolveTemplate(workType) {
const file = WORK_TYPE_FILE[workType];
if (!file) return { kind: "UnknownWorkType", workType };

const pluginRoot = path.resolve(__dirname, "..");
const root = repoRoot();
if (root) {
const override = path.join(root, REPO_OVERRIDE_DIR, file);
if (fs.existsSync(override)) return { kind: "RepoOverride", path: override };
}
return { kind: "PluginDefault", path: path.join(pluginRoot, "templates", file) };
}

function main() {
const workType = process.argv[2];
const outcome = resolveTemplate(workType);
if (outcome.kind === "UnknownWorkType") {
process.stderr.write(
"resolve-template: unknown Work Type '" + String(workType) + "'. " +
"Known: " + Object.keys(WORK_TYPE_FILE).join(", ") + "\n"
);
process.exit(2);
}
process.stderr.write(outcome.kind + "\n");
process.stdout.write(outcome.path + "\n");
}

if (require.main === module) main();

module.exports = { resolveTemplate, WORK_TYPE_FILE, REPO_OVERRIDE_DIR };
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
# Issue Plan Contract

<what-to-do>
Represent every coordinated issue write as one JSON Issue Plan and submit it once:

```bash
jakuta-agent-issue apply-plan --repo OWNER/REPO --plan-file <plan.json>
```

Use this exact shape:

```json
{
"plan_key": "checkout-recovery",
"references": {
"existing-parent": 12
},
"issues": [
{
"key": "retry-failed-payment",
"title": "Retry a failed payment",
"body_file": "bodies/retry-failed-payment.md",
"labels": ["feature"],
"parent": "existing-parent",
"blocked_by": []
}
]
}
```

For an existing managed issue, add `"issue_number": 34`. Keys use lowercase
words separated by hyphens and stay stable across reruns. Body paths are
relative to the plan file. `parent` is a managed key, a key in `references`, or
`null`. Every `blocked_by` entry is likewise a managed or reference key.

Before applying a plan, make it complete:

- `title`, `body_file`, and `labels` are the exact desired issue state.
- `parent` is the exact desired native GitHub parent.
- `blocked_by` is the exact desired native GitHub blocker set.
- `references` names existing issues that supply relationships without becoming
managed issues.
- Every managed body includes the Agent Attribution Marker exactly once.

When an existing issue enters a plan, read its current title, body, labels, and
native relationships first. Carry forward every value that should remain. Do
not submit a partial representation and expect omitted state to survive.

Use GitHub reads to inspect native relationships:

```bash
gh api --paginate repos/OWNER/REPO/issues/NUMBER/sub_issues
gh api --paginate repos/OWNER/REPO/issues/NUMBER/dependencies/blocked_by
gh api graphql -F owner=OWNER -F name=REPO -F number=NUMBER \
-f query='query($owner:String!,$name:String!,$number:Int!){repository(owner:$owner,name:$name){issue(number:$number){parent{number}}}}'
```

Run one mutation command for the whole request. Read the structured result to
report the resolved issue numbers and URLs. A rerun uses the same plan and keys;
the command reconciles instead of creating duplicates.
</what-to-do>

<supporting-info>
The plan is the whole desired result, not a sequence of issue-sized operations.
Validation happens before GitHub writes, and the command owns creation, updates,
labels, native parent links, and native blocked-by links together.
</supporting-info>
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<!--
Work Type: announcement
Audit Skill (optional): none.
Routing: not part of the Parent/Child Tree. An Announcement is a repo-local
coordination item (release note, pinned issue, or pinned discussion). It is not
an org-wide broadcast and never a public gist.
The body below is the Canonical Issue Body for the announcement.
-->

## Headline

<one plain line: what changed or what the reader needs to know>

## Details

<what shipped, what is affected, what the reader should do, if anything>

## Applies to

<version / date window / surface this announcement covers>

## Links

- <release, PR, or issue this references>

<!-- AGENT_ATTRIBUTION_MARKER appended here by manage-announcement -->
35 changes: 35 additions & 0 deletions .opencode/skill-assets/github-coordination/templates/bug.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
<!--
Work Type: bug
Audit Skill (optional): github-coordination:audit-issues
Routing: the Issue Plan records any parent or blocker in GitHub's native graph.
The issue body below is the Canonical Issue Body — the authoritative record.
-->

## What's broken

<one plain sentence: what the user sees go wrong>

## Steps to reproduce

1. <step>
2. <step>
3. <step>

## Expected vs actual

- **Expected:** <what should happen>
- **Actual:** <what happens>

## Environment

<os / browser / build / commit — whatever pins the bug down>

## Evidence-Backed Acceptance Criteria

<!-- Each box needs attached Evidence (link, log, screenshot, PR ref, command output) before it may be checked. A bare checkbox is not done. -->

- [ ] The reproduction no longer triggers the failure — Evidence:
- [ ] A regression test covers this path — Evidence:
- [ ] The fix PR references and closes this issue — Evidence:

<!-- AGENT_ATTRIBUTION_MARKER appended here by guided-issue-filing -->
Loading
Loading