mirror of
https://github.com/garrytan/gstack.git
synced 2026-10-03 01:46:55 +02:00
* feat: add surface-aware exploratory QA and ship documentation gates * test: preserve delegated QA setup authority after main integration * fix(qa): clarify exploration order and preserve report artifacts * test(qa): follow the shared setup reference directly * refactor(ship): make verification and recovery routes explicit * test(ship): align evidence and review guards with explicit routes * fix(workflows): clarify ship recovery and functional QA evidence * fix(workflows): clarify approval recovery and full QA coverage * refactor(workflows): order review transactions and clarify ship state * fix(ship): clarify final verification and fail closed at publication * fix(evals): attribute native atomic documentation writes * fix(ship): clarify recovery and documentation lifecycle guidance * fix(test): preserve observed native placeholder styling in CI * fix(codex): report watchdog timeouts without a process-exit race * Checkpoint functional QA implementation and workflow validation repairs * Fix documentation and shared-review fixture contracts * docs: clarify judge reuse and evaluation supervision * test: align review evidence and selected case contracts * test: verify append-only documentation checkpoints and recovery * fix: qualify QA workflows and CI validation repairs * fix: launch shared-libs fixture scripts on Windows * fix: qualify QA deadlines, fixture isolation, and shard cleanup * fix: preserve qualified QA and cancellation repairs * fix: enforce functional fixture authority and share strict event decoding * fix: retain free-test evidence and explain recovery * fix: reject malformed native evidence after decoder consolidation * test: use reliable capture for telemetry privacy filters * test: refresh measured quick coverage and document validation costs * Fix native fixture receipts and preserve VM validation evidence * Align negative judge controls with upstream clarity policy * Fix report-only QA preparation and public evidence handling * Clarify QA-only preparation and current-report preservation * Stream Ship quality judgments with an explicit 64k response contract * Validate compact judge reasoning locally with supported wire schema * Align functional QA fixture instructions with evidence acceptance * Bind native browser diagnostics to execution evidence and align review verdicts * Preserve native diagnostic line boundaries * Serialize functional QA evidence from native captures * Keep large QA evidence fixture payload out of Windows argv
135 lines
6.2 KiB
TypeScript
135 lines
6.2 KiB
TypeScript
/**
|
|
* Learnings resolver — cross-skill institutional memory
|
|
*
|
|
* Learnings are stored per-project at ~/.gstack/projects/{slug}/learnings.jsonl.
|
|
* Each entry is a JSONL line with: ts, skill, type, key, insight, confidence,
|
|
* source, branch, commit, files[].
|
|
*
|
|
* Storage is append-only. Duplicates (same key+type) are resolved at read time
|
|
* by gstack-learnings-search ("latest winner" per key+type).
|
|
*
|
|
* Cross-project discovery is opt-in. The resolver asks the user once via
|
|
* AskUserQuestion and persists the preference via gstack-config.
|
|
*/
|
|
import type { TemplateContext } from './types';
|
|
import { getHostConfig } from '../../hosts/index';
|
|
|
|
// Whitelist for query= macro values. Allows alphanumeric, space, hyphen, underscore.
|
|
// Anything else (e.g. $, backticks, quotes, ;) is a shell-injection vector when the
|
|
// emitted bash interpolates the value into `--query "${queryArg}"`. Static template
|
|
// queries hand-written in gstack are safe, but the resolver API must defend against
|
|
// future contributors writing dangerous values.
|
|
const QUERY_SAFE_RE = /^[A-Za-z0-9 _-]+$/;
|
|
|
|
export function generateLearningsSearch(ctx: TemplateContext, args?: string[]): string {
|
|
// Parse query= arg. Empty value falls through to no-query (principle of least surprise:
|
|
// a stray {{LEARNINGS_SEARCH:query=}} placeholder gets today's behavior, not a build error).
|
|
const queryArg = (args || [])
|
|
.filter(a => a.startsWith('query='))
|
|
.map(a => a.slice(6))
|
|
.filter(Boolean)[0];
|
|
if (queryArg && !QUERY_SAFE_RE.test(queryArg)) {
|
|
throw new Error(
|
|
`{{LEARNINGS_SEARCH:query=...}} value must match ${QUERY_SAFE_RE} (alphanumeric, space, hyphen, underscore). Got: ${JSON.stringify(queryArg)}`
|
|
);
|
|
}
|
|
const queryFlag = queryArg ? ` --query "${queryArg}"` : '';
|
|
const findingKind = ctx.skillName === 'qa' || ctx.skillName === 'qa-only' ? 'QA' : 'review';
|
|
|
|
if (ctx.skillName === 'qa-only') {
|
|
return `## Prior Learnings
|
|
|
|
Read this project's existing learnings.jsonl only if its directory is already known
|
|
and the caller permits that Read. Otherwise skip this optional lookup.
|
|
${queryArg ? `Look for notes matching "${queryArg}".\n` : ''}Do not run gstack-learnings-search here: its slug helper can update a cache.
|
|
Do not change configuration, enable cross-project search or create a learning store.
|
|
|
|
Treat old notes as leads, not proof. When a QA finding matches a past learning,
|
|
cite it as "Prior learning applied: [key] (confidence N/10, from [date])" and verify
|
|
the current behavior. Reading old notes never requires writing new ones.`;
|
|
}
|
|
|
|
if (getHostConfig(ctx.host).learningsMode === 'basic') {
|
|
// Basic learnings mode (host config learningsMode: 'basic' — every host
|
|
// except claude and factory): simpler version, no cross-project prompt,
|
|
// uses $GSTACK_BIN (all basic hosts are env-var hosts)
|
|
return `## Prior Learnings
|
|
|
|
Search for relevant learnings from previous sessions on this project:
|
|
|
|
\`\`\`bash
|
|
$GSTACK_BIN/gstack-learnings-search --limit 10${queryFlag} 2>/dev/null || true
|
|
\`\`\`
|
|
|
|
If learnings are found, incorporate them into your analysis. When a ${findingKind} finding
|
|
matches a past learning, note it: "Prior learning applied: [key] (confidence N, from [date])"`;
|
|
}
|
|
|
|
return `## Prior Learnings
|
|
|
|
Search for relevant learnings from previous sessions:
|
|
|
|
\`\`\`bash
|
|
_CROSS_PROJ=$(${ctx.paths.binDir}/gstack-config get cross_project_learnings 2>/dev/null || echo "unset")
|
|
echo "CROSS_PROJECT: $_CROSS_PROJ"
|
|
if [ "$_CROSS_PROJ" = "true" ]; then
|
|
${ctx.paths.binDir}/gstack-learnings-search --limit 10${queryFlag} --cross-project 2>/dev/null || true
|
|
else
|
|
${ctx.paths.binDir}/gstack-learnings-search --limit 10${queryFlag} 2>/dev/null || true
|
|
fi
|
|
\`\`\`
|
|
|
|
If \`CROSS_PROJECT\` is \`unset\` (first time): ${ctx.skillName === 'plan-eng-review' ? 'Build a full decision brief from these facts and options using the preamble format, then ask and wait:' : 'Use AskUserQuestion:'}
|
|
|
|
> gstack can search learnings from your other projects on this machine to find
|
|
> patterns that might apply here. This stays local (no data leaves your machine).
|
|
> Recommended for solo developers. Skip if you work on multiple client codebases
|
|
> where cross-contamination would be a concern.
|
|
|
|
Options:
|
|
- A) Enable cross-project learnings (recommended)
|
|
- B) Keep learnings project-scoped only
|
|
|
|
If A: run \`${ctx.paths.binDir}/gstack-config set cross_project_learnings true\`
|
|
If B: run \`${ctx.paths.binDir}/gstack-config set cross_project_learnings false\`
|
|
|
|
Then re-run the search with the appropriate flag.
|
|
|
|
If learnings are found, incorporate them into your analysis. When a ${findingKind} finding
|
|
matches a past learning, display:
|
|
|
|
**"Prior learning applied: [key] (confidence N/10, from [date])"**
|
|
|
|
This makes the compounding visible. The user should see that gstack is getting
|
|
smarter on their codebase over time.`;
|
|
}
|
|
|
|
export function generateLearningsLog(ctx: TemplateContext): string {
|
|
const binDir = ctx.paths.binDir; // env-var hosts already resolve to $GSTACK_BIN via types.ts
|
|
|
|
return `## Capture Learnings
|
|
|
|
If you discovered a non-obvious pattern, pitfall, or architectural insight during
|
|
this session, log it for future sessions:
|
|
|
|
\`\`\`bash
|
|
${binDir}/gstack-learnings-log '{"skill":"${ctx.skillName}","type":"TYPE","key":"SHORT_KEY","insight":"DESCRIPTION","confidence":N,"source":"SOURCE","files":["path/to/relevant/file"]}'
|
|
\`\`\`
|
|
|
|
**Types:** \`pattern\` (reusable approach), \`pitfall\` (what NOT to do), \`preference\`
|
|
(user stated), \`architecture\` (structural decision), \`tool\` (library/framework insight),
|
|
\`operational\` (project environment/CLI/workflow knowledge).
|
|
|
|
**Sources:** \`observed\` (you found this in the code), \`user-stated\` (user told you),
|
|
\`inferred\` (AI deduction), \`cross-model\` (both Claude and Codex agree).
|
|
|
|
**Confidence:** 1-10. Be honest. An observed pattern you verified in the code is 8-9.
|
|
An inference you're not sure about is 4-5. A user preference they explicitly stated is 10.
|
|
|
|
**files:** Include the specific file paths this learning references. This enables
|
|
staleness detection: if those files are later deleted, the learning can be flagged.
|
|
|
|
**Only log genuine discoveries.** Don't log obvious things. Don't log things the user
|
|
already knows. A good test: would this insight save time in a future session? If yes, log it.`;
|
|
}
|