Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
d4a53e8
Add layout support and associated tests in DocumentsLocator and graph…
fklosowski Jun 29, 2026
ceae8c0
Wired graph resolution into validate_code process and enhance structu…
fklosowski Jun 29, 2026
322068e
Refactor import statements and format writeProjectFile calls for cons…
fklosowski Jun 29, 2026
8ededb4
Add query functions for project structure analysis and enhance refere…
fklosowski Jun 30, 2026
0d3dd4e
Add nearestModules function and levenshtein utility for improved modu…
fklosowski Jun 30, 2026
60233a8
feat(graphql): add extractGraphqlTable function to parse table names …
fklosowski Jun 30, 2026
08aafd8
feat: enhance platformos-graph integration with structural extraction
fklosowski Jul 1, 2026
d09ab82
feat: enhance asset handling in DocumentsLocator and related modules
fklosowski Jul 1, 2026
be8ad1c
Refactor code to replace dependency structure with impact analysis
fklosowski Jul 2, 2026
f8d4549
feat: add unit tests for AppCache to ensure parsed-project reuse and …
fklosowski Jul 2, 2026
6b305d6
feat: implement AppCache for efficient project source reuse across li…
fklosowski Jul 2, 2026
f686fa0
feat: implement incremental graph updates with applyFileChange and en…
fklosowski Jul 2, 2026
ab434c5
feat(graph): implement deserialization for app graph persistence
fklosowski Jul 2, 2026
c32a35a
feat(graph-cache): implement scoped source walk for platformOS roots …
fklosowski Jul 2, 2026
75647ca
feat(impact): enhance runImpact to handle non-trackable files and imp…
fklosowski Jul 2, 2026
2081733
Update backlog
fklosowski Jul 3, 2026
8012ea7
refactor(graph): supervisor review round-2 remediation
fklosowski Jul 7, 2026
e83bb6c
refactor(graph): own edge-source enumeration; supervisor is a pure co…
fklosowski Jul 7, 2026
2aa1863
fix(graph): key partial entry points by URI, not basename
fklosowski Jul 7, 2026
38e3e11
refactor(graph): retire name-based partial keying + dead getModule br…
fklosowski Jul 7, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
---
id: TASK-8.6
title: >-
Translate structured Offense.fix / suggest into validate_code proposed_fixes +
diagnostic.fix (minimal fix passthrough)
status: To Do
assignee: []
created_date: '2026-07-02 15:00'
labels:
- mcp-supervisor
- fixes
- ergonomics
dependencies:
- TASK-8.1
references:
- packages/platformos-mcp-supervisor/src/lint/lint.ts
- packages/platformos-mcp-supervisor/src/result/assemble.ts
- packages/platformos-mcp-supervisor/src/result/types.ts
parent_task_id: TASK-8
priority: high
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
WHY. The check-common engine ALREADY computes structured fixes (`Offense.fix` — `FixDescription` text-edits/inserts — and `Offense.suggest`), but the current supervisor slice DROPS them: `assemble.ts` hardcodes `proposed_fixes: []`, and `lint.ts` `toDiagnostic` maps only check/severity/message/range (its own doc: "the structured `Offense.fix` / `suggest` are intentionally not translated"). So `validate_code` today returns detection but ZERO fixes. This wires the fixes the engine already produces through to the agent — no edit text is ever regenerated.

RELATIONSHIP TO TASK-8.3. TASK-8.3 ("port the v1 rule library … hints, variants, did-you-mean, confidence, fixes") is the BROAD enrichment port. This task is the MINIMAL, independently-shippable slice: just faithfully pass through the structured fixes check-common already emits. It can land before the full rule library; 8.3's hints/variants/confidence layer on top. (Graph-BACKED did-you-mean + cross-file fixes are a separate task under TASK-9 — see the graph-driven autofix task.)

WHAT.
- `lint.ts`: translate `Offense.fix` (FixDescription) → the `AgentFix` shape (`text_edit` | `insert` | `guidance`) on `ValidateCodeDiagnostic.fix`, and `Offense.suggest` → `diagnostic.suggestion`. Preserve check-common's offsets faithfully into the `AgentFix` 0-based `start_index`/`end_index` contract (mind that diagnostics use 1-based line/col but `AgentFix` uses 0-based offsets — the two must not be conflated).
- `assemble.ts`: populate top-level `proposed_fixes: ProposedFix[]` from the per-diagnostic fixes (carrying the originating `check`), instead of `[]`.
- Keep the "supervisor never regenerates edit text from scratch" invariant (types.ts) — translate, don't synthesize.

TESTS. Whole-value result assertions (CLAUDE.md): a check that emits a text-edit fix → the exact `AgentFix` appears on the diagnostic AND in `proposed_fixes` with its `check`; a check with only a `suggest` → `suggestion` set, no machine fix; a check with neither → no fix fields. Offset mapping pinned exactly. No regression to lint/impact/assemble.

