refactor(sf): rename guidance files TASTE.md→STYLE.md, ANTI-GOALS.md→NON-GOALS.md
More self-explanatory names. No behavioral change — same files, same purpose. - .sf/TASTE.md → .sf/STYLE.md (# Taste → # Style) - .sf/ANTI-GOALS.md → .sf/NON-GOALS.md (# Anti-goals → # Non-goals) - All code references updated: auto-bootstrap-context, system-context, gitignore, milestone-framing-check, scaffold-constants, spec-projections - Section headings injected into agent context updated to match Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
parent
48a01dd764
commit
702ec3fc0e
8 changed files with 31 additions and 31 deletions
|
|
@ -1,4 +1,4 @@
|
|||
# Anti-goals
|
||||
# Non-goals
|
||||
|
||||
- SF must not ship or revive an MCP server package or runtime endpoint. SF may consume external MCP servers as a client, but its own tools remain native SF/pi tools.
|
||||
- Runtime state files under `.sf/` must not become a peer source of truth when SQLite can hold the structured state. JSON, JSONL, and Markdown runtime artifacts are generated evidence, projections, or legacy import inputs.
|
||||
|
|
@ -1,4 +1,4 @@
|
|||
# Taste
|
||||
# Style
|
||||
|
||||
- Prefer runtime adapters over ad hoc file parsing when reading SF state. For example, query solver eval history through `sf-db.js` helpers rather than reading `.sf/evals/**/report.json`.
|
||||
- Make DB-backed tools the pleasant path. If a human-readable file mirrors structured state, prefer a tool that mutates the DB and regenerates the file over hand-editing the projection.
|
||||
|
|
@ -21,9 +21,9 @@ const AUTO_BOOTSTRAP_SF_SPEC_FILES = [
|
|||
".sf/RUNTIME.md",
|
||||
".sf/STATE.md",
|
||||
".sf/PRINCIPLES.md",
|
||||
".sf/TASTE.md",
|
||||
".sf/STYLE.md",
|
||||
".sf/preferences.yaml",
|
||||
".sf/ANTI-GOALS.md",
|
||||
".sf/NON-GOALS.md",
|
||||
".sf/CODEBASE.md",
|
||||
];
|
||||
const AUTO_BOOTSTRAP_ORIENTATION_FILES = [
|
||||
|
|
@ -117,7 +117,7 @@ export function buildAutoBootstrapContext(basePath) {
|
|||
"Use explorer-style subagents or equivalent high-context research passes before planning when the runtime supports them.",
|
||||
"Recommended explorer passes: docs/purpose/vision; source architecture and dependency map; tests/gates/tooling; risks/backlog/eval candidates.",
|
||||
"Merge explorer findings into one repo map with cited file paths before creating milestones.",
|
||||
"Follow harness-engineering principles: keep AGENTS.md short as a table of contents, use docs/ for human exports and git-history reports, capture operational knowledge in .sf/DB-backed state, prefer mechanically enforced architecture/taste rules, and add cleanup/gardening work when repo knowledge is stale.",
|
||||
"Follow harness-engineering principles: keep AGENTS.md short as a table of contents, use docs/ for human exports and git-history reports, capture operational knowledge in .sf/DB-backed state, prefer mechanically enforced architecture/style rules, and add cleanup/gardening work when repo knowledge is stale.",
|
||||
"Optimize for agent legibility: every milestone should improve the next agent's ability to understand, validate, and safely modify the repo.",
|
||||
"Create actionable milestones and slices from the repo's docs and source tree rather than asking the user to restate them.",
|
||||
"",
|
||||
|
|
|
|||
|
|
@ -446,7 +446,7 @@ function loadSelfFeedbackBlock(cwd) {
|
|||
return `\n\n[SELF-FEEDBACK — Recent sf-internal anomalies]\n\n${block}`;
|
||||
}
|
||||
/**
|
||||
* Load tacit knowledge files (.sf/PRINCIPLES.md, .sf/TASTE.md, .sf/ANTI-GOALS.md)
|
||||
* Load tacit knowledge files (.sf/PRINCIPLES.md, .sf/STYLE.md, .sf/NON-GOALS.md)
|
||||
* into a single block injected after the architecture block.
|
||||
*
|
||||
* Each section is capped at 4 KB. Sections are skipped silently when the
|
||||
|
|
@ -475,13 +475,13 @@ export function loadTacitKnowledgeBlock(cwd) {
|
|||
return stripped;
|
||||
}
|
||||
const principles = readSection("PRINCIPLES.md");
|
||||
const taste = readSection("TASTE.md");
|
||||
const antiGoals = readSection("ANTI-GOALS.md");
|
||||
if (!principles && !taste && !antiGoals) return "";
|
||||
const style = readSection("STYLE.md");
|
||||
const nonGoals = readSection("NON-GOALS.md");
|
||||
if (!principles && !style && !nonGoals) return "";
|
||||
const parts = ["[TACIT KNOWLEDGE — read carefully]"];
|
||||
if (principles) parts.push(`\n## Principles\n\n${principles}`);
|
||||
if (taste) parts.push(`\n## Taste\n\n${taste}`);
|
||||
if (antiGoals) parts.push(`\n## Anti-goals\n\n${antiGoals}`);
|
||||
if (style) parts.push(`\n## Style\n\n${style}`);
|
||||
if (nonGoals) parts.push(`\n## Non-goals\n\n${nonGoals}`);
|
||||
return `\n\n${parts.join("\n")}`;
|
||||
}
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -20,7 +20,7 @@ export { SF_RUNTIME_PATTERNS } from "./git-runtime-patterns.js";
|
|||
/**
|
||||
* SF runtime/generated exclusion patterns for repos where .sf/ is a LOCAL DIRECTORY.
|
||||
* Granular so deliberate human-authored guidance such as .sf/PRINCIPLES.md,
|
||||
* .sf/TASTE.md, and .sf/ANTI-GOALS.md can remain trackable.
|
||||
* .sf/STYLE.md, and .sf/NON-GOALS.md can remain trackable.
|
||||
*
|
||||
* NOT used when .sf/ is a symlink — symlinks need the blanket SF_SYMLINK_EXCLUSION_PATTERNS
|
||||
* because git cannot traverse symlinks to match per-file patterns.
|
||||
|
|
|
|||
|
|
@ -3,7 +3,7 @@
|
|||
*
|
||||
* Reviews:
|
||||
* 1. Milestone CONTEXT.md / title against PROJECT.md vision
|
||||
* 2. .sf/ANTI-GOALS.md — does this milestone violate any?
|
||||
* 2. .sf/NON-GOALS.md — does this milestone violate any?
|
||||
* 3. Category error: is this solving the right problem?
|
||||
*
|
||||
* Non-blocking: findings are surfaced as structured annotations.
|
||||
|
|
@ -13,11 +13,11 @@ import { existsSync, readdirSync, readFileSync } from "node:fs";
|
|||
import { join } from "node:path";
|
||||
import { sfRoot } from "./paths.js";
|
||||
/**
|
||||
* Check milestone framing against project vision, anti-goals, and category-error heuristics.
|
||||
* Check milestone framing against project vision, non-goals, and category-error heuristics.
|
||||
*
|
||||
* Reads:
|
||||
* - <basePath>/PROJECT.md (vision)
|
||||
* - <basePath>/.sf/ANTI-GOALS.md
|
||||
* - <basePath>/.sf/NON-GOALS.md
|
||||
* - <basePath>/.sf/milestones/<milestoneId>/<milestoneId>-CONTEXT.md (or CONTEXT.md)
|
||||
*
|
||||
* @param basePath - project root (cwd)
|
||||
|
|
@ -30,8 +30,8 @@ export function checkMilestoneFraming(basePath, milestoneId) {
|
|||
const projectMdPath = join(basePath, "PROJECT.md");
|
||||
const projectMd = safeRead(projectMdPath);
|
||||
const sfDir = sfRoot(basePath);
|
||||
const antiGoalsPath = join(sfDir, "ANTI-GOALS.md");
|
||||
const antiGoalsMd = safeRead(antiGoalsPath);
|
||||
const nonGoalsPath = join(sfDir, "NON-GOALS.md");
|
||||
const nonGoalsMd = safeRead(nonGoalsPath);
|
||||
// Try to find milestone context file
|
||||
const milestonePath = join(sfDir, "milestones", milestoneId);
|
||||
const contextCandidates = [
|
||||
|
|
@ -49,14 +49,14 @@ export function checkMilestoneFraming(basePath, milestoneId) {
|
|||
if (!contextMd) return findings; // nothing to check
|
||||
const contextLower = contextMd.toLowerCase();
|
||||
// ── Anti-goal keyword check ──────────────────────────────────────────────
|
||||
if (antiGoalsMd) {
|
||||
const antiGoalLines = extractBulletLines(antiGoalsMd);
|
||||
for (const line of antiGoalLines) {
|
||||
if (nonGoalsMd) {
|
||||
const nonGoalLines = extractBulletLines(nonGoalsMd);
|
||||
for (const line of nonGoalLines) {
|
||||
const keywords = extractKeywords(line);
|
||||
const matched = keywords.filter((kw) => contextLower.includes(kw));
|
||||
if (matched.length > 0) {
|
||||
findings.push({
|
||||
concern: `Milestone description contains "${matched[0]}" — ANTI-GOALS.md entry: "${line.slice(0, 120)}"`,
|
||||
concern: `Milestone description contains "${matched[0]}" — NON-GOALS.md entry: "${line.slice(0, 120)}"`,
|
||||
source: "anti_goal",
|
||||
severity: "warning",
|
||||
});
|
||||
|
|
@ -144,7 +144,7 @@ function extractBulletLines(text) {
|
|||
}
|
||||
/**
|
||||
* Extract significant lowercase keywords (length >= 5, non-common) from text.
|
||||
* Used for fuzzy matching between milestone context and anti-goals / vision.
|
||||
* Used for fuzzy matching between milestone context and non-goals / vision.
|
||||
*/
|
||||
function extractKeywords(text) {
|
||||
const stopwords = new Set([
|
||||
|
|
|
|||
|
|
@ -434,8 +434,8 @@ Add entries as you make decisions. Each entry: 1-2 sentences. Cite the rationale
|
|||
`,
|
||||
},
|
||||
{
|
||||
path: ".sf/TASTE.md",
|
||||
content: `# Taste
|
||||
path: ".sf/STYLE.md",
|
||||
content: `# Style
|
||||
|
||||
What good code looks like here. Idioms, conventions, "we prefer X over Y" calls.
|
||||
|
||||
|
|
@ -447,10 +447,10 @@ Add entries as you notice patterns worth preserving. Each entry: 1-2 sentences w
|
|||
`,
|
||||
},
|
||||
{
|
||||
path: ".sf/ANTI-GOALS.md",
|
||||
content: `# Anti-goals
|
||||
path: ".sf/NON-GOALS.md",
|
||||
content: `# Non-goals
|
||||
|
||||
What we explicitly DON'T want. Things that look attractive but we've decided against.
|
||||
What this project explicitly does not want. Things that look attractive but have been decided against.
|
||||
|
||||
This is gold — most wrong agent calls come from not knowing what to avoid. Each entry: 1-2 sentences with the rationale.
|
||||
|
||||
|
|
|
|||
|
|
@ -75,8 +75,8 @@ export function buildWorkingModelInputs(basePath) {
|
|||
`- Generator: \`sf-spec-projections@${SPEC_GENERATOR_VERSION}\``,
|
||||
`- Database: \`.sf/sf.db\` (${present(join(sfDir, "sf.db"))})`,
|
||||
`- Guidance: \`.sf/PRINCIPLES.md\` (${present(join(sfDir, "PRINCIPLES.md"))})`,
|
||||
`- Guidance: \`.sf/TASTE.md\` (${present(join(sfDir, "TASTE.md"))})`,
|
||||
`- Guidance: \`.sf/ANTI-GOALS.md\` (${present(join(sfDir, "ANTI-GOALS.md"))})`,
|
||||
`- Guidance: \`.sf/STYLE.md\` (${present(join(sfDir, "STYLE.md"))})`,
|
||||
`- Guidance: \`.sf/NON-GOALS.md\` (${present(join(sfDir, "NON-GOALS.md"))})`,
|
||||
`- Optional knowledge: \`.sf/KNOWLEDGE.md\` (${present(join(sfDir, "KNOWLEDGE.md"))})`,
|
||||
`- Optional preferences: \`.sf/preferences.yaml\` (${present(join(sfDir, "preferences.yaml"))})`,
|
||||
readDbSummary(basePath),
|
||||
|
|
@ -193,7 +193,7 @@ export function generateOperatingModelSpec(basePath) {
|
|||
"",
|
||||
"Markdown under `.sf/` has two roles:",
|
||||
"",
|
||||
"- working guidance and knowledge that the runtime loads, such as `PRINCIPLES.md`, `TASTE.md`, `ANTI-GOALS.md`, `KNOWLEDGE.md`, and `preferences.yaml`;",
|
||||
"- working guidance and knowledge that the runtime loads, such as `PRINCIPLES.md`, `STYLE.md`, `NON-GOALS.md`, `KNOWLEDGE.md`, and `preferences.yaml`;",
|
||||
"- human-readable projections from DB-owned records, such as rendered decisions, requirements, roadmap, plan, summary, and state files.",
|
||||
"",
|
||||
"Markdown under `docs/specs/` is a human export for review, navigation, and git history. Generated docs can change; Git records that human-facing history. If SF needs its own operational history, it should store that in `.sf`/DB-backed state. Plans should record any surface, protocol, output-format, run-control, or permission-profile impact explicitly when a milestone changes integration behavior.",
|
||||
|
|
@ -234,7 +234,7 @@ export function generateOperatingModelSpec(basePath) {
|
|||
"",
|
||||
"- `.sf/sf.db` is the canonical structured runtime state store for initialized SF repos. Treat a missing or unreadable DB as bootstrap/recovery, not a normal alternate source of truth.",
|
||||
"- `.sf/DECISIONS.md`, `.sf/REQUIREMENTS.md`, milestone roadmaps, and similar files are rendered working projections when database-backed tools own the data. They are useful to humans and agents but must not compete with DB rows.",
|
||||
"- `.sf/PRINCIPLES.md`, `.sf/TASTE.md`, `.sf/ANTI-GOALS.md`, `.sf/KNOWLEDGE.md`, and `.sf/preferences.yaml` are repo-local working guidance files when present.",
|
||||
"- `.sf/PRINCIPLES.md`, `.sf/STYLE.md`, `.sf/NON-GOALS.md`, `.sf/KNOWLEDGE.md`, and `.sf/preferences.yaml` are repo-local working guidance files when present.",
|
||||
"- Generated `.sf/` runtime files are evidence, projections, or import/recovery artifacts.",
|
||||
"- Durable human-facing exports belong in `docs/specs/`, `docs/adr/`, or `docs/plans/`. They are reviewable projections and git-history artifacts, not a second planning database.",
|
||||
"",
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue