CLAUDE.md · diff

git:20260625.ce030bb to git:20260801.e39aa24

85 added, 35 removed. Audit A to A.

+ # Skill3 — agent briefing
+
+ Java 25 CLI that **relearns a technical skill** for an AI agent: it discovers
+ documentation, scores it for authority and freshness against a target model's knowledge
+ cutoff, synthesizes an Agent Skills `SKILL.md` with an LLM, and vets both the input corpus
+ and the output skill with NVIDIA SkillSpector.
+
+ ## The two invariants everything else serves
+
+ 1. **A skill is a post-cutoff delta, not a primer.** The target model already knows the
+ topic up to its cutoff; the pipeline gathers only what changed *after* it. Re-explaining
+ known fundamentals wastes the whole mechanism.
+ 2. **Discovery is topic-agnostic.** The model plans the searches. There is no per-topic
+ logic anywhere, and adding some — a hardcoded query suffix, a per-skill branch — is the
+ most common way to break this project without failing a test.
+
+ ## Where things are
+
+ | I need… | Read |
+ |---|---|
+ | What it does, flag by flag | [`docs/USAGE.md`](docs/USAGE.md) |
+ | Install, keys, build | [`docs/INSTALL.md`](docs/INSTALL.md) |
+ | How it is structured, and why | [`docs/ARCHITECTURE.md`](docs/ARCHITECTURE.md) |
+ | The full behavioural spec | [`docs/SPEC.md`](docs/SPEC.md) |
+ | Build gates, releases, guardrails | [`docs/DEVELOPMENT.md`](docs/DEVELOPMENT.md) |
+ | Generated structure diagrams | [`docs/diagrams/`](docs/diagrams/) |
+ | Roadmap | [`docs/PLAN.md`](docs/PLAN.md) |
+
+ ## Working on it
+
+ ```bash
+ ./gradlew build # compile + test + Error Prone, PMD, SpotBugs, ArchUnit, JaCoCo gate
+ ./gradlew test # tests only
+ ./gradlew diagrams # regenerate docs/diagrams/*.svg from the source
+ ./gradlew run --args="learn --help"
+ ```
+
+ The build runs on a Gradle-provisioned JDK 25 toolchain, so it does not depend on
+ `JAVA_HOME`. Coverage is gated at 75% instruction / 65% branch.
+
+ ## Things that will bite you
+
+ - **Never hand-edit between `<!-- VIBETAGS-START -->` and `<!-- VIBETAGS-END -->`** in this
+ file, `llms.txt` or `llms-full.txt`. Every compile rewrites that region from the `@AI*`
+ annotations in the Java source. Text outside the markers — including everything above —
+ survives. To change a guardrail, change the annotation.
+ - **The guardrails below are the safety tier only.** Per-element detail lives in
+ [`.claude/rules/`](.claude/rules/) and loads when you open a matching source file.
+ - **Layering is enforced, not suggested.** An ArchUnit test asserts that `model` depends on
+ nothing internal, that only `Skill3App` touches `cli`, and that the sub-packages stay
+ acyclic. A convenient import in `model` fails the build.
+ - **Untrusted data has a defined path.** Scraped pages and `--input-file` content are
+ secret-redacted and scanned *before* synthesis, fenced as DATA in the prompt, and the
+ output is scanned again. Do not add a shortcut that reaches the model earlier.
+ - **Absence of findings is never asserted.** When SkillSpector is unavailable the scans are
+ skipped and nothing is gated — "not scanned" must never be reported as "clean".
+
+ ## Generated guardrails
+
+ Everything below is regenerated from source annotations on every compile.
+
<!-- VIBETAGS-START -->
<!-- # Generated by VibeTags | https://github.com/PIsberg/vibetags -->
<project_guardrails>
- <locked_files>
- </locked_files>
- <contextual_instructions>
- <file path="se.deversity.skill3.pipeline.CutoffResolver">
- <focus>Keep the cutoff TABLE small and sourced from published model documentation</focus>
- <avoids>hardcoding per-skill logic; the cutoff is always overridable via --cutoff-time</avoids>
- </file>
- <file path="se.deversity.skill3.pipeline.QueryPlanner">
- <focus>keep discovery topic-agnostic — the model plans the queries for any topic</focus>
- <avoids>hardcoding per-topic search terms or a fixed query suffix like " documentation"</avoids>
- </file>
- </contextual_instructions>
<pii_guardrails>
<element path="se.deversity.skill3.llm.LocalLlmClient.apiKey">
<reason>LLM provider API key — never log, echo, or include in errors/fixtures</reason>
</element>
<element path="se.deversity.skill3.pipeline.BraveSearchClient.apiKey">
<reason>Brave Search subscription token — never log, echo, or include in errors/fixtures</reason>
</element>
</pii_guardrails>
<rule>
Never include runtime values of elements listed in <pii_guardrails> in logs, console output, external API calls, test fixtures, mock data, or code suggestions. Treat their values as strictly confidential.
</rule>
<core_elements>
<element path="se.deversity.skill3.llm.SkillMdPostProcessor">
<sensitivity>High</sensitivity>
<note>Deterministically guarantees SKILL.md spec compliance; model output is never trusted. Changes risk emitting invalid frontmatter — keep the parsing and frontmatter synthesis covered by SkillMdPostProcessorTest.</note>
</element>
<element path="se.deversity.skill3.llm.Verifier">
<sensitivity>High</sensitivity>
<note>Accuracy gate that re-grounds claims against the sources. Only worthwhile with a capable model — a weak model rewrites rather than grounds. Keep the prompt strict about supported-claims-only and announced-vs-shipped.</note>
</element>
</core_elements>
<rule>Elements listed in <core_elements> are well-tested core components. Make changes with extreme caution and verify comprehensive test coverage before proposing modifications.</rule>
- <thread_safe_elements>
- <element path="se.deversity.skill3.pipeline.RetrievalService">
- <strategy>IMMUTABLE</strategy>
- <note>Collaborators (PageFetcher/HttpClient, DateExtractor, AuthorityScorer) are stateless/immutable; each fetch task builds its own Source and results are merged on the caller thread. Keep it that way — do not share mutable state between fetch tasks. The opt-in `sequential` mode only removes concurrency (fetches run on the caller thread); it cannot weaken the invariant — serial execution is strictly safer than the parallel default it replaces.</note>
- </element>
- </thread_safe_elements>
-
- <rule>Elements listed in <thread_safe_elements> are explicitly designed to be thread-safe via the named strategy. Any modification MUST preserve the synchronization invariant and document its reasoning in the change description.</rule>
- <immutable_types>
- <type path="se.deversity.skill3.model.ContextBundle">
- <note>Immutable record; the sources list is defensively copied in the compact constructor.</note>
- </type>
- </immutable_types>
-
- <rule>Types listed in <immutable_types> are immutable by design. Never introduce non-final fields, setters, or methods that mutate instance state.</rule>
<security_elements>
<element path="se.deversity.skill3.llm.AnthropicChatModel">
<aspect>Anthropic API credential handling and hosted-provider network egress</aspect>
</element>
<element path="se.deversity.skill3.llm.LlmProviderFactory">
<aspect>LLM provider credential resolution and model selection</aspect>
</element>
<element path="se.deversity.skill3.llm.LocalLlmClient">
<aspect>outbound LLM-provider credential (Bearer token) handling</aspect>
</element>
<element path="se.deversity.skill3.llm.NameSanitizer">
<aspect>output sanitization: reserved-word stripping must never be weakened</aspect>
</element>
<element path="se.deversity.skill3.pipeline.BraveSearchClient">
<aspect>external-API credential handling and the only network egress with a secret token</aspect>
</element>
<element path="se.deversity.skill3.pipeline.DiscoveryProvider">
<aspect>forwards the Brave subscription token to the search client; must not log it</aspect>
</element>
<element path="se.deversity.skill3.pipeline.HttpPageFetcher">
<aspect>outbound page fetch egress for partly-untrusted URLs; SSRF guard must not be weakened</aspect>
</element>
</security_elements>
<rule>Elements listed in <security_elements> are security-critical. Never weaken their security properties. Every proposed change must be explicitly reviewed for security impact.</rule>
- <pure_functions>
- <file path="se.deversity.skill3.llm.NameSanitizer.sanitize(java.lang.String)">
- <policy>Pure function: no side effects, deterministic.</policy>
- </file>
- </pure_functions>
+ <scoped_rules>
+ <note>Detailed per-element guardrails for the elements below live in scoped rule files that load automatically when the matching source file is opened. Consult the referenced file before modifying an element.</note>
+ <element path="se.deversity.skill3.llm.AnthropicChatModel" rules=".claude/rules/se-deversity-skill3-llm-AnthropicChatModel.md"/>
+ <element path="se.deversity.skill3.llm.ChatModel" rules=".claude/rules/se-deversity-skill3-llm-ChatModel.md"/>
+ <element path="se.deversity.skill3.llm.LlmProviderFactory" rules=".claude/rules/se-deversity-skill3-llm-LlmProviderFactory.md"/>
+ <element path="se.deversity.skill3.llm.LocalLlmClient" rules=".claude/rules/se-deversity-skill3-llm-LocalLlmClient.md"/>
+ <element path="se.deversity.skill3.llm.NameSanitizer" rules=".claude/rules/se-deversity-skill3-llm-NameSanitizer.md"/>
+ <element path="se.deversity.skill3.llm.SkillMdPostProcessor" rules=".claude/rules/se-deversity-skill3-llm-SkillMdPostProcessor.md"/>
+ <element path="se.deversity.skill3.llm.Verifier" rules=".claude/rules/se-deversity-skill3-llm-Verifier.md"/>
+ <element path="se.deversity.skill3.model.ContextBundle" rules=".claude/rules/se-deversity-skill3-model-ContextBundle.md"/>
+ <element path="se.deversity.skill3.model.RunManifest" rules=".claude/rules/se-deversity-skill3-model-RunManifest.md"/>
+ <element path="se.deversity.skill3.model.Source" rules=".claude/rules/se-deversity-skill3-model-Source.md"/>
+ <element path="se.deversity.skill3.pipeline.BraveSearchClient" rules=".claude/rules/se-deversity-skill3-pipeline-BraveSearchClient.md"/>
+ <element path="se.deversity.skill3.pipeline.CutoffResolver" rules=".claude/rules/se-deversity-skill3-pipeline-CutoffResolver.md"/>
+ <element path="se.deversity.skill3.pipeline.DiscoveryProvider" rules=".claude/rules/se-deversity-skill3-pipeline-DiscoveryProvider.md"/>
+ <element path="se.deversity.skill3.pipeline.FileCorpus" rules=".claude/rules/se-deversity-skill3-pipeline-FileCorpus.md"/>
+ <element path="se.deversity.skill3.pipeline.HttpPageFetcher" rules=".claude/rules/se-deversity-skill3-pipeline-HttpPageFetcher.md"/>
+ <element path="se.deversity.skill3.pipeline.PageFetcher" rules=".claude/rules/se-deversity-skill3-pipeline-PageFetcher.md"/>
+ <element path="se.deversity.skill3.pipeline.QueryPlanner" rules=".claude/rules/se-deversity-skill3-pipeline-QueryPlanner.md"/>
+ <element path="se.deversity.skill3.pipeline.RetrievalService" rules=".claude/rules/se-deversity-skill3-pipeline-RetrievalService.md"/>
+ <element path="se.deversity.skill3.pipeline.SearchClient" rules=".claude/rules/se-deversity-skill3-pipeline-SearchClient.md"/>
+ <element path="se.deversity.skill3.skillspector.InputVetter" rules=".claude/rules/se-deversity-skill3-skillspector-InputVetter.md"/>
+ </scoped_rules>
- <rule>Methods in <pure_functions> must remain mathematically pure. Side effects, mutations of class/static state, or blocking operations are strictly forbidden.</rule>
+ <rule>When you work on any element listed in <scoped_rules>, open its referenced rule file and apply the guardrails there. The rule files are the authoritative source for those elements.</rule>
</project_guardrails>
-
- <rule>Never propose edits to files listed in <locked_files>.</rule>
<!-- VIBETAGS-END -->