Skip to content

Profile v3: enterprise kinds, shared OOP prompt, secrets rule, linear fact counters - #269

Open
bernardladenthin wants to merge 18 commits into
mainfrom
feat/profiles-v3-kinds-and-safety
Open

bernardladenthin wants to merge 18 commits into
mainfrom
feat/profiles-v3-kinds-and-safety

Conversation

@bernardladenthin

@bernardladenthin bernardladenthin commented Nov 4, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Follow-ups of the four reviews of 2026-11-03, on one branch: linear fact counters and a test that measures it, a secrets rule, regenerateOnProfileChange, fileNames / contentMatches / {a,b} globs / BOM, [include] for facts files.
  • Profile v3 covers the kinds of an enterprise Java code base: one shared prompt file-body-oop for Java, Kotlin, Groovy and Scala (replaces file-body-java), view, contract and ops prompts, and fact counters for every kind it routes (29 new facts files in all).
  • Scripts without an extension are selected and routed by their #! line; fact counters gain [raw], [distinct] and a time limit; the model reads a file's path, not its name alone.
  • Measured on OpenRouter twice: before switching Java, and after the prompt changes (tables below).

Fixes

  • Fact counters quadratic over unclosed markup: xml-facts, pom-facts (100 KB of unclosed <? took 8.4 s; an unclosed DOCTYPE subset ran for minutes), plus three more the new test found: the Java type arguments and field initializer, the shell option arms. Unclosed regions now run to the end of the file.
  • ProfileFactPatternsLinearityTest measures every pattern of every facts file over 110 adversarial lines by counting the characters the matcher reads (4× input ≤ 6× reads), exact on any machine, and asserts that no pattern reaches the new time limit.
  • Secrets: every file prompt names a secret and writes *** for its value. The self-test and the examples skip .env, *.tfvars, *.tfstate, Maven settings.xml, keys, *secret* / *credential* configuration.

