Skip to content

Commit 82bafec

Browse files
authored
feat: add thread research briefs (#8)
1 parent ebc561c commit 82bafec

23 files changed

Lines changed: 3157 additions & 113 deletions

README.md

Lines changed: 25 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -132,11 +132,12 @@ gitcontribute search code "context.WithTimeout" --repo golang/go
132132

133133
### 2. Investigate
134134

135-
Build a dossier, inspect repository health, record a hypothesis, and check for
136-
duplicate or competing work before committing time.
135+
Build a dossier or per-thread research brief, inspect repository health, record
136+
a hypothesis, and check for duplicate or competing work before committing time.
137137

138138
```sh
139139
gitcontribute dossier build owner/repo
140+
gitcontribute research brief issue:owner/repo#42 --format markdown
140141
gitcontribute health owner/repo --json
141142
gitcontribute investigation start owner/repo --json
142143
gitcontribute hypothesis add --title="Fix retry timeout" \
@@ -195,7 +196,7 @@ MCP capabilities are deliberately separate:
195196

196197
| Capability | Examples |
197198
| --- | --- |
198-
| **Offline reads** | Search, inspect repositories and threads, read dossiers, explain matches, inspect evidence and opportunities. |
199+
| **Offline reads** | Search, inspect repositories and threads, build research briefs, read dossiers, explain matches, inspect evidence and opportunities. |
199200
| **Network reads** | Sync repositories, hydrate threads, start crawls, and acquire workspaces. |
200201
| **Local writes** | Start investigations, record hypotheses, promote opportunities, define validations, and prepare drafts. |
201202
| **Execution** | Run a validation only when the request includes `execute: true`. |
@@ -208,7 +209,7 @@ application and adapter boundaries.
208209

209210
| Operation | Network | Local write | Runs a process | GitHub write |
210211
| --- | :---: | :---: | :---: | :---: |
211-
| Search, health, dossier inspection |||||
212+
| Search, health, dossier and research-brief inspection |||||
212213
| Investigations, evidence, lenses |||||
213214
| Sync, crawl, hydrate |||||
214215
| Acquire or create a workspace | remote-dependent || `git` only ||
@@ -327,7 +328,7 @@ enforces size limits, and rejects dirty worktrees.
327328
</details>
328329

329330
<details>
330-
<summary><strong>Radar, search, dossiers, health, seeds, and lenses</strong></summary>
331+
<summary><strong>Radar, research briefs, search, dossiers, health, seeds, and lenses</strong></summary>
331332

332333
```sh
333334
gitcontribute radar owner/repo --limit 20
@@ -341,6 +342,8 @@ gitcontribute search all "retry" --repo owner/repo
341342
gitcontribute dossier build owner/repo
342343
gitcontribute dossier export owner/repo --format markdown \
343344
--output owner-repo-dossier.md
345+
gitcontribute research brief owner/repo#42
346+
gitcontribute research brief pr:owner/repo#108 --json
344347
gitcontribute health owner/repo --stale-after 336h --json
345348
gitcontribute seeds owner/repo --json
346349
```
@@ -361,6 +364,23 @@ Radar scores carry a version (`radar.v1`) so saved JSON remains auditable when
361364
ranking semantics evolve. It never syncs, hydrates, executes repository code,
362365
or writes to GitHub.
363366

367+
`research brief` is also a strict offline read. Its versioned
368+
`research-brief.v1` output has fixed sections for state, stored problem fields,
369+
acceptance hints, participants, timeline, explicit/duplicate references,
370+
linked PRs, indexed code, contribution guidance, health, coverage gaps, and
371+
next commands. Every section carries source references or an explicit unknown
372+
reason. Checkboxes and maintainer phrases remain source extracts—not fabricated
373+
or complete acceptance criteria. Markdown output redacts common credentials and
374+
quotes untrusted source text; JSON ordering is deterministic.
375+
376+
Missing child facets and code stay visible instead of triggering hidden work:
377+
378+
```sh
379+
gitcontribute archive hydrate owner/repo#42 --with issue_comments
380+
gitcontribute index owner/repo /path/to/clean-checkout
381+
gitcontribute research brief issue:owner/repo#42 --json
382+
```
383+
364384
Use a lens to apply saved filters and weighted ranking to a bounded population:
365385

366386
```sh

docs/architecture.md

Lines changed: 7 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ TUI ---------+ |
2222
observations + projections
2323
|
2424
v
25-
offline search, radar, health, dossiers,
25+
offline search, radar, health, dossiers, thread briefs,
2626
investigations, evidence, and drafts
2727
```
2828

@@ -43,7 +43,7 @@ application and domain packages expose product-owned values and interfaces.
4343

4444
| Capability | Examples | Network | Local write | Process execution | GitHub mutation |
4545
| --- | --- | ---: | ---: | ---: | ---: |
46-
| Corpus read | search, health, dossier show, MCP resources | no | no | no | no |
46+
| Corpus read | search, health, dossier show, research brief, MCP resources | no | no | no | no |
4747
| Corpus write | investigations, evidence, lenses, tracking | no | yes | no | no |
4848
| GitHub read | sync, crawl, hydrate | yes | yes | no | no |
4949
| Git acquisition | acquire, workspace create | remote-dependent | yes | `git` only | no |
@@ -153,8 +153,11 @@ uses a bounded population and therefore does not support cursor pagination.
153153
Contribution Radar similarly ranks a bounded open-issue population, separates
154154
eligibility from score, and reports positive signals, risks, blockers, and
155155
unknown evidence. Missing coverage is never silently converted into a negative
156-
signal. Health metrics and dossier generation also operate only on stored facts
157-
and report partial or missing coverage when required facets are incomplete.
156+
signal. Health metrics, dossier generation, and thread research briefs also
157+
operate only on stored facts and report partial or missing coverage when
158+
required facets are incomplete. A research-brief section must carry a source
159+
reference or an explicit unknown reason; untrusted thread text remains data and
160+
cannot grant an adapter additional authority.
158161

159162
## Schema changes
160163

internal/app/control.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,7 @@ func (s *Service) Metadata(ctx context.Context) (*cli.MetadataResult, error) {
4444
capabilities := []string{
4545
"archive", "clustering", "collections", "contribution-radar", "dossiers", "evidence",
4646
"github-read", "investigations", "local-search", "mcp-stdio",
47-
"validation", "workspaces",
47+
"thread-research-brief", "validation", "workspaces",
4848
}
4949
sort.Strings(capabilities)
5050
return &cli.MetadataResult{
@@ -62,6 +62,7 @@ func (s *Service) Metadata(ctx context.Context) (*cli.MetadataResult, error) {
6262
"github_mutations": false,
6363
"mcp_stdio": true,
6464
"semantic_search": false,
65+
"thread_research": true,
6566
"validation_exec": true,
6667
},
6768
}, nil

internal/app/control_test.go

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,9 @@ func TestMetadataIsLocalAndDoesNotCreateCorpus(t *testing.T) {
4141
if !result.Features["contribution_radar"] || !containsString(result.Capabilities, "contribution-radar") {
4242
t.Fatalf("radar capability missing from metadata: %+v", result)
4343
}
44+
if !result.Features["thread_research"] || !containsString(result.Capabilities, "thread-research-brief") {
45+
t.Fatalf("thread research capability missing from metadata: %+v", result)
46+
}
4447
if _, err := os.Stat(result.CorpusPath); !os.IsNotExist(err) {
4548
t.Fatalf("metadata created corpus %q: %v", result.CorpusPath, err)
4649
}

internal/app/research.go

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package app
2+
3+
import (
4+
"context"
5+
"errors"
6+
7+
"github.com/morluto/gitcontribute/internal/cli"
8+
"github.com/morluto/gitcontribute/internal/research"
9+
)
10+
11+
// ThreadResearchBrief builds a deterministic brief from the local corpus. It
12+
// performs no network access, local mutation, or process execution.
13+
func (s *Service) ThreadResearchBrief(ctx context.Context, ref research.ThreadRef) (*research.Brief, error) {
14+
brief, err := research.NewBuilder(&corpusReader{s: s}, s.now).Build(ctx, ref)
15+
if err == nil {
16+
return brief, nil
17+
}
18+
if errors.Is(err, errRepositoryNotFound) || errors.Is(err, research.ErrThreadNotFound) || errors.Is(err, research.ErrThreadKindMismatch) {
19+
return nil, cli.NewCLIError(cli.ExitNotFound, err)
20+
}
21+
return nil, err
22+
}

0 commit comments

Comments
 (0)