From greenfield
Sanitizes analysis specs by removing implementation details and rewriting from behavioral understanding while preserving provenance metadata. Run after analysis, before implementation.
How this skill is triggered — by the user, by Claude, or both
Slash command
/greenfield:spec-sanitizationThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
The sanitization pass turns raw analysis into specs an implementer can build from.
The sanitization pass turns raw analysis into specs an implementer can build from.
Analysts read source code, binaries, and runtime behavior. Even with good intentions, they leak implementation details:
workspace/raw/source/analysis/chunk-42.md:67)These have no place in the output specs. Your job: READ each spec, UNDERSTAND the behavior, REWRITE without source references, TRANSFORM provenance citations.
You must not copy text from raw specs into output specs. Not sentences, not paragraphs, not sections. Read the raw specs to understand the behavior, then write a fresh output spec from your understanding.
Why? Raw specs have source code identifiers woven into every sentence — minified names (k0, Wq, z1), internal function signatures (Pn(a, b, c)), and numeric implementation constants. Find-and-replace cannot catch them all; many internal identifiers read like plain English (shouldRetryOnTimeout, evict_stale_connections). A paraphrase that preserves the original's structure is still leaking the original's design. The only reliable approach is to never copy the text at all.
Process per file:
[UNCERTAINTY: U-{DOMAIN}-{NNN}] {what the raw spec said, in behavioral terms as best you can} — behavioral purpose could not be determined from available analysis.Never preserve implementation jargon in slightly-reworded form. "validator.Exists() is called" rewritten as "the exists check runs" is still jargon — neither you nor the implementer knows what it means. Either translate it to behavior ("verifies the value exists in the constrained list") or flag it as uncertain.
If you find yourself copying a sentence and then editing out identifiers — STOP. You are doing it wrong. Rewrite the sentence from scratch.
Caveat on the "close the file" step: this is a behavioral instruction, not an enforced mechanism. Closing a file does not evict its content from the agent's context window; the raw text remains readable until the session ends. The Layer 6 second-pass review, which runs in a fresh session with access only to workspace/output/, is the practical check on verbatim leakage. Treat this process as discipline, not guarantee.
For every identifier in a spec, ask: "Would an implementor encounter this exact string?"
DATABASE_URL → YES: it's in official docs, users type it. KEEP.--format → YES: it's in CLI help output. KEEP.plugins → YES: it's a config file key users write. KEEP.Retry-After → YES: it's an HTTP header defined by RFC 9110. KEEP.routeRequest → NO: this is an internal function name the developer chose. A different developer would name it differently. GENERALIZE to "the request routing operation."ff_batch_commit_v2 → NO: this is an internal feature flag. No user ever types this. GENERALIZE to "the batch commit feature gate."q9 → NO: this is a minified identifier with zero semantic content. REMOVE.No set of regex patterns will catch every internal identifier because many look like legitimate English (e.g., shouldRetryOnTimeout, evict_stale_connections). You must read every line and apply the principle above.
Every identifier in a raw spec is either an implementation detail that must be abstracted or an external contract that must be preserved. This distinction is language-agnostic — it applies whether the source is TypeScript, Python, Rust, Go, Java, C++, or anything else.
These are choices the original developers made that a reimplementor would reasonably make differently. Abstract them to behavioral descriptions or remove them entirely.
Internal names — function, method, class, module, and variable names chosen by the original developers:
_drain_write_queue(), RecordParser, should_retry_on_timeoutrouteRequest(), evictStaleConnections, indexBuilderfn decode_packet(), struct ProtocolFrameparseRecord(), validateRecord(), AbstractMessageProcessorInternal architecture — how the codebase is organized into files, modules, packages, or namespaces:
pkg/internal/transport/"Framework-specific patterns — references to libraries, state management, UI frameworks, or runtime internals:
useStore, getState(), @observable, store.get()className=, styled., @Component, #[derive(Template)]Model.objects.filter(), has_many :sessions, @Entity@Inject, container.resolve(), wire.Build()Build and deployment artifacts — paths, chunk IDs, minified identifiers, line numbers:
src/, lib/, pkg/, internal/, dist/, target/, build/auth-handler.ts, record_parser.py, transport.go, Main.javaAb2, q9, a$b, __webpack_require__MOD-023, chunk-NNNNInternal feature gates — feature flag names, experiment keys, and vendor-specific telemetry identifiers:
ff_batch_commit_v2, FF_NEW_INDEX_FORMAT, ff_dark_modeexp_async_cache_v2, exp_beta_usercli_parse_completed, worker_startedCode structure language — descriptions that mirror source code control flow rather than observable behavior:
These are constraints imposed by the outside world that a reimplementor cannot redesign. Preserve them exactly.
User-facing identifiers — anything a user types, reads in docs, or sees in output:
DATABASE_URL, DEBUG, RUST_LOG--format, --workers, -h, --verboseupstreams, log_level, timeout_secondsvscode://, myapp://Wire protocol fields — names that appear in network traffic, file formats, or IPC contracts:
request_id, batch_size, streamAuthorization, 200, 401External system schemas — databases, APIs, CLIs, and file formats the application does not own:
Published constants and standard names — values defined by specifications or public documentation:
For any identifier, ask: "Could the reimplementor reasonably redesign this?"
When the test isn't clear-cut, apply these secondary questions:
--verbose → external contract. If one might write parseArgs and another parse_arguments → implementation detail.Internal identifiers like routeRequest, should_retry_on_timeout, FlushConnectionPool, strict_schema_validation look legitimate because they're descriptive English words in some casing convention. But they are implementation details — an implementor would choose different names for the same concepts. This applies equally to camelCase, snake_case, PascalCase, and kebab-case identifiers.
The test: Does this exact string appear in official documentation, a config file schema, a CLI --help output, or an API response? If not, it's an internal identifier and MUST be generalized, even if it's descriptive.
Examples that STAY (user-facing): upstreams, rate_limit, --workers, DATABASE_URL
Examples that GO (internal): routeRequest → "the request routing operation", should_retry_on_timeout → "the retry-on-timeout check", FlushConnectionPool → "the connection pool flush"
When the target depends on external systems it does not own — shared databases, third-party APIs, CLI tools it shells out to, imposed file formats — the details of those systems are behavioral constraints, NOT implementation details. The contract-extractor should have produced dedicated contract files in workspace/raw/specs/contracts/ for each external system.
Do NOT strip from external system contracts:
External contract files flow to implementation: workspace/raw/specs/contracts/{database,api-*,cli-*,format-*}.md → workspace/output/specs/contracts/.
Concrete before/after examples for the sanitizer to follow.
Before: The Ab2() function is the application entry point
After: The application entry point is invoked on startup
Before: ## Part 3: RecordParser Module
After: ## Session Lifecycle Management
Before: The application consists of 52 modules organized into 8 domains
After: (remove entirely — this is analysis organizational metadata)
Before:
| Module | Depends On |
|--------|-----------|
| RecordParser | IndexBuilder, ConfigLoader |
After: (remove entirely — replaced by behavioral integration requirements in BIR format)
Before: Sends a message on the task-completed IPC channel
After: Notifies listeners when the order status changes via internal messaging
Before: Reads store.activeDocument.content to get the document body
After: Retrieves the current document body from application state
Before: When FF_NEW_INDEX_FORMAT is enabled, the feature activates
After: When the batch commit feature gate is active, the feature activates
Before: Defined in the auth handler source file alongside the token refresh logic
After: The authentication handling component also manages token refresh
Before: The validation logic at line 234 checks for...
After: The validation logic checks for...
Before: Applies the .toolbar__button--active CSS class
After: Applies active styling to the toolbar button
Before: Stores data in the sessions table with columns: id, owner, started_at, state
After: Persists session data including identifier, owner, start time, and state
Exception: If the system is external (documented in contracts/database.md, contracts/api-*.md, contracts/cli-*.md, or contracts/format-*.md), KEEP details as-is — they are external constraints, not implementation choices. This applies to database schemas, API field names, CLI flags, file format fields, etc.
Before: The parseRecord() function calls validateRecord() which calls refreshToken() on failure
After: Authentication validates the current token and refreshes it on failure
After sanitization, every file in workspace/output/ must be reviewed. External contract files (database.md, api-*.md, cli-*.md, format-*.md) are exempt — they document systems the app does not own.
Review each file in workspace/output/specs/ (excluding external contract files) and produce a structured verdict. The review is semantic — you are reading for meaning, not running pattern matches.
For each file, check these categories:
Content contamination:
Ab2, a$b, Qz)src/, lib/, pkg/, internal/, dist/, target/)auth-handler.ts, record_parser.py, transport.go, Main.java)Structural contamination:
Part N: header prefixes from analysis's module organizationMOD-NNN)workspace/raw/)Verdict format per file:
FILE: {path}
VERDICT: PASS | FAIL
ISSUES:
- LINE {N}: {quoted text} — {category}: {why this is contamination}
- LINE {N}: {quoted text} — {category}: {why this is contamination}
A file with zero issues gets VERDICT: PASS and no ISSUES block. A file with any issue gets VERDICT: FAIL with line-level citations.
Hard rule: ALL files in workspace/output/specs/ must receive PASS. Any FAIL verdict blocks the pipeline. Fix the contamination and re-verify until all files pass.
When transforming workspace/raw/specs/modules/ into workspace/output/specs/domains/:
record-parser.md, auth-handler.md)session-lifecycle.md, authentication.md)Part N: prefixes from all headers## Cross-Module Dependencies sections entirely## Module Boundary sections entirely## Inter-Module Interface sections entirelyMOD-NNN identifiersFrom module: fields in Prerequisites sectionsNeeds: session token. From module: IndexBuilder. Failure: returns 401Needs: session token. Failure: returns 401workspace/
├── public/ # Public origin - pass through directly
│ ├── docs/ # doc-researcher output
│ └── ecosystem/ # sdk-analyzer output
├── raw/ # RAW - your input
│ └── specs/
│ ├── modules/ # Module-organized specs (analysis structure)
│ ├── journeys/
│ ├── contracts/
│ │ ├── cli.md # Flows to implementation
│ │ ├── environment.md # Flows to implementation
│ │ ├── configuration.md # Flows to implementation
│ │ ├── inter-module.md # ⛔ Stays in analysis
│ │ └── behavioral-integration.md # Flows to implementation
│ ├── test-vectors/
│ └── validation/
│ └── acceptance-criteria/
└── output/ # Sanitized specs for the implementer
├── public/ # Copied from workspace/public/ directly
├── specs/
│ ├── domains/ # Behavioral specs merged by domain (NOT original modules)
│ ├── journeys/
│ └── contracts/ # External contracts + behavioral integration requirements
├── test-vectors/ # Output test vectors (sibling of specs/)
└── validation/ # Output acceptance criteria (sibling of specs/)
└── acceptance-criteria/
Semantic judgment, not pattern matching.
Pattern-matching tools can catch obvious contamination like Ab2 or src/cli/main, but they miss descriptive internal identifiers ("calls the parser function", "the auth handler validates") and produce false positives on legitimate terms. The sanitizer must READ each spec, UNDERSTAND the behavior, and REWRITE it in pure behavioral terms — applying the implementation-detail-vs-external-contract test to every identifier.
BEFORE (what raw analysis looks like):
### Entry Point: main()
Location: src/cli/main:45
Function calls parseArgs() for arg parsing, then detectMode().
Variable config.recordId = generateRecordId() creates session ID.
<!-- cite: source=source-code, ref=workspace/raw/source/analysis/chunk-0003.md:45, confidence=confirmed, agent=chunk-analyzer -->
AFTER (what spec-sanitizer produces):
### Application Entry
**Trigger:** Command-line invocation
**Behavior:**
1. Parse command-line arguments into structured format
2. Detect execution mode from first positional argument
3. Generate session ID (UUID v4, once per process)
4. Route to mode-specific handler
**Session ID:**
- Format: UUID v4
- Lifecycle: Generated at startup, immutable
- Scope: Process-global singleton
<!-- cite: source=source-code, confidence=confirmed, agent=chunk-analyzer -->
Same information. Zero source references. Provenance preserved (minus raw paths).
During sanitization, provenance citations are transformed based on their source type.
Sources: source=source-code, source=runtime-observation, source=binary-analysis
These contain file paths into workspace/raw/ — implementation details that must not reach the implementer.
Before:
<!-- cite: source=source-code, ref=workspace/raw/source/analysis/chunk-0019.md:77, confidence=confirmed, agent=deep-dive-analyzer, corroborated_by=runtime-observation -->
After:
<!-- cite: source=source-code, confidence=confirmed, agent=deep-dive-analyzer, corroborated_by=runtime-observation -->
Rule: Remove the ref field entirely. Preserve source, confidence, agent, and corroborated_by.
Sources: source=official-docs, source=sdk-analysis, source=public-api
These contain references to public URLs or workspace/public/ paths — no implementation details.
Before:
<!-- cite: source=official-docs, ref=workspace/public/docs/claims/claims-by-topic.md:89, confidence=confirmed, agent=doc-researcher -->
After (unchanged):
<!-- cite: source=official-docs, ref=workspace/public/docs/claims/claims-by-topic.md:89, confidence=confirmed, agent=doc-researcher -->
Rule: Pass through without modification.
workspace/public/ content is NOT raw. It comes from official documentation, SDK analysis, and public APIs. Copy directly to workspace/output/public/ without modification.workspace/raw/specs/ content IS raw. It was produced by agents that read source code, binaries, or runtime internals. Every file must be sanitized before entering workspace/output/specs/.workspace/output/ is the output. The implementer reads ONLY from here.Acceptance criteria and test vectors also need sanitization:
AC-{MODULE}-{NNN}) must survive sanitization intact.workspace/raw/specs/ to relative references within workspace/output/specs/).digraph sanitization_process {
rankdir=TB;
"Start sanitization" [shape=doublecircle];
"Assess contamination level" [shape=box];
"Copy public/ to output/public/" [shape=box];
"Sanitize each spec in raw/specs/" [shape=box];
"Sanitize validation artifacts" [shape=box];
"Run LLM judge verification on output/specs/" [shape=box];
"All files PASS?" [shape=diamond];
"Verify behavioral completeness preserved" [shape=box];
"Sanitization complete" [shape=doublecircle];
"STOP: Contamination found in output" [shape=octagon, style=filled, fillcolor=red, fontcolor=white];
"Start sanitization" -> "Assess contamination level";
"Assess contamination level" -> "Copy public/ to output/public/";
"Copy public/ to output/public/" -> "Sanitize each spec in raw/specs/";
"Sanitize each spec in raw/specs/" -> "Sanitize validation artifacts";
"Sanitize validation artifacts" -> "Run LLM judge verification on output/specs/";
"Run LLM judge verification on output/specs/" -> "All files PASS?";
"All files PASS?" -> "Verify behavioral completeness preserved" [label="yes"];
"All files PASS?" -> "STOP: Contamination found in output" [label="no"];
"Verify behavioral completeness preserved" -> "Sanitization complete";
}
/sanitize workspace/
This:
workspace/raw/specs/ to assess contamination levelworkspace/public/ to workspace/output/public/workspace/output/specs/workspace/output/sanitization-report.mdThe sanitization report lives in output/ — it is an implementer-facing artifact. It must not contain source module names, raw file paths, source-to-domain mappings, or any reference to the original's internal structure. Include only: file inventory (counts, domain names, SPEC ID prefixes), provenance format explanation, SPEC ID coverage, directory structure, and implementer guidance.
Why this matters: the report is read alongside the output specs by the team building the reimplementation. Its job is to help them navigate the output, not to describe what the original looked like. References to the original's module names or architectural decomposition anchor the reimplementation to the original's design — the implementer should be free to choose their own.
workspace/output/; the raw analysis stays in workspace/raw/npx claudepluginhub earchibald/prime-radiant-marketplace --plugin greenfield2plugins reuse this skill
First indexed Jul 8, 2026
Audits sanitized specs for structural leakage, content contamination, and behavioral completeness using three parallel LLM-based reviewers. Run after sanitization, before implementation handoff.
Extracts Allium specifications from existing codebases. Use to distill behavior into specs, reverse-engineer from implementation, generate specs from code, or document codebase behavior in Allium terms.
Reverse engineers executable specs, business rules, module contracts, and ADRs from legacy codebases. Use before evolving undocumented systems.