Use when investigating an external tool's behavior, bug, quirk, capability gap, or fix difficulty; proactively write the troubleshooting doc the moment you finish diagnosing or working around one, even when the user did not ask; also use when writing or updating a doc/troubleshooting/<topic>.md file. The write-up is a required completion step, not an offer.
Installs into .claude/skills of the current project.
Sponsors
SSkills DirectorySkills Directory
Know which skills are safe — weekly.
SSkills DirectorySkills Directory
Is that skill safe to install?
Are you the author of Troubleshooting Doc?
Add the live security badge to your README. It updates with every re-scan.
[](https://www.skillsdirectory.com/skills/aquaticats-troubleshooting-doc)
---
name: troubleshooting-doc
description: >-
Use when investigating an external tool's behavior, bug, quirk, capability gap, or fix difficulty;
proactively write the troubleshooting doc the moment you finish diagnosing or working around one, even
when the user did not ask; also use when writing or updating a doc/troubleshooting/<topic>.md file.
The write-up is a required completion step, not an offer.
---
# Writing a troubleshooting file
Fires automatically,
no user request needed,
when you investigate an
external tool's behavior,
bug,
quirk,
capability gap,
or fix difficulty.
The source-clone and clone-boundary sections apply during investigation.
It also fires the moment you finish diagnosing or land a workaround for
an external tool's bug,
quirk,
surprising behavior,
or capability gap.
Writing the doc is a required completion step,
not something to offer or
defer:
do not declare the work done,
and do not say "I could document
this" or "want me to write it up?
",
until doc/troubleshooting/<topic>.
md
exists.
Walk this skill end-to-end whenever you write or update one.
Other surface phrases that should trigger the skill:
"document this",
"write it up",
"add a troubleshooting entry".
But the primary trigger is self-initiated:
you just fixed or explained an
external tool behaving unexpectedly,
so write it up now without being asked.
The skill encodes the required sections,
the source-trace rule,
source-clone investigation rule,
third-party clone boundary,
out-of-scope upstream-filing check,
and the 6-constraint upstream-filing check that gates the draft GitHub
issue at the end.
A troubleshooting file is the durable artifact of investigating an
external tool.
Future sessions and external readers must be able to
reproduce,
verify,
and act on every claim.
The canonical worked example
is [doc/troubleshooting/resharp.md](../../../doc/troubleshooting/resharp.md);
match its shape unless the topic genuinely lacks a section.
## File naming
`doc/troubleshooting/<topic>.md`.
`<topic>` is kebab-case,
specific enough to distinguish from sibling docs (`bun-fetch-streaming`,
not `bun`).
One bug or one cluster of related bugs per file.
## External source investigation
When investigating an external tool's behavior,
bug,
quirk,
capability gap,
or fix difficulty,
clone the tool's source and read the
relevant code path.
"No public diagnosis exists" is not a stopping
point when the source is open.
Cite file path,
line number,
and code
excerpt whenever you present a source finding.
This applies whether you reproduced the bug yourself,
are summarizing an
undiagnosed tracker report,
or are estimating whether a fix is possible.
## Third-party clone boundaries
Never modify files in cloned repositories we do not own for local
workarounds.
"Third-party" is determined by repository ownership,
not
origin URL:
a fork under our git user's GitHub namespace is our code and
may be edited,
including to prepare a pull request.
For repos we do not own,
use configuration,
environment variables,
wrapper scripts,
or consumer-side code at our boundary.
The only
unforked-clone edit path this skill permits is the disposable prototype
clone in "Auto-prototype when constraints 1-5 hold or sorta-hold".
## Required sections
1. **Title (`#`)**:
one-line problem statement naming the tool,
the
version,
the surface trigger,
and the resulting failure mode.
See
the resharp doc's title for the shape.
2. **Symptom**:
what the user sees.
Quote error strings verbatim,
list
the surface-syntax patterns that trigger them,
and note which error
variant each pattern produces when more than one exists.
3. **Root cause**:
the call chain that produces the failure,
walked
step by step.
Every claim about source code cites
`path/to/file.ext:LINE` and quotes the relevant code excerpt in a
fenced block.
Asserting "the parser rewrites X" without showing the
rewrite is not allowed;
this is the doc-writing specialization of
`AGENTS.md`'s broader source-citation rule.
Link every citation of a source this repository does not own to the
selection itself:
a web page gets a `#:~:text=` text fragment,
and a file in a hosted repository gets the platform's selection
permalink (on GitHub,
a commit-pinned URL with a line range,
for
example `blob/<commit>/path/file.ext#L10-L20`).
Load each link in `agent-browser` and confirm it lands on the quoted
text before it goes into the doc or into an issue draft.
Files in this repository keep repo-relative paths.
If a previous reading or
hypothesis was wrong,
name it explicitly and quote the evidence
that disproved it,
so the next investigator does not re-derive the
bad cause (the resharp doc's "earlier alternation-count reading was
wrong" paragraph is the shape).
4. **Verification**:
version under test (with crates.
io checksum,
commit hash,
or release tag),
a runnable harness (shell invocation
of the affected binary,
a minimal source-level program in the
target tool's language,
or both when each surfaces different
information),
and at least two catalogs:
patterns that work
cleanly and patterns that fail.
When multiple error variants exist,
split the failing catalog by variant so each failure mode is
enumerated.
5. **Verified workarounds**:
each workaround is a runnable patch with
its tradeoffs named (what semantics shift,
what edge cases slip
through).
A workaround whose tradeoffs you have not stated is not
verified.
6. **What does not work**:
approaches you tried and rejected,
with the
reason each failed.
Saves the next investigator from re-discovering
dead ends.
7. **Upstream filing artifact**:
a new-issue draft,
an additive-comment
draft,
or an explicit "nothing to add" note,
gated by the 6-constraint
check below and the duplicate search.
## The 6-constraint upstream-filing check
Before filing upstream,
all six must hold.
Write an "Upstream filing
decision" subsection that walks each constraint explicitly,
whatever the
outcome;
the audit trail is the point.
The neutral heading is deliberate:
it holds whether the decision is to file,
not file,
comment on a
duplicate,
or post nothing.
Default policy:
do not file.
Every reported
issue that does not satisfy all six is treated as a publicity incident.
1. **Is it really upstream's fault?
** Distinguish behavior from
wording from architectural restriction.
2. **Can upstream fix it?
** This fails only when upstream genuinely
cannot:
an architectural or algebraic-core limitation that makes the
fix impossible.
A fix that is large,
structural,
or spread across
several files still meets this constraint.
Trace the depth to scope
the prototype,
not to decide pass/fail.
3. **Are they supporting this use case?
** Look for docs,
examples,
tests,
or stated value propositions covering the combination.
4. **Would the repo welcome our contribution?
** Check `CONTRIBUTING.md`,
issue templates,
pull request templates,
README,
maintainer docs,
repository policies,
and recent maintainer responses for signals
about external contributions and AI-assisted reports.
This fails
when the repo says not to file issues or patches from outside
contributors,
requires a fully human-authored filing we cannot
truthfully provide,
bans AI-assisted or heavily-AI-assisted issue
filings,
or maintainers have rejected comparable AI-assisted
contributions.
Absence of a policy is not a fail:
cite the files and
tracker searches checked,
and say no ban was found.
When the policy
allows AI assistance only with disclosure,
the draft must disclose
assistance and name the reproduction,
source trace,
prototype,
and
workaround checks a human verified.
5. **Will they likely fix it?
** This fails only when upstream is
actively leaning no,
shown by their docs (a documented won't-fix or
stated non-goal) or direct communication (a maintainer declining a
comparable request).
Absence of signal is not a fail:
no existing
issue,
no recent commits in the path,
or silence all still meet this
constraint.
Check the upstream tracker,
commit history,
and release
deltas to inform the draft,
not to gate it;
cite what you find.
6. **Have we prototyped a minimal fix compatible with their
architecture?
** "Minimal" means the smallest set of changes that
actually solves the issue;
it does not constrain the fix's
complexity.
The minimal set may span several files or be intricate.
Speculative "suggested fix" prose without code,
a correctness
argument,
or tests against a nontrivial set does not count.
### Candidate-fix applicability gate
Before applying the auto-prototype requirement to a known upstream issue,
verify candidate fix is absent from installed source,
and candidate issue's version,
signal/stack,
and inputs match observed failure.
A reproduction that fails only on pre-fix source proves upstream history,
not current incident.
Treat it as an excluded hypothesis:
record it in "What does not work",
do not create or keep its patch,
and collect missing incident evidence instead.
### Auto-prototype when constraints 1-5 hold or sorta-hold
When constraints 1-5 are all "yes" or "yes-with-a-soft-yes" (e.g. #5
reads "plausible" or "likely" rather than a definitive yes),
do not
stop at "constraint 6:
not yet" and report the gate failed.
Prototype
the minimal fix yourself before declaring the audit done,
working either
in a fork under our own account (our code,
not a third-party clone;
prefer this when the prototype is headed for a pull request) or in a
disposable upstream clone.
The disposable-clone path:
1. Before first use,
ensure current scratch root exists with private permissions:
`mkdir --parents "${HOME}/temp/agent"; chmod 700 "${HOME}/temp/agent"`.
Clone upstream source into a fresh,
private,
unpredictable directory created with
`mktemp --directory "${HOME}/temp/agent/upstream-prototype.XXXXXXXX"`
(or an equivalently private throwaway workspace under `~/temp/agent/`).
Never reuse an existing `~/temp/agent/<repo>` clone or any other
pre-existing directory for this step.
2. Confirm the clone's `origin` URL and checked-out commit/tag match the
upstream source cited in the doc before editing.
3. Apply the minimal set of changes that solves the cause identified in
"Root cause":
the smallest complete fix,
not the simplest-looking
one.
Do not pad it with unrelated cleanup,
and do not shrink it below
what actually solves the issue because it looks large or spans
several files.
4. Verify with the least-trusting harness that proves the change:
prefer
a targeted minimal program or existing reproduction harness over the
upstream package's full test/build scripts.
Run upstream scripts only
inside a secret-free sandbox or container with no ambient credentials
and no write access to this repository.
Whatever you pick must surface
the failure pre-patch and the success post-patch;
"it compiles" is
not verification.
5. Record the result,
the verification command,
and the verification
output inline in the doc.
For the diff itself:
if it is small
(single hunk,
roughly under 20 lines including context),
embed it
inline in a fenced `diff` block.
If it is larger (multiple hunks,
multiple files,
or long enough that inline embedding would crowd
the doc),
save it beside the doc as
`doc/troubleshooting/<topic>.patch` (matching the doc's `<topic>`,
prefix dropped since the directory names the family) and link to it
from the doc with a relative path (the bare filename,
same directory).
The inline-vs-file
threshold is about readability,
not significance;
either form
satisfies constraint 6 as long as the patch is reproducible from
what is recorded.
With this recording in place,
the audit ends
with all six constraints "yes" and the draft becomes fileable.
The trigger is "constraints 1-5 hold or sorta-hold";
not "the fix is
already known to be small" and not "we are sure upstream will accept
this.
" A large or multi-file minimal set is still in scope:
prototype
it.
Sometimes the minimal fix is a one-line terminfo or config-table
change that costs less than the audit itself,
but size is never the
gate,
neither to skip the prototype nor to require it.
If prototyping reveals the change breaks the architectural core or
fails the tool's existing tests in unrelated places,
re-evaluate
constraints 2 and 5 with the new evidence and revise the audit.
A fix
that is merely large or intricate is not grounds to re-evaluate;
only
an architectural blocker is.
The prototype is also a probe;
a failed
probe is useful data,
not a wasted step.
The third-party clone boundary above still applies to local workarounds,
where editing source bypasses the intended boundary.
Two paths are open
for the prototype.
A fork under our own account is our code,
not a
third-party repository,
so editing it is unrestricted;
prefer the fork
when the prototype is headed for a pull request.
Otherwise this skill
permits one narrow exception for an unforked upstream clone:
the
disposable prototype clone described here,
created fresh for the upstream
patch diff,
after origin verification,
and verified without exposing
credentials or this repository to third-party scripts.
The consumer-side workaround belongs at our boundary (e.g. a parse-time
guard in the consuming crate) where it solves the user-facing problem
regardless of upstream movement.
## Check `.out-of-scope/` before filing upstream
Before filing or drafting an upstream issue/comment,
read `.out-of-scope/`
for the tool or bug class.
A listed exemption means upstream tracking is
out of scope:
still write or update `doc/troubleshooting/<topic>.md`,
still record symptoms,
root cause,
verification,
and workarounds,
but skip
GitHub issue/comment filing.
Record the exemption path and reason in the "Upstream filing decision"
subsection.
If no exemption matches,
continue with duplicate search and
the 6-constraint check.
## Check for an existing issue before filing
A duplicate report is itself a publicity incident,
so before opening
anything new,
search the upstream tracker for the same behavior:
`gh search issues` and `gh search prs` across open and closed state,
with terms drawn from the symptom and the root cause,
not just the tool
name.
Constraint 5 already runs this search to gauge movement;
this step
acts on a hit.
Read the matching thread and its comments in full,
not
just the title.
If a matching issue exists,
do not open a second one.
The contribution
becomes a comment on that issue,
and the comment carries only what
genuinely advances the thread beyond what is already there:
a sharper
root-cause trace,
a reproduction they lack,
affected-version data,
the
prototyped fix and diff,
or a workaround not yet mentioned.
Diff your
findings against the existing thread before writing.
If nothing you have is absent from the thread,
the comment is empty:
post nothing.
A bare "+1",
"me too",
"still happens",
or a subscribe is
not additive and is not a comment worth making.
An empty comment is the
correct outcome when the thread already says everything you would,
not a
gap to fill.
Record the duplicate in the doc:
link the existing issue,
and when there
is additive content keep the drafted comment the same way the draft
issue is kept (in a `~~~md` fence,
ready to post).
When there is nothing
to add,
say so explicitly so a future session does not re-derive the
same empty comment.
## Check whether someone is already fixing it
A search for the same report does not find a fix that is being written but not yet linked to any report.
Before filing,
and again on the day of filing,
look for work in progress on the code path,
and record the queries and what each returned:
- Open and draft pull requests,
and pull requests merged in the last few days,
that touch the files or functions named in the root cause
(`gh search prs`,
and `gh pr list --state all --search "<path or function>"`).
- Branches on the upstream and on forks whose names or diffs match the symptom.
- Comments on the matching and near-miss issues that claim the work ("I'll take this",
"working on a fix"),
and assignees on them.
- Recent commits by maintainers on the same files (`gh api repos/<owner>/<repo>/commits --method GET -f path=<file>`).
Fresh maintainer churn in the code path means the owner is working there now:
say so in the filing decision,
and read their open work before posting.
A claimed or in-progress fix turns the filing into a comment on that work,
or into nothing,
by the same rule as a duplicate report.
No hit is a result worth one line in the doc,
with the queries,
so the next session does not repeat them.
## Risk to the poster before any filing
Always draft the artifact;
drafting is never held back by this section.
Filing is what puts a real person's account in front of strangers,
so this section governs what the doc says about filing:
weigh that person's risk before saying anything about whether to file
and before marking a draft fileable.
- Some repositories run trust or blocklist systems.
Vouch is one;
others exist,
some more thorough,
and this skill does not survey them.
They close or lock issues and pull requests from unvouched users,
and a maintainer can denounce a poster.
- The lists are files in a repository or published service,
and projects can aggregate each other's lists,
so a denouncement can follow a person from one project to others.
Other people may see it:
interviewers,
employers,
colleagues,
and maintainers of unrelated projects.
Do not assume it stays private or can be undone.
- An agent-written report that a maintainer judges low-effort is exactly
the case these systems exist for.
A reproduction,
a source trace and a prototype lower that risk
and do not remove it.
- A recommendation to file is allowed,
and only when the work earns it:
all six constraints hold,
the reproduction was re-run,
the prototype was verified against a baseline,
the duplicate search and the trust-system search were done and
recorded with dates,
and no known limit leaves the evidence weak.
When any of those is missing,
say plainly what is missing and make no recommendation.
- Every statement about filing,
a recommendation included,
names this risk next to it and says filing is the poster's decision.
A recommendation that skips the risk,
or that rests on unchecked work,
is the bad recommendation this section exists to prevent.
- Before the draft is marked fileable,
search the target repository for a trust or blocklist system
(workflow files,
a `VOUCHED` or similar list,
contribution-policy text),
record what was searched with the date and the commit,
and tell the filer to check the live repository again on the day of
posting.
## Readiness check before anyone files
HQP:
Before filing or asking a user to file,
attempt the authorized fix prototype and report its test result.
Report-only handoffs need an explicit scope choice or a concrete prototype blocker.
Run this whenever the user asks whether a draft is ready,
and run it before marking any draft fileable.
Record each item with the date,
the commit or release it was checked against,
and what was observed.
An item that was not run is written as not run,
never as passed.
1. Reproduce on the latest release and on every other maintained major line.
Read the release list,
not memory.
A bug on one line may be fixed or absent on another,
and a project that fixes bugs "in every affected version" needs the
affected versions named.
State which lines were tested and which were not.
When a report proposes one edit for several copies of a text
(versioned documentation pages,
mirrors),
test each copy's claim against its own release line before accepting the scope:
a suggestion to fix every copy can be wrong for the older line.
Find the editable source too,
because a generated copy is overwritten by its generator.
2. Read prior art in the code the bug touches.
Search merged and closed pull requests and issues for the symbols,
file names and error text involved,
not only the symptom words,
and read their descriptions in full.
A maintainer's stated reason for the current behavior changes the
framing:
the draft cites it and argues for narrowing it,
and does not present a documented decision as an oversight.
3. Fit the target's intake.
Read its issue forms,
template configuration,
pull request template and contribution policy.
Apply the template to everything posted there,
not only new issues:
a pull request body,
an edited issue body and a comment each follow the template or form that covers them,
with every section present in its order and only the items the template says to remove left out.
A body written before the template was read is rewritten,
not defended.
Note what is required,
which fields only the poster can answer
(a runtime version the reproduction never recorded is one),
whether blank issues are off,
and what the tracker says issues are for.
Never invent an answer for a field.
List those fields outside the fenced block.
A checkbox such as "I am working on a fix" says the poster will open the pull request,
and the form may assign the issue to them.
Tick it whenever the doc records a prototyped fix that passes its verification,
with no question to the user:
the prototype is the plan,
and an unticked box invites someone else to start the same fix.
Leave it unticked when there is no prototype.
A prototyped fix also means the pull request is opened together with the issue,
once the readiness check recommends filing,
with no wait for a maintainer to ask and no question to the user about issue versus pull request.
The pull request closes the issue and follows the target's pull request template,
changeset rules and disclosure footer.
Open only the issue when the target's written contribution policy asks for an issue or approval first
(quote the rule),
or when no prototype passes its verification.
A maintainer's stated decision on the behavior is not such a policy:
name it in the pull request body and argue for narrowing it,
as item 2 requires.
4. Read what automation does to a new filing:
triage bots,
auto-labelers,
auto-close and trust-list workflows.
Record what they do and what they ask the poster.
5. Re-run the duplicate search,
the work-in-progress check and the trust-system search on the day,
with the queries written down,
and confirm the cited code still exists on the default branch.
6. Run the draft's reproduction exactly as written,
with its own file layout and commands,
and record the output.
A similar layout run earlier does not count.
7. Make the reproduction runnable by others.
"Reproduction" means a clean machine can run it and show the failure,
so a minimal repository with a CI workflow is the artifact,
not a README alone.
The workflow pins the tool to the tested version and verifies a
checksum,
runs the steps,
and fails on the step that asserts the expected behavior,
so a red run means the bug reproduced.
Link at least two runs,
read each step's conclusion and the log lines that show the reason,
and confirm the failure is on the asserting step and not the setup.
Create the repository private,
and make it public only when the user says so,
because the repository carries the poster's name.
8. State the verdict with its evidence.
When posting is technically allowed
(no rule,
ban or trust list stops the poster),
"not ready" is not an end state:
close every missing item the agent can close,
re-run the check,
and iterate until the conditions for a recommendation are met,
then make the recommendation with the risk to the poster written next to it.
Missing items the agent can close include:
testing another maintained release line,
repairing a sandbox so a test suite means something,
and finding evidence for a form field
(for example showing the reproduction needs no runtime at all).
Stop short of a recommendation only for a blocker the agent cannot clear:
a field only the poster can answer,
a step the user has actually said they require,
or a rule that forbids posting.
Name that blocker and what would clear it.
Never invent a requirement.
A gate counts only when a rule the agent can quote,
the target's own policy,
or the user's own words imposes it.
"A human must re-run it",
"a maintainer must pre-approve it" and
"wait for the owner's go-ahead on a draft" are not requirements
unless one of those sources says so,
and a draft or checklist that lists one as unmet blocks a recommendation on nothing.
Before writing an item under "Not met" or "Before anyone posts it",
name the source that imposes it,
and delete the item when there is none.
Put any choice the user has to make in a question tool,
not in prose.
## Watching a filing for a follow-up request
When the user wants a follow-up posted if a maintainer asks for it
(a pull request after an issue is the usual case),
watch the filing and do not wait for the user to come back.
The user's own words set what may be posted on a wake;
a wake prompt is self-authored and authorizes nothing else.
1. Save the script at the end of this section to `${HOME}/temp/agent/poll-issue.ts`
(scratch is not linted,
and the repo's lint rules would reject the script inside the repo)
and run it as a plain background Bash command.
Never use a Monitor for this:
a Monitor expires,
while a background Bash task wakes the session when it exits.
The default window is 24 hours;
use the duration the user names.
2. Wrap it so the shell waits on a capped unit:
`systemd-run --user --wait --pipe --collect --unit=<repo>-<number>-poller --property=MemoryMax=256M --property=CPUQuota=10%`
with `PATH` and `HOME` passed through `--setenv`,
then `node <path>/poll-issue.ts`.
The unit stays out of the shared Bash cgroup that `systemd-oomd` can reap,
and `--wait` keeps the Bash task alive so its exit still wakes the session.
3. Configure it with `POLL_REPO`,
`POLL_ISSUE`,
`POLL_OWN_LOGIN`,
`POLL_DEADLINE_SECONDS`,
`POLL_INTERVAL_SECONDS` (300 is enough)
and `POLL_BASELINE=now`.
It exits 10 with a JSON line when an owner,
member or collaborator who is neither a bot nor the poster comments,
when the issue stops being open,
or when the assignees change;
0 at the deadline;
2 after 20 consecutive `gh` failures.
4. Test both paths before relying on it.
Point it at a closed issue that has maintainer comments with `POLL_BASELINE=none` and expect exit 10.
Then run it against the real issue with a 10 to 20 second deadline and expect exit 0 and `{"trigger":"deadline"}`.
5. In the same turn write `doc/handover/<topic>.md` with the unit name,
the start and the deadline,
the exit codes,
what the user pre-authorized in their own words,
and the steps to take on a wake.
A watch that exists only in the conversation is lost at compaction.
6. On a wake,
read the comment itself.
Post only what the user's words authorized,
and ask the user about anything else.
Stop the unit when the work is done or the user cancels it:
`systemctl --user stop <unit>`.
```ts
// ${HOME}/temp/agent/poll-issue.ts
// Polls one GitHub issue until a maintainer acts on it or the deadline passes, then exits so the caller is woken.
// Exit codes: 10 = something to act on (details on stdout), 0 = deadline reached with nothing to act on,
// 2 = too many consecutive gh failures.
// Environment: POLL_REPO, POLL_ISSUE, POLL_OWN_LOGIN, POLL_DEADLINE_SECONDS, POLL_INTERVAL_SECONDS,
// POLL_BASELINE (`now` ignores comments that exist at start; `none` treats them all as new, for testing).
import { execFileSync } from 'node:child_process';
const repo = process.env.POLL_REPO ?? 'pnpm/pnpm';
const issue = process.env.POLL_ISSUE ?? '17485';
const ownLogin = process.env.POLL_OWN_LOGIN ?? 'Aquaticat';
const deadlineSeconds = Number(process.env.POLL_DEADLINE_SECONDS ?? 86400,);
const intervalSeconds = Number(process.env.POLL_INTERVAL_SECONDS ?? 300,);
const baselineMode = process.env.POLL_BASELINE ?? 'now';
const maintainerAssociations = new Set(['OWNER', 'MEMBER', 'COLLABORATOR',],);
type Comment = { readonly id: number; readonly user: { readonly login: string; readonly type: string; }; readonly author_association: string; readonly body: string; readonly html_url: string; };
type Issue = { readonly state: string; readonly assignees: readonly { readonly login: string; }[]; };
const gh = (path: string,): string => execFileSync('gh', ['api', '--paginate', path,], { encoding: 'utf8', timeout: 120000, },);
const log = (message: string,): void => console.error(`${new Date().toISOString()} ${message}`,);
const sleep = (seconds: number,): Promise<void> => new Promise((resolve,) => setTimeout(resolve, seconds * 1000,),);
const readComments = (): readonly Comment[] => JSON.parse(`[${gh(`repos/${repo}/issues/${issue}/comments`,).replaceAll('][', ',',).replace(/^\[/, '',).replace(/\]\s*$/, '',)}]`,) as Comment[];
const readIssue = (): Issue => JSON.parse(gh(`repos/${repo}/issues/${issue}`,),) as Issue;
const isMaintainerComment = (comment: Comment,): boolean =>
comment.user.type !== 'Bot' && comment.user.login !== ownLogin && maintainerAssociations.has(comment.author_association,);
const started = Date.now();
const seen = new Set<number>();
let failures = 0;
let initialAssignees: string | undefined;
if (baselineMode === 'now') for (const comment of readComments()) seen.add(comment.id,);
log(`polling ${repo}#${issue} every ${intervalSeconds}s for ${deadlineSeconds}s, ${seen.size} existing comments ignored`,);
while ((Date.now() - started) / 1000 < deadlineSeconds) {
try {
const comments = readComments();
const current = readIssue();
const assignees = current.assignees.map((assignee,) => assignee.login,).join(',',);
initialAssignees ??= assignees;
failures = 0;
const fresh = comments.filter((comment,) => !seen.has(comment.id,),);
for (const comment of fresh) seen.add(comment.id,);
const maintainerComments = fresh.filter(isMaintainerComment,);
if (maintainerComments.length > 0) {
console.log(JSON.stringify({ trigger: 'maintainer-comment', comments: maintainerComments.map((comment,) => ({ by: comment.user.login, association: comment.author_association, url: comment.html_url, body: comment.body.slice(0, 1500,), }),), },),);
process.exit(10,);
}
if (current.state !== 'open') {
console.log(JSON.stringify({ trigger: 'issue-not-open', state: current.state, },),);
process.exit(10,);
}
if (assignees !== initialAssignees) {
console.log(JSON.stringify({ trigger: 'assignees-changed', from: initialAssignees, to: assignees, },),);
process.exit(10,);
}
} catch (error) {
failures += 1;
log(`poll failed (${failures}): ${String(error,).slice(0, 200,)}`,);
if (failures >= 20) process.exit(2,);
}
await sleep(intervalSeconds,);
}
console.log(JSON.stringify({ trigger: 'deadline', },),);
process.exit(0,);
```
## Upstream filing artifact format
Keep the draft whatever the decision:
when all six constraints hold it
is fileable as-is,
and when they do not it stays as an auditable record
a future session can re-evaluate if upstream signal changes.
Mark it
"do not file as-is" only in that second case.
Wrap the draft body in a
`~~~md` fence so the future filer can copy it cleanly.
The new-issue
draft contains:
title,
labels,
description with the same source trace as the "Root cause" section,
reproduction code from "Verification",
and a "Suggested fix" naming
concrete code locations.
When "Check for an existing issue" found a duplicate,
the kept artifact
is the additive comment draft (or an explicit note that there was
nothing to add),
not a new-issue draft presented as fileable.
## Quality checks before declaring the doc done
- Every "the source does X" claim has a file path,
line number,
and
code excerpt next to it.
- Every citation of a source this repository does not own links to its
selection (a `#:~:text=` fragment or a commit-pinned line-range
permalink),
and `agent-browser` confirmed each link lands on the quoted text.
- If a filing artifact exists,
the doc states the risk to the poster
(see "Risk to the poster before any filing"),
and any recommendation to file meets the conditions listed there.
- The readiness check ran,
each item is recorded with a date and what was observed,
and every item that did not run says so.
- The draft's own reproduction was run exactly as written,
and a clean-runner workflow reproduces it.
- No claim is wider than what was read or run:
"only",
"never" and "always" appear only where the whole space was checked,
and two observations from different runs are not joined into one.
- The harness reproduces the failure when run as written.
- Every workaround names a tradeoff.
- The 6-constraint subsection is present even when you decide to file,
so the rationale is auditable.
- `.out-of-scope/` was checked before upstream filing.
If an exemption
matched,
the doc names its path and does not keep a fileable draft.
- The draft issue is wrapped in a fenced block and marked "do not file
as-is" unless all six constraints hold.
- The upstream tracker was searched for a duplicate.
If one exists,
the
doc links it and either keeps a fenced,
additive-only comment draft or
states explicitly there is nothing to add;
it does not also present a
new-issue draft as fileable.
- If constraints 1-5 held or sorta-held but constraint 6 read "not
yet,
" the audit is incomplete;
either run the auto-prototype step
and record the result,
or document why prototyping was attempted and
abandoned.
"Not yet" without that follow-up is the failure mode this
skill explicitly catches.