Working dir: ~/Work/platformos-tools/platformos-tools.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 lint.ts translates Offense.fix (FixDescription) → AgentFix (text_edit|insert|guidance) on ValidateCodeDiagnostic.fix, and Offense.suggest → diagnostic.suggestion; offsets mapped faithfully (AgentFix 0-based indices, not conflated with 1-based line/col)
- [ ] #2 assemble.ts populates top-level proposed_fixes from the per-diagnostic fixes (each carrying its originating check), replacing the hardcoded []
- [ ] #3 Edit text is never regenerated — only translated from the engine's FixDescription (types.ts invariant preserved)
- [ ] #4 Whole-value tests: text-edit fix appears on diagnostic + proposed_fixes with check; suggest-only → suggestion; neither → no fix fields; exact offset mapping pinned
- [ ] #5 supervisor suite + type-check + format green; no regression to lint/impact/assemble; scope/overlap with TASK-8.3 documented
<!-- AC:END -->
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ title: >-
status: To Do
assignee: []
created_date: '2026-06-23 10:32'
updated_date: '2026-06-25 15:45'
labels: []
dependencies: []
references:
Expand Down Expand Up @@ -49,3 +50,41 @@ This is the tracking epic. See child tasks. Prerequisite for the supervisor's st
- [ ] #4 The LSP consumers (references/dependencies/dead_code) remain correct (re-verified) and changes to shared types are additive
- [ ] #5 platformos-mcp-supervisor consumes this API and contains NO graph/scanner logic of its own
<!-- AC:END -->

## Implementation Notes

<!-- SECTION:NOTES:BEGIN -->
## Supervisor wiring guidance — consuming `extractFileReferences` (2026-06-25)

The per-file dependency primitive now exists in `platformos-graph` (see TASK-9.3 notes). This is how the supervisor (`platformos-mcp-supervisor`, branch `add-pos-supervisor`) should wire it into `validate_code`. WRITE NO GRAPH LOGIC IN THE SUPERVISOR (invariant #5 / this epic's principle) — it only calls the primitive and shapes the output.

### Where it goes (respect the architecture guards)
- Graph resolution does fs I/O → it must NOT live in the pure layers. The `architecture-invariants.spec.ts` guard marks `enrich/` and `result/` as PURE (no fs/process/I/O). So add a NEW I/O-layer sibling to `lint/` — e.g. `src/structure/` — that calls `extractFileReferences`, exactly mirroring how `lint/lint.ts` is the only other I/O seam.
- Do NOT route through `platformos-language-server-common` / `AppGraphManager` — that's a banned dependency (invariant #1, enforced). Use `@platformos/platformos-graph` directly (already a declared dep, currently unused).

### Call shape (matches the existing `runLint` seam)
```ts
import { extractFileReferences, toSourceCode } from '@platformos/platformos-graph';
import { NodeFileSystem } from '@platformos/platformos-check-node'; // supervisor already deps check-node

const rootUri = URI.file(projectDir).toString(); // projectDir from SupervisorContext
const sourceUri = URI.file(absoluteFilePath).toString(); // same abs path runLint builds
const sourceCode = await toSourceCode(sourceUri, content); // parse the BUFFER, not disk
const refs = await extractFileReferences(rootUri, sourceUri, sourceCode, { fs: NodeFileSystem });
```
Key: parse `params.content` (the in-flight buffer), NOT the on-disk file — the file may not exist yet ("validate before writing"). `rootUri`/`projectDir`: reuse check-common `findRoot` for consistency with the CLI if you want pointed-inside-project tolerance.

### Result shaping (pure `result/` layer)
- Map `Reference[]` → a NEW agent-facing `dependencies` field on `ValidateCodeResult` (e.g. `{ kind, target_path, line, column }[]`). Convert `target.uri` → project-relative via check-common `path.relative(target.uri, rootUri)`; convert `source.range` offsets → 1-based line/col with the SAME `+1`/positionAt approach `lint.ts` uses.
- Do NOT overload the existing name-level `structural.renders_used` (that's TASK-9.3's self-structural NAMES, a separate slot). `dependencies` = resolved edges; `structural.*` = declared names.

### What NOT to duplicate (overlap with existing lint)
- The linter ALREADY emits `MissingPartial` for unresolved render targets. So do NOT re-detect missing targets as supervisor diagnostics from the dependency edges — the unique value the graph adds is the CANONICAL RESOLVED URI + the `kind` taxonomy, not re-flagging "missing". If you want an explicit `exists` per edge, `stat` `target.uri` via the same `fs` in the `structure/` layer (don't add `exists` to check-common's `Reference` — too broad, breaks LSP).

### Out of scope for the primitive (future graph work, still TASK-9.2)
- Incoming `references` ("who renders this file") need the on-disk whole-graph build (`buildAppGraph`) + per-root caching (mirror `AppGraphManager`'s cache). That's heavier and project-wide; defer to the project-structure query API (TASK-9.2). At validate-time the file's OWN outgoing deps are the primary signal.

### Verify after wiring
- `architecture-invariants.spec.ts` still green (no LSP dep, pure layers stay pure, `structure/` is the only new I/O seam).
- AC #5 of this epic: supervisor contains zero graph/scanner logic — it just calls `extractFileReferences` and maps the result.
<!-- SECTION:NOTES:END -->
Loading