diff --git a/.github/ISSUE_TEMPLATE/1-bug.yml b/.github/ISSUE_TEMPLATE/1-bug.yml new file mode 100644 index 0000000..ea1f5a9 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/1-bug.yml @@ -0,0 +1,105 @@ +name: šŸ› Bug +description: Something behaves incorrectly and you can show it. +title: "bug: " +labels: ["type:bug", "needs:triage"] +body: + - type: markdown + attributes: + value: | + Lead with the error. A worked example lives in + [EXAMPLE.md](../blob/HEAD/.github/ISSUE_TEMPLATE/EXAMPLE.md). + + - type: textarea + id: problem + attributes: + label: Problem + description: The symptom you observed, in one or two sentences. Not your theory. + placeholder: Agent runs abort at ~70% of the context limit with BudgetExceeded. + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Error output + description: Full traceback, failing assertion, alert, or log line. Redact secrets. + render: shell + validations: + required: true + + - type: textarea + id: reproduce + attributes: + label: Reproduction + description: Exact commands from a clean checkout. A failing test is ideal. + value: | + 1. `git clone … && cd …` + 2. `uv sync` + 3. `pytest tests/test_x.py::test_y` + render: shell + validations: + required: true + + - type: input + id: expected + attributes: + label: Expected behavior + placeholder: Budget reports the actual measured token count. + validations: + required: true + + - type: input + id: version + attributes: + label: Version / commit + description: Release tag or full commit SHA. "latest" is not a version. + placeholder: v0.9.3 or 4f2a1c9 + validations: + required: true + + - type: dropdown + id: severity + attributes: + label: Severity + description: This routes triage. Be honest; inflation gets ignored. + options: + - S4 — cosmetic or minor annoyance + - S3 — degraded, workaround exists + - S2 — major function broken, no workaround + - S1 — production down, data loss, or security exposure + validations: + required: true + + - type: dropdown + id: env + attributes: + label: Environment + multiple: true + options: [local (macOS), local (Linux), CI (GitHub Actions), dev, staging, prod] + validations: + required: true + + - type: input + id: regression + attributes: + label: Last known good version + description: Leave blank if it never worked. If set, a bisect range is implied. + placeholder: v0.9.1 + + - type: textarea + id: context + attributes: + label: Anything else + description: Suspected cause, related issues, screenshots, workaround in use. + + - type: checkboxes + id: gates + attributes: + label: Before submitting + options: + - label: I searched open and closed issues for a duplicate. + required: true + - label: I reproduced this on the latest release or main. + required: true + - label: No secrets, tokens, or customer data appear anywhere above. + required: true diff --git a/.github/ISSUE_TEMPLATE/2-feature.yml b/.github/ISSUE_TEMPLATE/2-feature.yml new file mode 100644 index 0000000..beb53c7 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/2-feature.yml @@ -0,0 +1,79 @@ +name: ✨ Feature +description: Propose a capability that does not exist yet. +title: "feat: " +labels: ["type:feature", "needs:triage"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: The user-facing pain, not the solution. Who is blocked, and on what? + placeholder: Operators cannot tell which agent consumed a token budget, so cost overruns are diagnosed by guesswork. + validations: + required: true + + - type: textarea + id: today + attributes: + label: What you do today + description: The current workaround and its cost in time, money, or risk. + validations: + required: true + + - type: textarea + id: proposal + attributes: + label: Proposed behavior + description: What should exist. Include the interface — CLI flag, API shape, config key. + render: shell + validations: + required: true + + - type: textarea + id: alternatives + attributes: + label: Alternatives considered + description: Options you rejected and why. "None" usually means the problem is underexplored. + validations: + required: true + + - type: textarea + id: done + attributes: + label: Definition of done + description: Observable, checkable outcomes. This becomes the acceptance criteria. + value: | + - [ ] + - [ ] + validations: + required: true + + - type: dropdown + id: scope + attributes: + label: Scope + options: + - S — under a day, one file or module + - M — a few days, one repo + - L — multi-repo or migration required + - XL — needs a design doc first + validations: + required: true + + - type: dropdown + id: breaking + attributes: + label: Breaking change? + options: ["No", "Yes — behind a flag", "Yes — requires a major bump"] + validations: + required: true + + - type: checkboxes + id: gates + attributes: + label: Before submitting + options: + - label: I searched for an existing request covering this. + required: true + - label: This is a concrete capability, not an open-ended question (those go to Discussions). + required: true diff --git a/.github/ISSUE_TEMPLATE/3-task.yml b/.github/ISSUE_TEMPLATE/3-task.yml new file mode 100644 index 0000000..917c424 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/3-task.yml @@ -0,0 +1,45 @@ +name: šŸ”§ Task +description: Known work with a known solution — chore, refactor, infra, docs. +title: "chore: " +labels: ["type:task"] +body: + - type: textarea + id: problem + attributes: + label: Problem + description: Why this work exists. What breaks or degrades if it is never done? + validations: + required: true + + - type: textarea + id: work + attributes: + label: Work + description: The steps. Each line should be independently verifiable. + value: | + - [ ] + - [ ] + validations: + required: true + + - type: dropdown + id: kind + attributes: + label: Kind + options: [chore, refactor, infra / IaC, CI / CD, docs, dependency bump, test coverage] + validations: + required: true + + - type: textarea + id: risk + attributes: + label: Risk and rollback + description: Blast radius if this goes wrong, and how to undo it. + validations: + required: true + + - type: input + id: blocks + attributes: + label: Blocks / blocked by + placeholder: "blocked by #1191, blocks #1204" diff --git a/.github/ISSUE_TEMPLATE/4-incident.yml b/.github/ISSUE_TEMPLATE/4-incident.yml new file mode 100644 index 0000000..bf7247d --- /dev/null +++ b/.github/ISSUE_TEMPLATE/4-incident.yml @@ -0,0 +1,90 @@ +name: 🚨 Incident +description: Production is degraded or down. File first, investigate second. +title: "incident: " +labels: ["type:incident", "sev:untriaged", "priority:P0"] +assignees: [] +body: + - type: markdown + attributes: + value: | + **Page first, file second.** If customers are affected and nobody is paged, + stop and page on-call. This issue is the written record, not the alarm. + If this is a security compromise, use a private advisory instead — not this form. + + - type: input + id: started + attributes: + label: Detected at (UTC) + placeholder: "2026-07-28T15:04:00Z" + validations: + required: true + + - type: textarea + id: problem + attributes: + label: Problem + description: Customer-visible symptom. What is broken, for whom, since when? + validations: + required: true + + - type: dropdown + id: severity + attributes: + label: Severity + options: + - S1 — full outage, data loss, or security exposure + - S2 — major degradation, no workaround + - S3 — partial degradation, workaround in place + validations: + required: true + + - type: textarea + id: evidence + attributes: + label: Signal + description: Alert name, dashboard link, error rate, sample traceback. + render: shell + validations: + required: true + + - type: textarea + id: impact + attributes: + label: Blast radius + description: Services, regions, tenants, and estimated request or user count. + validations: + required: true + + - type: textarea + id: mitigation + attributes: + label: Mitigation in progress + description: What is being done right now. Update this field as it evolves. + validations: + required: true + + - type: input + id: suspect + attributes: + label: Suspected trigger + description: Deploy, PR, config change, or upstream provider. Link it. + placeholder: "#1184, deployed 14:52Z" + + - type: input + id: commander + attributes: + label: Incident commander + placeholder: "@handle" + validations: + required: true + + - type: checkboxes + id: gates + attributes: + label: Response checklist + options: + - label: On-call paged. + required: true + - label: Status page or stakeholder comms updated, or explicitly not needed. + - label: Timeline is being kept in the comments below. + - label: Postmortem issue will be opened within 48h of resolution. diff --git a/.github/ISSUE_TEMPLATE/EXAMPLE.md b/.github/ISSUE_TEMPLATE/EXAMPLE.md new file mode 100644 index 0000000..ed02529 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/EXAMPLE.md @@ -0,0 +1,54 @@ + + +# bug: token budget aborts runs at ~70% of the real context limit + +**Problem** +Long agent runs abort well before the context limit. Operators see `BudgetExceeded` +at roughly 122k actual tokens against a 200k limit, so ~40% of usable context is +unreachable. + +**Error output** +``` +agentkit.budget.BudgetExceeded: 198,004 / 200,000 tokens at turn 42 + at agentkit/budget.py:88 in BudgetTracker.check + (independently measured context via tiktoken: 122,311 tokens) +``` + +**Reproduction** +``` +1. git clone git@github.com:acme/agentkit && cd agentkit && git checkout v0.9.3 +2. uv sync +3. pytest tests/replay/test_long_run.py::test_42_turns -> fails at turn 42 +``` + +**Expected behavior** +The tracker reports the actual measured token count, so runs continue to the real limit. + +**Version / commit** — v0.9.3 (`4f2a1c9`) +**Severity** — S2 — major function broken, no workaround +**Environment** — local (macOS), CI (GitHub Actions), prod +**Last known good version** — v0.8.7, so the regression is in the 0.9.0 reducer rewrite + +**Anything else** +Reported usage is consistently ~1.45x measured, and the ratio tracks the count of +tool-result messages — suggesting those are counted twice. `BudgetTracker.add()` +appears to be called from both `transport.py:212` and `reducer.py:96`. Workaround in +use: `AGENTKIT_BUDGET_LIMIT=290000` on the four affected agents, which is unsafe +because the real ceiling is then unenforced. + +**Before submitting** — searched (#902 is related but closed as fixed), reproduced on +main at `4f2a1c9`, trace excerpts scrubbed by `scripts/scrub_trace.py`. + +--- + +## Why this is a good report + +- The **problem** is the symptom an operator saw, not a theory about the cause. +- The traceback is complete, and it includes an **independent measurement** that + proves the number is wrong rather than merely surprising. +- Reproduction starts from a clean clone at a pinned SHA. +- **Last known good** turns a vague bug into a bisect range. +- The theory is present but quarantined in "anything else", below the facts. +- The workaround is stated **along with why it is unsafe**, which is what makes this + urgent rather than merely annoying. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml new file mode 100644 index 0000000..44b882f --- /dev/null +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -0,0 +1,11 @@ +blank_issues_enabled: false +contact_links: + - name: šŸ” Report a security vulnerability + url: https://github.com/Quantum-L9/.github/security/advisories/new + about: Do NOT open a public issue. Use a private security advisory. + - name: šŸ’¬ Question or design discussion + url: https://github.com/orgs/Quantum-L9/discussions + about: Open-ended questions, architecture debate, and "how do I…" belong in Discussions. + - name: šŸ“• Documentation + url: https://github.com/Quantum-L9/Cursor-Governance/blob/main/CANONICAL_LAW.md + about: Check the runbooks and reference before filing. diff --git a/.github/labels.yml b/.github/labels.yml new file mode 100644 index 0000000..ffcb582 --- /dev/null +++ b/.github/labels.yml @@ -0,0 +1,27 @@ +# Apply with: gh label create --file (or github-label-sync). Names are load-bearing: +# workflows in .github/workflows/issue-triage.yml write sev:*, priority:*, scope:*. +- { name: "type:bug", color: "d73a4a", description: "Incorrect behavior with evidence" } +- { name: "type:feature", color: "0e8a16", description: "New capability" } +- { name: "type:task", color: "c5def5", description: "Known work, known solution" } +- { name: "type:incident", color: "b60205", description: "Production degraded or down" } +- { name: "sev:S1", color: "b60205", description: "Outage, data loss, or security exposure" } +- { name: "sev:S2", color: "d93f0b", description: "Major function broken, no workaround" } +- { name: "sev:S3", color: "fbca04", description: "Degraded, workaround exists" } +- { name: "sev:S4", color: "fef2c0", description: "Cosmetic" } +- { name: "sev:untriaged", color: "ededed", description: "Severity not yet assessed" } +- { name: "priority:P0", color: "b60205", description: "Drop everything" } +- { name: "priority:P1", color: "d93f0b", description: "This sprint" } +- { name: "priority:P2", color: "fbca04", description: "Backlog, scheduled" } +- { name: "priority:P3", color: "fef2c0", description: "Someday" } +- { name: "scope:S", color: "e4e669", description: "Under a day" } +- { name: "scope:M", color: "e4e669", description: "A few days, one repo" } +- { name: "scope:L", color: "e4e669", description: "Multi-repo or migration" } +- { name: "scope:XL", color: "e4e669", description: "Needs a design doc first" } +- { name: "needs:triage", color: "ededed", description: "Awaiting maintainer review" } +- { name: "needs:info", color: "ededed", description: "Blocked on the reporter; eligible for stale" } +- { name: "regression", color: "5319e7", description: "Worked in an earlier version" } +- { name: "breaking", color: "5319e7", description: "Requires a major version bump" } +- { name: "env:prod", color: "1d76db", description: "Observed in production" } +- { name: "area:ci", color: "1d76db", description: "CI/CD pipelines" } +- { name: "security:possible-leak", color: "b60205", description: "May contain a credential — rotate" } +- { name: "good first issue",color: "7057ff", description: "Scoped, documented, low context" } diff --git a/.github/workflows/issue-triage.yml b/.github/workflows/issue-triage.yml new file mode 100644 index 0000000..4410ff8 --- /dev/null +++ b/.github/workflows/issue-triage.yml @@ -0,0 +1,93 @@ +name: Issue triage + +on: + issues: + types: [opened, edited, reopened] + +permissions: + issues: write + +concurrency: + group: triage-${{ github.event.issue.number }} + cancel-in-progress: true + +jobs: + triage: + runs-on: ubuntu-latest + steps: + - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const issue = context.payload.issue; + const body = issue.body || ''; + const add = new Set(); + const notes = []; + + const field = name => { + const re = new RegExp(`###\\s*${name}\\s*\\n+([\\s\\S]*?)(?=\\n###\\s|$)`, 'i'); + const m = body.match(re); + return m ? m[1].replace(/_No response_/gi, '').trim() : ''; + }; + + // severity -> priority, single source of truth + const sev = field('Severity'); + const map = { S1: 'P0', S2: 'P1', S3: 'P2', S4: 'P3' }; + const key = (sev.match(/S[1-4]/) || [])[0]; + if (key) { add.add(`sev:${key}`); add.add(`priority:${map[key]}`); } + + // route by declared environment + const env = field('Environment').toLowerCase(); + if (env.includes('prod')) add.add('env:prod'); + if (env.includes('ci')) add.add('area:ci'); + + // a stated last-known-good implies a bisectable regression + if (field('Last known good version')) add.add('regression'); + + // scope from the feature form + const scope = (field('Scope').match(/^(XL|L|M|S)\b/) || [])[0]; + if (scope) add.add(`scope:${scope}`); + if (scope === 'XL') notes.push('Scope is **XL** — open a design doc before implementation work starts.'); + + if (/^(Yes|yes)/.test(field('Breaking change?'))) add.add('breaking'); + + // quality signals, not gatekeeping + const evidence = field('Error output') || field('Signal'); + if (evidence && !/```|\w{40,}|Traceback|Error|error/.test(evidence)) + notes.push('The **error output** field has no traceback or log lines. Paste raw output — it is the fastest path to a fix.'); + + const version = field('Version / commit'); + if (/^(latest|main|master|newest)$/i.test(version)) + notes.push('Please replace "' + version + '" in **Version / commit** with a release tag or full SHA, so this can be bisected.'); + + const repro = field('Reproduction'); + if (repro && repro.split('\n').filter(l => l.trim()).length < 2) + notes.push('**Reproduction** looks incomplete. Steps from a clean checkout let a maintainer confirm this without a round trip.'); + + // secret scan on the visible body — cheap, high value + const SECRETS = [ + [/\bgh[pousr]_[A-Za-z0-9]{20,}\b/, 'GitHub token'], + [/\bAKIA[0-9A-Z]{16}\b/, 'AWS access key id'], + [/\bsk-[A-Za-z0-9]{20,}\b/, 'API secret key'], + [/-----BEGIN [A-Z ]*PRIVATE KEY-----/, 'private key'], + [/\beyJ[A-Za-z0-9_-]{10,}\.[A-Za-z0-9_-]{10,}\./, 'JWT'], + ]; + const hits = SECRETS.filter(([re]) => re.test(body)).map(([, n]) => n); + if (hits.length) { + add.add('security:possible-leak'); + notes.push(`> [!CAUTION]\n> This issue may contain a **${hits.join(', ')}**. Edit it out now and **rotate the credential** — issue edit history is public.`); + } + + if (add.size) { + await github.rest.issues.addLabels({ ...context.repo, issue_number: issue.number, labels: [...add] }); + } + + if (notes.length && context.payload.action === 'opened') { + await github.rest.issues.createComment({ + ...context.repo, issue_number: issue.number, + body: `Thanks for the report. A few things that will speed this up:\n\n` + + notes.map(n => n.startsWith('>') ? n : `- ${n}`).join('\n') + + `\n\nSee the [worked example](../blob/HEAD/.github/ISSUE_TEMPLATE/EXAMPLE.md) for the standard.`, + }); + } + + if (hits.length) core.setFailed('Possible credential in issue body: ' + hits.join(', ')); diff --git a/.github/workflows/stale.yml b/.github/workflows/stale.yml new file mode 100644 index 0000000..977bbd8 --- /dev/null +++ b/.github/workflows/stale.yml @@ -0,0 +1,29 @@ +name: Stale issues + +on: + schedule: + - cron: "17 8 * * 1" + workflow_dispatch: + +permissions: + issues: write + +jobs: + stale: + runs-on: ubuntu-latest + steps: + - uses: actions/stale@5bef64f19d7facfb25b37b414482c7164d639639 # v9.1.0 + with: + days-before-stale: 60 + days-before-close: 14 + only-labels: needs:info + exempt-issue-labels: "type:incident,priority:P0,priority:P1,pinned,security:possible-leak" + stale-issue-label: stale + stale-issue-message: > + This has sat 60 days awaiting the requested information. It will close in + 14 days unless updated. Reopening later is fine and costs nothing. + close-issue-message: > + Closing for now — no information received. Comment with the missing details + and it will be reopened. + days-before-pr-stale: -1 + days-before-pr-close: -1 diff --git a/docs/issue-templates/DEPLOY.md b/docs/issue-templates/DEPLOY.md new file mode 100644 index 0000000..d176414 --- /dev/null +++ b/docs/issue-templates/DEPLOY.md @@ -0,0 +1,82 @@ +# Deploy + +## 1. Place the files + +```bash +./scripts/install.sh /path/to/org/.github +cd /path/to/org/.github +git checkout -b feat/issue-templates +git add .github docs && git commit -m "feat(issues): org-wide issue forms, taxonomy, triage" +git push -u origin feat/issue-templates +``` + +The repository must be named exactly `.github` and be **public** for org-wide +defaults. Merge to the default branch; forms appear on the next new issue in any +repo without its own `ISSUE_TEMPLATE/` directory. + +Note the override semantics: a repo with even one local form ignores **all** org +defaults. There is no merge. If a repo needs one extra form, copy the whole set. + +## 2. Fix the placeholders in config.yml + +`contact_links` ships with `YOUR_ORG` placeholders. Replace all three: + +```bash +grep -rl YOUR_ORG .github | xargs sed -i '' 's/YOUR_ORG/acme/g' # macOS +``` + +Confirm the security advisory URL resolves, and that private vulnerability +reporting is enabled org-wide (**Settings → Code security → Private vulnerability +reporting**). The link is the only thing standing between you and a zero-day filed +as a public issue. + +## 3. Create the labels + +Triage writes `sev:*`, `priority:*`, `scope:*`, `regression`, `breaking`, `env:prod`, +`area:ci`, and `security:possible-leak`. `addLabels` creates missing labels silently +with a random color, so seed them deliberately: + +```bash +./scripts/sync-labels.sh acme/agentkit +# or across the org +gh repo list acme --json nameWithOwner -q '.[].nameWithOwner' \ + | xargs -n1 ./scripts/sync-labels.sh +``` + +## 4. Distribute the workflows + +Workflows in `.github` do **not** run for other repos — only the templates +propagate. Convert `issue-triage.yml` to `workflow_call` and have each repo add a +caller pinned to a tag: + +```yaml +on: + issues: + types: [opened, edited, reopened] +jobs: + triage: + uses: YOUR_ORG/.github/.github/workflows/issue-triage.yml@v1 +``` + +## 5. Validate + +```bash +gh issue create --template 1-bug.yml # forms are prefilled by name +gh issue view --json labels -q '.labels[].name' +gh run list --workflow "Issue triage" +``` + +Field parsing depends on the rendered `###