Feat/unannotated tracking sync guidance#3
Merged
Conversation
- Add annotated_files/unannotated_files to ThreatModel (parser tracks coverage) - CLI: 'guardlink unannotated' standalone command + status --not-annotated flag - CLI: 'guardlink clear' removes annotations with --dry-run and --include-definitions - CLI: 'guardlink sync' standalone command (was only available via MCP/TUI) - TUI: /unannotated, /clear, /sync commands - MCP: guardlink_unannotated, guardlink_clear, guardlink_sync tools - Dashboard: File Coverage bar + unannotated file list on Code & Annotations page - Templates: sync guidance in workflow section for all 7 agent formats - Templates: tightened negative guardrail against over-annotation - Picker: 'All of the above' as last numbered option instead of 'a' shortcut - Auto-sync: status and validate commands auto-sync agent files after parsing
…mplates Removed 108 annotation lines from 16 source files using guardlink clear. Preserved: .guardlink/definitions.ts (17 assets, 20 threats, 15 controls), tests/fixtures/, agents/prompts.ts (example annotations in prompt templates). Previous annotations had systemic issues: - 11 @accepts with zero @Audit pairs (rubber-stamped risk acceptances) - Duplicate acceptances across files for same risk - @accepts where @mitigates was correct (cli/index.ts arbitrary-write) - Missing @exposes for accepted risks (mcp path-traversal) Ready for re-annotation via Claude Code using updated templates that: - Prohibit @accepts (agent writes @Audit + @comment instead) - Require coupled annotation blocks - Include sync guidance in workflow
Shield blocks (@shield:begin/@shield:end) now properly exclude their content from the threat model. Previously, example annotations inside template literal strings in prompts.ts were parsed as real annotations, causing duplicate ID errors and dangling reference warnings. The shield markers themselves are still emitted (for counting), but all content between them is skipped during parsing.
24 source files annotated via Claude Code using updated templates that prohibit @accepts and require coupled annotation blocks. Results: 336 annotations, 64 exposures (46 mitigated, 15 open with @Audit), 70 flows, 14 boundaries, 21 audits, 0 acceptances in source. Agent instruction files auto-synced with current threat model context.
…ted exposures New 'review' command across all surfaces: CLI: guardlink review [--list] [--severity critical,high] TUI: /review [severity] MCP: guardlink_review_list + guardlink_review_accept Users walk through unmitigated exposures sorted by severity and choose: accept — writes @accepts + @Audit with timestamped governance trail remediate — writes @Audit with planned-fix note skip — leaves open for next review Core module: src/review/index.ts - getReviewableExposures() — filters test fixtures, sorts by severity - applyReviewAction() — inserts annotations after coupled block - detectCommentStyle() — matches JSDoc, //, #, -- comment styles - Mandatory justification for accept and remediate (no rubber-stamping) - Auto-syncs agent files after annotations written MCP tools enforce human-in-the-loop: tool descriptions explicitly state acceptance decisions require human confirmation before calling.
- package.json / package-lock.json → 1.3.0 - MCP server version → 1.3.0 - CHANGELOG.md: full v1.3.0 entry (review, clear, sync, unannotated, shield fix) - README.md: added review/clear/sync/unannotated to command table, updated annotation count - GUARDLINK_REFERENCE.md: added Governance & Maintenance section - Agent instruction files synced with updated threat model
Animesh-Sri-bugb
added a commit
that referenced
this pull request
May 12, 2026
Three bugs in src/mcp/lookup.ts surfaced during v1.5.0 testing where two queries against the same identifier disagreed about whether it existed: - `asset #login` returned 0 results when #login was referenced via @confirmed but never declared in definitions.ts, while `threats for #login` and `confirmed` both returned the joined record. (Bug 1) - Bare `#login-sqli` returned no_match when the identifier appeared only as an asset/threat ref in exposures or confirmed and was never declared, even though `unmitigated` happily returned it. (Bug 2) - The no_match hint contained literal double quotes that got escaped twice through MCP (content wrap + JSON-RPC envelope), rendering as \\\" in clients that print the raw response. (Bug 3) Root cause for 1 and 2: the resolver only knew about declared entities (model.assets/threats/controls). Now both lookupAsset and lookupFuzzy fall back to the annotation graph (exposures, confirmed, mitigations, acceptances, audits, flows, boundaries) and synthesize stub records marked `declared: false` with a `referenced_in: [...]` audit trail. Consumers can distinguish synthesized stubs from real declarations. Bug 3 fix: the hint now uses backticks around examples instead of double quotes, so it survives both JSON.stringify passes intact. Adds tests/lookup.test.ts (14 tests, previously zero coverage on this 500-line module). Includes regression guards for the working queries (unmitigated, confirmed, features, threats for) and failing-then-fixed cases for all three bugs. Fixes punch-list bugs #1, #2, #3. Closes test-coverage gap #16.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
Adds governance workflow capabilities to GuardLink, headlined by
guardlink review— an interactive command for walking through unmitigated exposures and recording formal risk decisions (accept, remediate, or skip) with mandatory justification and timestamped audit trails. Also addsclear,sync,unannotatedcommands, fixes the parser's shield block handling, and re-annotates the entire codebase with improved annotation quality (229 → 336 annotations, zero@acceptsin source).New commands across all three surfaces (CLI, TUI, MCP):
guardlink reviewguardlink clear--dry-runsupported)guardlink syncguardlink unannotatedKey design decisions:
@acceptsis write-gated behindguardlink review— agents are prohibited from writing it, enforcing human-in-the-loop governanceguardlink_review_list+guardlink_review_acceptwith tool descriptions explicitly requiring human confirmationBug fix: Parser now respects
@shield:begin/@shield:endblocks, preventing example annotations in template strings from being parsed as real annotations (was causing duplicate ID errors and dangling reference warnings).Type
Checklist
npm run buildpassesnpm testpassesguardlink validate .passes (if annotations changed)Spec changes
ThreatModel schema additions (backward-compatible):
annotated_files: string[]— list of source files containing at least one annotationunannotated_files: string[]— list of source files with no annotationsThese are additive fields used by the dashboard and
unannotatedcommand. No changes to annotation syntax or existing schema fields.