Skip to content

Commit 477f583

Browse files
bradygasterCopilot
andauthored
fix(gh-aw): publish research only on explicit request (#2194)
Remove bootstrap research seeding and recovery requirements while preserving Cast provenance and proposal creation. Require research before triage in launch guidance and cover compiled retries, comment preservation, and no-seed mutation detection. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a0288984-b7ca-479f-991d-ab5aaa8ede61
1 parent 71818eb commit 477f583

8 files changed

Lines changed: 190 additions & 420 deletions

File tree

‎.changeset/fix-bootstrap-triage-continuity.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,6 @@
33
"@bradygaster/squad-sdk": patch
44
---
55

6-
Make automatic bootstrap proposals immediately available to `/squad triage`
7-
after the linked Cast PR merges, and recognize the deterministic bootstrap Cast
8-
branch during team-required command gating.
6+
Keep automatic bootstrap limited to the Cast PR and linked proposals issue;
7+
publish research only on explicit `/squad research` before triage. Recognize
8+
the deterministic bootstrap Cast branch during team-required command gating.

‎docs/src/content/docs/guide/gh-aw.md‎

Lines changed: 9 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -290,7 +290,7 @@ missing, duplicate, open, foreign, or merged archived PRs fail closed. Closed
290290
fallback issues may be included in `archived_issues`; unrelated issues cannot.
291291
The operation never closes artifacts, deletes branches, or overwrites an
292292
installed team or registry. A merged current-generation Cast may still finish
293-
its linked research through ordinary recovery.
293+
its linked proposals issue through ordinary recovery.
294294

295295
Keep the same record and ID for retries: they select the same branch and artifact
296296
identities. Existing branch contents must match the validated payload; divergent
@@ -308,12 +308,14 @@ closed. If the originating dispatch failed after publishing provenance, use
308308
**Re-run all jobs** on that same run with its original inputs; another run
309309
cannot promote a failed origin into successful provenance.
310310

311-
Review and merge the Cast PR, then rerun `/squad triage` on the linked issue to
312-
classify its existing bootstrap proposals. If a proposal needs deeper or newer
313-
evidence, use one of the issue's focused `/squad research ...` commands first;
314-
that replaces the bootstrap research seed. Review the resulting plan and run
315-
`/squad activate`. The bootstrap journey ends when assignable implementation
316-
issues exist.
311+
Bootstrap creates the Cast PR and linked proposals issue only; it does not
312+
publish a research comment or artifact. Review and merge the Cast PR, then run
313+
`/squad research` on the linked issue, optionally using one of its focused
314+
proposal commands. Once research is published, run `/squad triage` to classify
315+
the findings, then `/squad plan`. Review the resulting plan and run
316+
`/squad activate`. Bootstrap retries do not require research and never modify
317+
existing research comments. The bootstrap journey ends when assignable
318+
implementation issues exist.
317319

318320
---
319321

‎test/gh-aw-bootstrap-workflow.test.ts‎

Lines changed: 121 additions & 86 deletions
Large diffs are not rendered by default.

‎test/gh-aw-lifecycle-upsert.test.ts‎

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -216,6 +216,34 @@ describe('#1935: deterministic research artifact safe output', () => {
216216
].join('\n');
217217
const boldSectionBody = body.replace(/^### (.+)$/gm, '**$1**');
218218

219+
it('publishes complete explicit research without a preexisting bootstrap artifact', async () => {
220+
const completeBody = [
221+
body,
222+
'',
223+
'### Online sources',
224+
'Online sources: unavailable — repository evidence only.',
225+
'',
226+
'### Recommendations',
227+
'- R1: address the verified finding before planning.',
228+
'',
229+
'### Next step',
230+
'Run `/squad triage` or `/squad plan`.',
231+
].join('\n');
232+
const result = await runResearchUpsert([
233+
{ type: 'upsert_research_artifact', body: completeBody },
234+
]);
235+
236+
expect(result.failures).toEqual([]);
237+
expect(result.updated).toEqual([]);
238+
expect(result.deleted).toEqual([]);
239+
expect(result.created).toEqual([{
240+
owner: 'octodemo',
241+
repo: 'consumer',
242+
issue_number: 5,
243+
body: `${completeBody}\n\nStructured data:\n\n\`\`\`json\n{"squad_artifact":"research","schema_version":"1","origin_issue":5,"phases":[]}\n\`\`\``,
244+
}]);
245+
});
246+
219247
it('accepts bold section labels and creates the fixed structured envelope', async () => {
220248
const result = await runResearchUpsert([
221249
{ type: 'upsert_research_artifact', body: boldSectionBody },

‎workflows/package/squad-bootstrap.md‎

Lines changed: 11 additions & 89 deletions
Original file line numberDiff line numberDiff line change
@@ -84,24 +84,10 @@ pre-agent-steps:
8484
})).filter((issue) => !issue.pull_request);
8585
let state;
8686
try {
87-
const preliminary = stateModule.classifyBootstrapState({
88-
...resetState,
89-
pullRequests,
90-
issues,
91-
defaultBranch: process.env.SQUAD_BOOTSTRAP_DEFAULT_BRANCH,
92-
});
93-
const comments = preliminary.issue
94-
? await github.paginate(github.rest.issues.listComments, {
95-
...context.repo,
96-
issue_number: preliminary.issue.number,
97-
per_page: 100,
98-
})
99-
: [];
10087
state = stateModule.classifyBootstrapState({
10188
...resetState,
10289
pullRequests,
10390
issues,
104-
comments,
10591
defaultBranch: process.env.SQUAD_BOOTSTRAP_DEFAULT_BRANCH,
10692
});
10793
} catch (error) {
@@ -116,9 +102,7 @@ pre-agent-steps:
116102
? state.pull_request.merge_commit_sha
117103
: state.pull_request?.head_sha;
118104
core.setOutput('cast_ref', castRef || '');
119-
core.info(
120-
`Bootstrap recovery action: ${state.action}; research artifacts: ${state.research_artifact_count}`,
121-
);
105+
core.info(`Bootstrap recovery action: ${state.action}`);
122106
- name: Restore an existing Cast tree for partial recovery
123107
if: steps.bootstrap-state.outputs.cast_ref != ''
124108
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1
@@ -171,7 +155,7 @@ pre-agent-steps:
171155
# BEGIN GENERATED RESOURCE DIGESTS
172156
check_hash "$install_verifier" "a279cd5c4adeb613ceb90c1bfbb9818265aeb6bc8986e2e3799e96f7c2d787e5"
173157
check_hash "$cast_validator" "c6d0b92aac71dc6f6d5727cac418a323b0bc9c12047400faa12d96150d548ada"
174-
check_hash "$bootstrap_validator" "ac969d1cc92353219ba312d0d9af8702b1a7a569b4e8d5c79ae71e4aa020aad6"
158+
check_hash "$bootstrap_validator" "8462546aa0d2efa37a52ea1df9ffa1b1f0cb7df4e4b17a64e98a11f05cb9d449"
175159
# END GENERATED RESOURCE DIGESTS
176160
node "$bootstrap_validator" \
177161
--encode-payload "${GITHUB_WORKSPACE:?}/.github/workflows/squad-bootstrap-payload.json" \
@@ -385,28 +369,13 @@ safe-outputs:
385369
state: 'all',
386370
per_page: 100,
387371
})).filter((issue) => !issue.pull_request);
388-
const preliminary = stateModule.classifyBootstrapState({
389-
...resetState,
390-
pullRequests,
391-
issues,
392-
defaultBranch: process.env.SQUAD_BOOTSTRAP_DEFAULT_BRANCH,
393-
});
394-
const comments = preliminary.issue
395-
? await github.paginate(github.rest.issues.listComments, {
396-
...context.repo,
397-
issue_number: preliminary.issue.number,
398-
per_page: 100,
399-
})
400-
: [];
401372
return {
402373
pullRequests,
403374
issues,
404-
comments,
405375
state: stateModule.classifyBootstrapState({
406376
...resetState,
407377
pullRequests,
408378
issues,
409-
comments,
410379
defaultBranch: process.env.SQUAD_BOOTSTRAP_DEFAULT_BRANCH,
411380
}),
412381
};
@@ -422,7 +391,7 @@ safe-outputs:
422391
return;
423392
}
424393
if (snapshot.state.action === 'noop') {
425-
core.info('The deterministic Cast PR, research-proposals issue, and research artifact already exist.');
394+
core.info('The deterministic Cast PR and research-proposals issue already exist.');
426395
return;
427396
}
428397
@@ -722,66 +691,23 @@ safe-outputs:
722691
const markedIssueBody = `${finalPayload.issue_body.slice(0, issueBodyNewline)}\n${provenanceMarker}${finalPayload.issue_body.slice(issueBodyNewline)}`;
723692
724693
snapshot = await listState();
725-
let issueNumber;
726694
if (snapshot.state.issue) {
727695
if (snapshot.state.issue.state !== 'open') {
728696
core.info(`Bootstrap issue #${snapshot.state.issue.number} is closed; preserving human state.`);
729697
return;
730698
}
731-
issueNumber = snapshot.state.issue.number;
732699
await github.rest.issues.update({
733700
...context.repo,
734-
issue_number: issueNumber,
701+
issue_number: snapshot.state.issue.number,
735702
title: stateModule.BOOTSTRAP_ISSUE_TITLE,
736703
body: markedIssueBody,
737704
});
738705
} else {
739-
const createdIssue = await github.rest.issues.create({
706+
await github.rest.issues.create({
740707
...context.repo,
741708
title: stateModule.BOOTSTRAP_ISSUE_TITLE,
742709
body: markedIssueBody,
743710
});
744-
issueNumber = createdIssue.data.number;
745-
}
746-
747-
const researchBody = validatorModule.createBootstrapResearchComment(
748-
finalPayload.issue_body,
749-
issueNumber,
750-
);
751-
const comments = await github.paginate(github.rest.issues.listComments, {
752-
...context.repo,
753-
issue_number: issueNumber,
754-
per_page: 100,
755-
});
756-
const researchArtifacts = validatorModule.findBootstrapResearchArtifacts(
757-
comments,
758-
issueNumber,
759-
);
760-
const currentResearch = researchArtifacts.at(-1);
761-
if (currentResearch) {
762-
if (validatorModule.isBootstrapResearchSeed(currentResearch)) {
763-
await github.rest.issues.updateComment({
764-
...context.repo,
765-
comment_id: currentResearch.id,
766-
body: researchBody,
767-
});
768-
} else {
769-
core.info(
770-
`Preserving focused research artifact comment #${currentResearch.id}.`,
771-
);
772-
}
773-
for (const duplicate of researchArtifacts.slice(0, -1)) {
774-
await github.rest.issues.deleteComment({
775-
...context.repo,
776-
comment_id: duplicate.id,
777-
});
778-
}
779-
} else {
780-
await github.rest.issues.createComment({
781-
...context.repo,
782-
issue_number: issueNumber,
783-
body: researchBody,
784-
});
785711
}
786712
} finally {
787713
rmSync(candidate, { recursive: true, force: true });
@@ -812,8 +738,6 @@ Read `.github/workflows/squad-bootstrap-state.json` before doing any analysis.
812738
issue from it, and request materialization.
813739
- `create_pr`: generate the Cast tree and request materialization; the writer
814740
updates the one marked issue with the real PR link.
815-
- `create_research`: preserve both linked artifacts and materialize or repair
816-
their one canonical structured research comment.
817741

818742
Never request more than one `materialize_bootstrap` output. The typed writer
819743
rechecks all pages of pull requests and issues, fails closed on duplicate or
@@ -948,15 +872,13 @@ The issue body must:
948872
11. End at assignable implementation issues in the actionable-backlog checklist.
949873
12. Include these exact numbered guidance lines:
950874
- `1. Review and merge the linked draft Cast PR.`
951-
- `2. Rerun /squad triage to classify these existing proposals, or use focused /squad research ... first when deeper research is desired.` Wrap each command in Markdown code spans.
875+
- `2. Run /squad research on the proposals you want to pursue, then /squad triage to classify the research findings.` Wrap each command in Markdown code spans.
952876
- `3. Run /squad plan, review the plan, then run /squad activate to create assignable implementation issues.` Wrap each command in Markdown code spans.
953877

954-
The typed writer derives one concise canonical `squad_artifact=research`
955-
comment from these validated proposal sections. Do not repeat the full research
956-
artifact in the payload or issue body. A later focused `/squad research ...`
957-
run replaces this seed through the normal research upsert contract. Bootstrap
958-
recovery preserves a newer focused research artifact rather than replacing it
959-
with the shorter seed.
878+
Bootstrap creates only the Cast PR and linked proposals issue, not a research
879+
comment or artifact. Research is published only when a human invokes
880+
`/squad research` (optionally with a proposal focus), before triage or planning.
881+
Bootstrap recovery never creates, updates, or deletes research comments.
960882

961883
Keep the issue concise and evidence-led. Do not copy the detailed research
962884
exemplar's long audit format.
@@ -981,7 +903,7 @@ Exit status zero with stdout exactly
981903
means the one output has already been submitted. Stop immediately.
982904
Never read, print, extract, or transcribe `squad-bootstrap-envelope.json` or its
983905
chunks. Never invoke `materialize_bootstrap` separately, retry the command, or
984-
split Cast and research into separate outputs.
906+
split Cast and proposals into separate outputs.
985907

986908
Any validation, envelope, or submission error is terminal: report the exact
987909
stderr and stop without another materialization attempt.

0 commit comments

Comments
 (0)