Agent-HAnS writes annotations automatically as an AI coding agent builds an app. This page is a manual, line-by-line check of every annotation it wrote in ReferenceManager, to see how well the automation actually held up.
Agent-HAnS uses three kinds of markers, called embedded feature annotations, to record which feature a piece of code belongs to. A folder mapping tags a whole folder. A file mapping tags a whole file, listed in a small companion file named .feature-to-file. A fragment mapping tags a specific block or line of code with a comment such as &begin[Groups] ... &end[Groups], or &line[Groups] for a single line. All of them point at an entry in .feature-model, the tree of every feature in the app.
For every annotation we checked two things. First, does the feature it points at still exist? A merge or a split can remove a feature, leaving old annotations pointing at nothing, we call that stale. Second, assuming the feature does exist, does the annotated code actually implement it? If the code doesn't match, or the annotation covers more than it should, we call that misplaced. Everything else is correct. A begin/end block is also either balanced, meaning its &begin has a matching &end, or not.
.feature-model)ReferenceManager
Database
ApiDocs
Versioning
Papers
CreatePaper
GetPaper
UpdatePaper
DeletePaper
ImportBibtex
ExportBibtex
SearchPapers
Authors
CreateAuthor
GetAuthor
ListAuthors
UpdateAuthor
DeleteAuthor
MergeAuthors
Groups
CreateGroup
GetGroup
UpdateGroup
DeleteGroup
AddPaperToGroup
RemovePaperFromGroup
25 non-root features, 6 top-level. Tags and Collections existed earlier and were merged into Groups at Evolution 5; Authors was promoted from Papers.Authors to top-level at Evolution 8 (split). Note that Authors has a dedicated ListAuthors feature, but Papers and Groups do not have a corresponding ListPapers / ListGroups — this asymmetry turns out to matter below.
Being balanced and pointing at a real feature name is a low bar, every begin/end block clears it, and every referenced feature name exists. But reading the actual code inside each block tells a stricter story: 3 of the 36 blocks (8%) are tagged with a feature their code doesn't actually implement. The two charts below show both counts side by side, the two mapping mechanisms Agent-HAnS uses (whole-file mappings and in-code fragment markers) land at different accuracy.
.feature-to-file) — 20 total| Folder | File | Mapped feature | Verdict | Reason |
|---|---|---|---|---|
Data | AppDbContext.cs | Database | correct | Feature exists, mapping matches file content. |
Data | DbSeeder.cs | Database | correct | Feature exists, mapping matches file content. |
Endpoints | PaperEndpoints.cs | Papers | correct | Feature exists, mapping matches file content. |
Endpoints | GroupEndpoints.cs | Groups | correct | Feature exists, mapping matches file content. |
Migrations | 20260424105228_AddCollections.cs | Collections | stale | Collections no longer exists — merged into Groups at Evolution 5. Dangling reference. |
Migrations | 20260424105228_AddCollections.Designer.cs | Collections | stale | Same as above. |
Migrations | 20260427060630_AddTags.cs | Tags | stale | Tags no longer exists — merged into Groups at Evolution 5. Dangling reference. |
Migrations | 20260427060630_AddTags.Designer.cs | Tags | stale | Same as above. |
Migrations | 20260427075843_MergeTagsAndCollectionsIntoGroups.cs | Groups | correct | Feature exists, this is the merge migration itself. |
Migrations | 20260427075843_MergeTagsAndCollectionsIntoGroups.Designer.cs | Groups | correct | Same as above. |
Models | Paper.cs | Papers | correct | Feature exists, mapping matches file content. |
Models | Author.cs | Papers | stale | Authors was promoted to top-level at Evolution 8; mapping was never updated. The fragment annotation in this same file correctly says &begin[Authors] — the two annotation layers disagree inside one file. |
Models | Affiliation.cs | Papers | stale | Same split-related staleness as Author.cs. |
Models | Group.cs | Groups | correct | Feature exists, mapping matches file content. |
Requests | PaperRequests.cs | Papers | correct | Feature exists, mapping matches file content. |
Requests | GroupRequests.cs | Groups | correct | Feature exists, mapping matches file content. |
Responses | ImportResult.cs | ImportBibtex | correct | Sub-feature of Papers, exists, mapping matches. |
Responses | SearchResponse.cs | SearchPapers | correct | Sub-feature of Papers, exists, mapping matches. |
Services | BibtexParser.cs | ImportBibtex | correct | Sub-feature of Papers, exists, mapping matches. |
Services | BibtexSerializer.cs | ExportBibtex | correct | Sub-feature of Papers, exists, mapping matches. |
Checking that &begin[Groups] names a feature that exists is a name lookup, it does not tell you whether the code between &begin[Groups] and &end[Groups] actually implements Groups. To check that, we read every one of the 36 blocks by hand against the code it wraps. Three resolve to a real feature yet are misplaced, meaning the code inside doesn't match, or over-broad, meaning the block covers more than the one feature it names.
Data/DbSeeder.cs:118–159 tagged Groups, but the code creates Paper objects// &begin[Groups] new Paper { Title = "On Using LLMs to 'Featurize' Software", ... }, new Paper { Title = "An IDE Plugin for Clone Management...", ... }, new Paper { Title = "Visualizing Feature-Oriented Software Evolution", ... }, ... (4 more Paper object literals) // &end[Groups]
No Group entity is constructed anywhere in this block — it is pure Paper seed data. It happens to seed the exact papers that a later, separate block (lines 165–192, correctly tagged Groups) assembles into a demo Group. The annotation likely followed "this data exists in service of the Groups demo" rather than "this code implements the Groups feature." By the paper's own definition, feature annotation should track where a feature's implementation asset lives, not where its test data originates — this block should be untagged or tagged Papers.
Endpoints/GroupEndpoints.cs:13–30 tagged GetGroup, but contains two distinct endpoints// &begin[GetGroup] app.MapGet("/groups", ...) // list all groups — named "ListGroups" .WithName("ListGroups")... app.MapGet("/groups/{id:int}", ...) // get one group — named "GetGroup" .WithName("GetGroup")... // &end[GetGroup]
The block covers both the collection-list endpoint and the single-item endpoint, but only GetGroup exists in the model — there is no ListGroups. The list endpoint's code is absorbed into a tag that names something narrower than what it covers.
Endpoints/PaperEndpoints.cs:14–31 tagged GetPaper, same pattern// &begin[GetPaper] app.MapGet("/papers", ...) // list all papers — named "ListPapers" .WithName("ListPapers")... app.MapGet("/papers/{id:int}", ...) // get one paper — named "GetPaper" .WithName("GetPaper")... // &end[GetPaper]
Identical issue to finding 2. Together these two findings show a systematic gap: 2 of 3 CRUD feature families (Papers, Groups) never got a ListX sibling feature the way Authors did, so the agent folded list-endpoint code into the closest existing tag instead of extending the feature model to match. This is a modeling-granularity miss carried consistently across two evolution steps (Evolution 1 for Papers, Evolution 2 for Collections/original Groups precursor), not a one-off slip.
The remaining 33 of 36 blocks were read in full and match their tags: all Authors/CreateAuthor/GetAuthor/UpdateAuthor/DeleteAuthor/ListAuthors/MergeAuthors blocks in AuthorEndpoints.cs correctly scope to their named operation (this family does distinguish List from Get). The nested MergeAuthors block inside the outer Authors block in the test file is correctly nested. All Program.cs, Requests/, Responses/, and Models/ (except Author.cs's file-mapping conflict, already listed above) blocks match their code exactly. All 14 &line[F] annotations sit on the correct line and name the correct feature.
| File | Line | Kind | Feature | Resolution | Placement |
|---|---|---|---|---|---|
Data/AppDbContext.cs | 9 | line | Groups | exists | correct |
Data/AppDbContext.cs | 10 | line | Authors | exists | correct |
Data/AppDbContext.cs | 14 / 22 | begin/end | Authors | exists | correct |
Data/AppDbContext.cs | 24 / 29 | begin/end | Groups | exists | correct |
Data/DbSeeder.cs | 12 / 51 | begin/end | Authors | exists | correct |
Data/DbSeeder.cs | 118 / 159 | begin/end | Groups | exists | misplaced — creates Papers, not a Group |
Data/DbSeeder.cs | 165 / 192 | begin/end | Groups | exists | correct |
Endpoints/AuthorEndpoints.cs | 12 / 17 | begin/end | ListAuthors | exists | correct |
Endpoints/AuthorEndpoints.cs | 19 / 26 | begin/end | GetAuthor | exists | correct |
Endpoints/AuthorEndpoints.cs | 28 / 43 | begin/end | CreateAuthor | exists | correct |
Endpoints/AuthorEndpoints.cs | 45 / 61 | begin/end | UpdateAuthor | exists | correct |
Endpoints/AuthorEndpoints.cs | 63 / 75 | begin/end | DeleteAuthor | exists | correct |
Endpoints/AuthorEndpoints.cs | 77 / 104 | begin/end | MergeAuthors | exists | correct |
Endpoints/AuthorEndpoints.cs | 107 / 144 | begin/end | Authors | exists | correct |
Endpoints/GroupEndpoints.cs | 13 / 30 | begin/end | GetGroup | exists | over-broad — also covers ListGroups (no such feature) |
Endpoints/GroupEndpoints.cs | 32 / 42 | begin/end | CreateGroup | exists | correct |
Endpoints/GroupEndpoints.cs | 44 / 57 | begin/end | UpdateGroup | exists | correct |
Endpoints/GroupEndpoints.cs | 59 / 71 | begin/end | DeleteGroup | exists | correct |
Endpoints/GroupEndpoints.cs | 73 / 90 | begin/end | AddPaperToGroup | exists | correct |
Endpoints/GroupEndpoints.cs | 92 / 107 | begin/end | RemovePaperFromGroup | exists | correct |
Endpoints/PaperEndpoints.cs | 14 / 31 | begin/end | GetPaper | exists | over-broad — also covers ListPapers (no such feature) |
Endpoints/PaperEndpoints.cs | 33 / 57 | begin/end | CreatePaper | exists | correct |
Endpoints/PaperEndpoints.cs | 59 / 82 | begin/end | UpdatePaper | exists | correct |
Endpoints/PaperEndpoints.cs | 84 / 96 | begin/end | DeletePaper | exists | correct |
Endpoints/PaperEndpoints.cs | 98 / 179 | begin/end | ImportBibtex | exists | correct |
Endpoints/PaperEndpoints.cs | 181 / 238 | begin/end | SearchPapers | exists | correct |
Endpoints/PaperEndpoints.cs | 240 / 256 | begin/end | ExportBibtex | exists | correct |
Models/Author.cs | 3 / 12 | begin/end | Authors | exists | correct (contradicts stale file mapping, see above) |
Models/Paper.cs | 7 | line | Authors | exists | correct |
Models/Paper.cs | 11 | line | ImportBibtex | exists | correct |
Models/Paper.cs | 12 | line | ImportBibtex | exists | correct |
Models/Paper.cs | 13 | line | Groups | exists | correct |
Program.cs | 10 / 14 | begin/end | ApiDocs | exists | correct |
Program.cs | 18 / 21 | begin/end | Database | exists | correct |
Program.cs | 25 | line | ApiDocs | exists | correct |
Program.cs | 27 / 33 | begin/end | ApiDocs | exists | correct |
Program.cs | 37 / 44 | begin/end | Database | exists | correct |
Program.cs | 46 | line | Versioning | exists | correct |
Program.cs | 48 | line | Authors | exists | correct |
Program.cs | 49 | line | Groups | exists | correct |
ReferenceManager.Tests/AuthorEndpointTests.cs | 9 / 229 | begin/end | Authors | exists | correct |
ReferenceManager.Tests/AuthorEndpointTests.cs | 115 / 227 | begin/end | MergeAuthors | exists | correct (correctly nested inside outer Authors block) |
Requests/AuthorRequests.cs | 3 / 6 | begin/end | Authors | exists | correct |
Requests/PaperRequests.cs | 3 / 5 | begin/end | Papers | exists | correct |
Responses/PaperResponse.cs | 5 / 7 | begin/end | Authors | exists | correct |
Responses/PaperResponse.cs | 9 / 11 | begin/end | Groups | exists | correct |
Responses/PaperResponse.cs | 16 | line | Authors | exists | correct |
Responses/PaperResponse.cs | 20 | line | ImportBibtex | exists | correct |
Responses/PaperResponse.cs | 21 | line | ImportBibtex | exists | correct |
Responses/PaperResponse.cs | 22 | line | Groups | exists | correct |
All 25 features in the model are referenced by at least one fragment annotation: Database, ApiDocs, Versioning, Papers, CreatePaper, GetPaper, UpdatePaper, DeletePaper, ImportBibtex, ExportBibtex, SearchPapers, Authors, CreateAuthor, GetAuthor, ListAuthors, UpdateAuthor, DeleteAuthor, MergeAuthors, Groups, CreateGroup, GetGroup, UpdateGroup, DeleteGroup, AddPaperToGroup, RemovePaperFromGroup. 25/25 — full coverage, independent of the placement findings above (coverage counts a feature as "referenced" even where the reference is over-broad, e.g. GetGroup / GetPaper).
Coverage-by-feature is not the same question as coverage-by-file. A feature can be "covered" by one annotated file while a second file that also implements it carries nothing. The section below checks the latter.
So far, every check has started from an annotation that exists and asked whether it's correct. This section asks the reverse question: are there files that clearly implement a feature, yet carry no annotation at all? Of the 41 .cs files in ReferenceManager, 23 carry at least one annotation and 18 carry none. Most of the 18 are legitimately infrastructural, files a human wouldn't annotate either, such as EF-generated *.Designer.cs siblings of already-tagged migrations, the auto-generated AppDbContextModelSnapshot.cs, the test-harness CustomWebApplicationFactory.cs, the three endpoint test suites (discussed separately below), and the generic cross-feature Responses/PagedResult.cs. Two are not, and those two are real gaps.
Test files are excluded from this check by design. Embedded feature annotations track where a feature is implemented, and tests verify a feature rather than implement it, so an unannotated test suite is not a recall failure. The interesting fact about the test suites turns out to run the other way, see below.
Migrations/20260427094357_StandaloneAuthors.cs (+ .Designer.cs) — the Evolution 8 split migration itself, completely untaggedpublic partial class StandaloneAuthors : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.CreateTable(name: "Authors", ...);
migrationBuilder.CreateTable(name: "PaperAuthor", ...);
// ... migrates existing JSON-embedded authors into standalone rows ...
migrationBuilder.DropColumn(name: "Authors", table: "Papers");
}
}
// no &begin[Authors] anywhere in this file
This is the migration that performs the Evolution 8 split promoting Authors to a top-level feature, the same structural change that left Models/Author.cs and Models/Affiliation.cs mapped to the wrong feature (finding above). It is the third distinct annotation failure traceable to that single evolution step, and arguably the most direct one, since this file is the split.
Migrations/20260427081828_AddJournalAndBooktitleToPaper.cs (+ .Designer.cs) — adds the exact columns tagged ImportBibtex elsewhere, but is itself untaggedpublic partial class AddJournalAndBooktitleToPaper : Migration
{
protected override void Up(MigrationBuilder migrationBuilder)
{
migrationBuilder.AddColumn<string>(name: "Booktitle", table: "Papers", ...);
migrationBuilder.AddColumn<string>(name: "Journal", table: "Papers", ...);
}
}
// no &line[ImportBibtex] anywhere in this file
Models/Paper.cs:11-12 tags the Journal and Booktitle properties &line[ImportBibtex], and Responses/PaperResponse.cs:20-21 does the same. The migration that adds these exact two columns to the database has no annotation at all, so the schema-level half of this feature's traceability is missing while the model- and response-level halves are present.
AuthorEndpointTests.cs is the outlier, not PaperEndpointTests.cs / GroupEndpointTests.cs| Test file | Lines | Annotation |
|---|---|---|
AuthorEndpointTests.cs | 229 | &begin[Authors] / &begin[MergeAuthors] — tagged, arguably shouldn't be |
PaperEndpointTests.cs | 118 | none, consistent with test files being out of scope |
GroupEndpointTests.cs | 160 | none, consistent with test files being out of scope |
Given that test files are out of scope for embedded feature annotation, the odd one out is AuthorEndpointTests.cs, which the agent annotated anyway during the Evolution 8 split, not the other two suites lacking annotation. This is an inconsistency in what the agent chose to tag, not a coverage gap, and it is the same asymmetry between Authors and Papers/Groups seen in the ListAuthors vs. missing ListPapers/ListGroups finding above, surfacing here as over-annotation rather than under-annotation.
Migrations/20260424100356_InitialCreate.cs (the original bootstrap migration for Database/ApiDocs/Papers) and Migrations/20260424100754_AddAuthorModel.cs / 20260424101024_AddMultipleAffiliations.cs (pre-split author migrations, together 96 lines) carry no annotation either, but they predate most of the feature model's granularity and are harder to fault under the notation as it existed at the time they were written.