Engine

  • regenerateOn (breaking, replaces regenerateOnProfileChange): one switch per cause next to the global force: source (C), prompt (P), facts (N), model (new M: definition key, remoteModel, model file name), children (new K: the child summaries a package or the project reads), each true by default. A missing/empty summary, another H, X, title or aiVersion rewrite always. Packages and the project now record P, M, K too, so a package is rewritten when its files were re-summarized over unchanged sources (was a gap: its C is built from the children's source checksums). Every example YAML shows the switches.
  • fileNames (selection and condition) for files without an extension; contentMatches; {a,b} in every glob (support.AiGlob); BOM / UTF-16 decoding.
  • fileInterpreters (selection) and the interpreters condition: a file without an extension whose #! line names bash, perl, python (or a version) is indexed and routed (support.AiShebang, env options read as env reads them; an unreadable file is skipped with a warning).
  • contentHeadChars (default 16,384, at least 256): how much contentMatches / interpreters read; never past the file's end.
  • Facts files: [include]<TAB><key>; [raw] counts before the [ignore] blanking, [distinct] counts different names; every pattern may read 2,000 characters per character plus 1,000,000, past that its counter is left out with a warning (the profile's patterns read at most ~830).
  • The user message of a file starts with its path below the base directory, so two web.xml of different modules read apart. SourceFileIndexer is final (constructor gained two parameters).

Profile v3

  • file-body-oop for Java, Kotlin, Groovy, Scala — replaces file-body-java (breaking: a rule naming file-body-java must name file-body-oop). It now names private helpers, constants and calls on dependencies too.
  • file-body-view, file-body-contract, file-body-ops (a workflow's name, Gradle scripts); the XML prompt defines EE descriptors, mappings, routes, BPMN, DMN, WSDL.
  • Facts files: kotlin, groovy, scala, java-ee, jee, persistence, mybatis, integration, bpmn, dmn, wsdl, jsp, facelets, openapi, asyncapi, proto, avro, graphql, k8s, actions, dockerfile, compose, ansible, terraform, camel-yaml, gradle, properties, powershell, batch. java-facts now skips comments and strings (TODO/FIXME is [raw]).
  • examples/copilot-api-gateway/srcmorph-luna.yaml routes every kind; pathGlob keeps matching in the case written (decided, documented).

Measurement (OpenRouter)

docs/ai-index-benchmark/remeasure-2026-11-04/ (3 runs each) and, after the prompt changes, remeasure-2026-11-04b/:

gpt-oss-20b GPT-6 Luna
card corpus, file-body-java 99.8 % 99.7 %
card corpus, file-body-oop 99.9 % → 99.5 % (6 runs) 100 % → 100 %
language corpus 98.7 % → 99.0 % (9 runs) 99.8 % → 99.5 %
enterprise corpus (new) 97.9 % → 99.7 % 97.3 % → 99.9 %

The names the first enterprise runs left out (release-backoffice, nextNumber, PREFIX, refund) are named in all six later runs. No summary copied the corpus's password, no fence, no ### heading, no failed file.

Test plan

  • Affected unit / integration tests pass locally: mvn install reactor green (core 2070 tests, CLI 74, plugin 54), SpotBugs clean, PIT core 1877/1877, .github/plugin-it.sh passed locally, benchmark tool tests 48/48, reuse lint compliant
  • A review agent checked the branch; its findings are fixed in the last commit (Terraform references, PowerShell verbs, AsyncAPI addresses, env options, unreadable files, contentHeadChars bounds, docs)
  • CI is green on this branch
  • Docs / CHANGELOG updated where applicable (CHANGELOG Unreleased, profiles/README, plugin, core and CLI README, TODO.md keeps only what is open)

Related issues / PRs

Refs #268

Checklist

  • I have read CONTRIBUTING.md and CODE_OF_CONDUCT.md
  • My commits follow Conventional Commits
  • No security-sensitive changes (the secrets rule reduces exposure; nothing else touches credentials)

🤖 Generated with Claude Code

https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad

bernardladenthin and others added 8 commits November 4, 2026 07:01
…rkup

The [ignore] line of xml-facts scanned to the end of the file again from every comment, CDATA
section, processing instruction or DOCTYPE that is never closed (100 KB of unclosed "<?" took
8.4 s; an unclosed internal subset repeated ran for minutes), the XSD counter from every unclosed
type tag, and pom-facts likewise from every unclosed comment, CDATA section and <plugin>. An
unclosed region now runs to the end of the file, as in sql-facts; a type tag ends at the next "<";
a nested plugin body at the next plugin tag. Well-formed files count as before.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…enerateOnProfileChange)

A file summary's header has recorded the prompt (P) and fact counters (N) it was written with
since 1.4.0, but only a changed source, aiVersion or force rewrote it. shouldWrite now compares P
and N too, unless srcMorph.regenerateOnProfileChange is false, which records them only. The
default is true. The plugin README's header section says what rewrites a summary.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…hree more

ProfileFactPatternsLinearityTest runs every pattern of every facts file of the profile over 55
repeated adversarial lines and counts the characters the matcher reads: four times the input may
read at most six times as many. It is exact on any machine. Against the 1.4.0 files it reports six
quadratic patterns; besides the XML and POM ones fixed before, the Java type arguments and field
initializer read across lines to the end of the file, and the shell dash-option arms backtracked
over blanks. Type arguments are now read within one line, an initializer up to 10,000 characters.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
fileNames (under srcMorph and as a condition leaf) selects and routes files by name, so a
Dockerfile or Jenkinsfile without an extension can be indexed. contentMatches routes by a Java
regular expression found in the first 16,384 characters of a file, so Kubernetes, OpenAPI and
Camel YAML take different rules; planning reads only that start unless it reads the whole file.
Every glob takes {a,b} alternatives (support.AiGlob, shared by excludes, pathGlob and fileNames).
A source file is decoded without its byte order mark, and as UTF-16 when it carries that one.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…secrets rule

Profile v3 gets file-body-oop (one prompt for Java, Kotlin, Groovy, Scala), file-body-view (JSP,
Facelets), file-body-contract (OpenAPI, AsyncAPI, Protobuf, ...) and file-body-ops (CI, Docker,
Kubernetes, Helm, Terraform), and fifteen fact files: kotlin, groovy, java-ee, jee, persistence,
mybatis, integration, bpmn, wsdl, jsp, facelets, openapi, k8s, actions and dockerfile. The XML prompt
defines the EE descriptors, mappings, routes, BPMN and WSDL.

A facts file can include another ([include]<TAB><key>), so jee-facts is xml-facts plus its own
counters and java-ee-facts is java-facts plus the annotations, without a copied line.

Every file prompt names a secret and never copies its value, and the self-test and the examples
skip the files that hold credentials. Four test classes pin every new counter; the linearity test
measures all of them.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
The Luna example routes Java with Jakarta EE imports, Kotlin, Groovy, Scala, the EE descriptors,
JPA/Hibernate and MyBatis mappings, integration routes, BPMN, WSDL, JSP, Facelets, OpenAPI, other
contracts, GitHub Actions, Kubernetes, Dockerfiles and other YAML to their prompt and counters;
ExamplesConfigBindingTest routes one file of every kind. run.py routes the same kinds and takes
--java-prompt, so the Java prompt and the shared OOP prompt can be measured side by side; score.py
counts a summary that copies a secret the facts mark. docs/ai-index-benchmark/corpus-enterprise
holds one or two files per kind with facts-enterprise.json.

SpotBugs: an [include] needs the profile directory, a resolver interface with a generic exception
replaces the unchecked wrapper, and GenerateEngine.run hands its log lines and the indexer to two
methods.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
… Camel YAML in the Luna example

The Luna example indexes .gradle (fallback prompt) and routes Camel's YAML DSL (- route:, - from:)
to the ops prompt. TODO.md lists what is open: the measurement, Java on the shared prompt, push
and pull request, selection by shebang, the kinds without counters, DMN, Gradle and scripts,
pathGlob's case, java-facts without an ignore line, and the review findings not proposed yet.
CHANGELOG and profiles README follow the branch; the README says that blanking leaves a space.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…of 2026-11-04

Three runs each of gpt-oss-20b and GPT-6 Luna on the model-card corpus with file-body-java and with
file-body-oop gave the same Java recall (Luna 100 % in all three with the shared prompt), so Java
moves to file-body-oop everywhere and file-body-java.md goes. The language corpus after the secrets
rule: Luna 99.8 %, gpt-oss 98.7 % (99.1 % before, within the noise of three runs). The enterprise
corpus: 97.9 % and 97.3 %, no summary copied its password. Outputs and metadata under
docs/ai-index-benchmark/remeasure-2026-11-04/; TODO.md keeps what the runs left open.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
@coderabbitai

coderabbitai Bot commented Nov 4, 2026

Copy link
Copy Markdown

Walkthrough

Warning

Review details and warnings were omitted to fit the comment limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
bernardladenthin and others added 5 commits November 4, 2026 12:07
…] and [distinct] counters, a time limit for fact patterns

- fileInterpreters (srcMorph) and the interpreters condition: a file without an extension whose
  #! line names one of the interpreters, or a version of one, is indexed and routed
  (support.AiShebang; the line is read only after the subtree, exclude and size filters).
- contentHeadChars (srcMorph, default 16,384): how much of a file contentMatches and interpreters read.
- [raw] counts the source before the [ignore] lines blank it, [distinct] the different texts a
  pattern matched (its first group where it took part); a summary written before keeps its facts hash.
- Every fact pattern may read 2,000 characters per character of the source plus 1,000,000: past
  that its counter is left out (an ignore counter blanks nothing) and a warning names it.
- SourceFileIndexer is final; AiShebang and AiFactExtractor$ReadBudget are on the PIT gate.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…, java-facts without comments and strings

- New facts files: scala, proto, avro, graphql, asyncapi, terraform (OpenTofu, Terragrunt), ansible,
  compose, camel-yaml, dmn (inputs, outputs, rules, hit policies), gradle, properties, powershell,
  batch. The YAML ones blank a block scalar's body from the indentation of its first line and keep
  its key. ContractFactPatternsTest, OpsFactPatternsTest and JvmFactPatternsTest pin each.
- java-facts: an [ignore] line for comments, Javadoc, text blocks and literals; TODO/FIXME is [raw].
- bpmn-facts counts BPMN only; DMN goes to dmn-facts.
- The Luna example and run.py route the new kinds; java-ee also by an annotation written with its
  package; shell and Perl also by the #! line (fileInterpreters).
- The linearity test repeats 110 adversarial lines and asserts no pattern reaches the time limit.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…b keeps its case; TODO for the measurement

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…ivate helpers, the ops prompt names a workflow and defines Gradle

- The file phase heads the user message with the path below the base directory, so two web.xml of
  different modules read apart (AiPromptSupport: a relative path whole with "/", an absolute one
  by its name); the plan's window check counts the same line. Every file prompt says so.
- file-body-oop: name the private functions, constants and fields the logic uses and each call on
  a dependency. file-body-ops: a CI workflow's or pipeline's name; Gradle build and settings scripts.
- PIT: the time-limit warning is asserted, AiShebang reads its words with \G so no equivalent
  boundary is left, and versions with 0 and 9 are tested.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…tion and the blanking it inherits

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…hat is open

remeasure-2026-11-04b: gpt-oss-20b and GPT-6 Luna on OpenRouter at 51fcd23. Enterprise corpus
99.7 / 99.9 % (97.9 / 97.3 % before), every name the morning's runs left out found in all six runs;
card corpus 99.5 % over six gpt-oss runs and 100 % for Luna; language corpus 99.0 % over nine
gpt-oss runs (the morning's 98.7 % was variation) and 99.5 % for Luna. No failed file, fence,
### heading or leaked secret.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
- terraform-facts: a reference on a line of its own (module.network, data.x.y in depends_on) is no
  block; powershell-facts: Verb-Noun names with an inner capital (ForEach-Object, ConvertTo-Json);
  asyncapi-facts: an address counts only with a value on its line, not a schema property;
  batch-facts: for /r with an unquoted root; camel-yaml's REST counter is labelled as verb blocks;
  scala-facts says how deep its comments nest.
- AiShebang reads env's -u / -C arguments and its -S / --split-string= command line.
- SourceFileIndexer skips an extensionless file it cannot read, with a warning; contentHeadChars is
  at least 256 (a whole #! line) and the head buffer never exceeds the file.
- Docs: run.py's routing, the DMN test, contentHeadChars, the final indexer and its log lines.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
… the project record P, M and K

- regenerateOn under srcMorph (source, prompt, facts, model, children; each true by default)
  replaces regenerateOnProfileChange; force rewrites every summary whatever they say. Each switch
  compares one header field: C, P, N, and the new M (model: definition key, remoteModel, model file
  name) and K (the child summaries a package or the project is written from). A missing or empty
  summary, another H, X, title or aiVersion rewrite always; a field not recorded counts as changed.
- A package is rewritten when a file is re-summarized over an unchanged source (K), and when its own
  prompt or model changed; the project likewise, its checksum now its packages alone.
- AiHeaderHashes (indexer) holds the prompt, model and children hashes; AiMdHeaderSupport takes one
  boolean per field (document may not see config). Every example YAML shows the switches.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
…rectory, M:/Modelle

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01DuAkLtHrCTx81vP5Mt4jad
@sonarqubecloud

sonarqubecloud Bot commented Nov 4, 2026

Copy link
Copy Markdown

This branch was successfully deployed

1 active deployment
startgate — e9be5100 Deployed Nov 4, 2026 by bernardladenthin via Start gate (abort window) #578
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant