* chore: wire docs/agents config into AGENTS.md Agent skills section
Add the `## Agent skills` discovery block pointing the engineering
skills at the existing docs/agents/{issue-tracker,triage-labels,domain}.md
files (issue tracker, triage label mapping, single-context domain docs).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: rebrand to GSD Core and restructure docs with Diataxis
Reorganise the root README and docs/ around the Diataxis framework
(tutorials, how-to guides, reference, explanation), add new how-to
guides and schema references (STATE.md / CONTEXT.md / PLAN.md /
planning artifacts), and cross-link the whole set. Update the lone
legacy gsd-build reference to open-gsd; keep internal get-shit-done/
filesystem paths unchanged (directory rename tracked separately in
open-gsd/gsd-core#604). Regenerate the ja-JP, ko-KR, pt-BR and zh-CN
localised trees to mirror the new structure.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
* docs: backfill changeset PR number (#605)
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 <noreply@anthropic.com>
8.1 KiB
Plan-vs-codebase drift guard: defaults and symbol-resolver seam
- Status: Proposed
- Date: 2026-05-29
- Issue: open-gsd/gsd-core#22
Context
The planner regularly cites symbols that do not exist in the codebase — invented decorators, wrong dataclass fields, renamed CLI flags, mismatched signatures. The phenomenon is measured, not anecdotal: the Practical Code Generation hallucination taxonomy (arXiv:2409.20550) reports Dependency Conflicts (11.26%) and API Knowledge Conflicts (20.41%), which together describe exactly this failure. Today the drift is caught only at execution time by the executor (ImportError/AttributeError), at roughly 10–15 min/fix, a dozen per multi-wave phase.
/gsd:plan-review-convergence does not catch it because planner and reviewer both read the same channel (other plan files); the drift originates in that channel. The fix must introduce an out-of-band source of truth: the project's own source code.
GSD ships intel.cjs (api-map.json et al.), but: enabling intel.enabled populates nothing (the gsd-intel-updater LLM agent is never auto-spawned; population is a manual /gsd:map-codebase --query refresh); extraction is regex, JS/CJS/ESM only; intel is stale after 24h with no auto-refresh (intelUpdate() is a stub); useful intel therefore costs recurring LLM-agent token spend.
The feature has two halves with different dependencies: a verification pass that reads live source (needs no intel, works in any language) and a surface-injection step that renders api-map.json into API-SURFACE.md for the planner (needs intel).
Decision
Part 1 — Defaults
- Ship the verification pass on by default, behind a new, additive config key
plan_review.source_grounding(boolean, defaulttrue, opt-out). Surface it as one question in/gsd:new-project(default Y) and as a toggle in/gsd:settings. It stays on permanently with an easy off switch. - The
API-SURFACE.mdinjection stays gated on the existingintel.enabled(default unchanged). It ships in this release but only activates for projects that opted into intel and populated it. Its planner instruction is a hint ("prefer symbols in API-SURFACE.md; it may be incomplete"), never a hard rule. intel.enabledstays opt-in. Flip its default totrueonly once all three hold: (a) deterministic population (tree-sitter/CJS parse, not an LLM agent); (b) auto-refresh on staleness; (c) multi-language coverage. None of these block the drift guard, because the default-on half does not depend on intel.- Only new, additive config keys are introduced. No pre-existing default is changed.
Part 2 — Symbol-resolver seam
The reviewer pass depends on a resolver seam, not a hardcoded tool:
resolve(ref) -> VERIFIED{location, exported, signature?} | MISSING | AMBIGUOUS{candidates} | UNCHECKABLE{reason}
Resolution is three-valued, not boolean. UNCHECKABLE (the adapter cannot analyze this language or symbol kind) never blocks and never falsely blesses; it is recorded as a coverage gap. Only MISSING from a capable adapter is actionable.
Adapters form an authority ladder, selected by plan_review.source_grounding_authority (enum; default grep, auto-upgrades to intel when intel.enabled), with no prompt changes when climbing:
| Rung | Backend | Asserts | This release? |
|---|---|---|---|
| 0 | ripgrep / Read | name present in source | yes (default) |
| 1 | api-map.json (existing intel) | name in parsed export list | yes (when intel on) |
| 2 | tree-sitter | real declaration + kind | deferred (new dep) |
| 3 | LSP workspace/symbol | + resolved definition, signature | deferred (new dep) |
| 4 | SCIP / GitNexus (#3802) | + exported?, signature, references | deferred (new dep) |
Locked sub-decisions:
- Severity.
MISSINGfrom rung 0–1 ->needs-acknowledgement(the plan proceeds if the author confirms the symbol is new/dynamic, logged), not a hard block — because rung 0–1 produce false positives on dynamic dispatch, re-exports, metaprogrammed decorators, and generated code. Hard block (HIGH) is reserved for rung >=3 adapters that can prove absence.AMBIGUOUS-> MEDIUM.UNCHECKABLE-> INFO. - New vs. existing. Plans declare created symbols in an "Artifacts this phase produces" section. The resolver only checks symbols not in that list, so greenfield work is never flagged MISSING.
- Extraction contract. The reviewer enumerates a fixed set of symbol kinds (
@decorators,Class.method,module.function,--cli-flags, file paths, dataclass/struct fields) and must quote the plan line for each, so coverage is auditable. - Signature-drift can only be asserted at rung >=3; rungs 0–1 return
UNCHECKABLEfor signatures (name-drift only this release). - Cadence. Resolve once per unique symbol per convergence cycle (cache within the cycle); run the pass every cycle so cycle-N fixes are re-verified in cycle N+1.
- Coverage reporting.
REVIEWS.mdcarries a "Verification coverage" INFO block listing every UNCHECKABLE/skipped symbol and why, so "the guard ran" can never silently mean "nothing was checked."
Consequences
Positive
- Every project gets drift protection on day one, in any language, with zero setup and zero token cost (live Grep/Read against ground truth).
- The high-authority, always-fresh check is the default; the low-authority, stale-able cache (intel) stays an explicit, paid opt-in.
- One interface, many backends: raising verification authority (grep -> intel -> tree-sitter -> LSP -> SCIP) is a config change, never a workflow rewrite — the durable lever against worsening hallucination.
- Three-valued resolution refuses the RAG trap where missing context is treated as permission to invent, and cannot be poisoned by stale/partial intel.
needs-acknowledgementkeeps the default-on gate tolerable (no blocking valid plans), preserving adoption.- Honors the issue author's own sequencing ("treat the default flip as a separate proposal") and changes no established default.
Negative
- Two config surfaces (
plan_review.source_groundingandintel.enabled) instead of one. - This release's rung 0–1 catch name-drift but not signature-drift; signature coverage waits on a rung >=3 adapter.
- Requires planners to populate the "Artifacts this phase produces" section reliably; a missing list degrades precision (new symbols flagged for acknowledgement).
- The default-on pass adds reviewer tool calls per convergence cycle (bounded by the cadence rule above).
Alternatives considered
- Flip
intel.enabledto default-on and gate the whole feature on it. Rejected: enabling intel populates nothing, so default-on intel makesAPI-SURFACE.mdinject a near-empty surface; combined with a "use only these symbols" instruction it tells the planner the codebase is empty — strictly worse than no surface. Also imposes silent emptiness on non-JS projects, widens the stale-data window, and pulls solo devs toward unrequested agent-token spend. - Ship the feature opt-in (default-off). Rejected: the protection is free in the default (live-source) configuration; opt-in would leave most projects unprotected for no benefit while hallucination worsens.
- Hardcode Grep/Read in the reviewer prompt. Rejected: binds the workflow to one tool, over-claims ("found in text" = "verified"), and forces a prompt rewrite to adopt a better backend later.
- Boolean resolution (verified / not-verified). Rejected: collapses "couldn't check" into "missing," producing false positives on every unsupported language and silently blessing nothing.
- Hard-block on any MISSING (as originally proposed). Rejected for rung 0–1: false positives from dynamic/re-exported/generated symbols would block valid plans and get the default-on guard switched off. Retained only for rung >=3.
References
- Issue: open-gsd/gsd-core#22 (migrated from open-gsd/gsd-core#3813)
- Relates to #3802 (GitNexus first-class code intelligence) — rung 4 backend
- arXiv:2409.20550 — hallucination taxonomy + RAG mitigation (modest gains)
- arXiv:2502.05111 — grammar-constrained decoding (soft vs hard constraints)
- SCIP: https://github.com/sourcegraph/scip · LSP 3.17 spec · tree-sitter.github.io