Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
14 changes: 14 additions & 0 deletions .backlog/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
project_name: "platformos-tools"
default_status: "To Do"
statuses: ["To Do", "In Progress", "Done"]
labels: []
date_format: yyyy-mm-dd
max_column_width: 20
auto_open_browser: true
default_port: 6420
remote_operations: true
auto_commit: false
bypass_git_hooks: false
check_active_branches: true
active_branch_days: 30
task_prefix: "task"
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
---
id: TASK-1
title: Mirror backend FULL_PHYSICAL_PATH regexps in path-utils.ts
status: To Do
assignee: []
created_date: '2026-05-11 12:48'
labels: []
dependencies: []
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Replace the current FILE_TYPE_DIRS + TYPE_MATCHERS in `packages/platformos-common/src/path-utils.ts` with a declarative table that is a 1:1 mirror of the backend `converters_config.rb` regexps. The linter must classify a file iff the backend would dispatch that file to a converter.

**Source of truth:** `desksnearme/app/services/app_builder/services/converters_config.rb` plus per-model `PATH_DIRECTORY` / `EXTENSION` / `PHYSICAL_PATH` constants under `desksnearme/app/models/`. Helpful header is `desksnearme/app/models/concerns/deployable.rb` (lines 20–22) which builds `FULL_PHYSICAL_PATH = \A + DIR_PREFIX + PHYSICAL_PATH`.

**Scope:**
- Drop `marketplace_builder` support entirely (user-approved). Only `app/` and `modules/{name}/{access}/` (plus optional `app/modules/{name}/{access}/`).
- Replace the dir-name-only matchers with full FULL_PHYSICAL_PATH regexps. This catches wrong extensions (e.g. `app/translations/en.json` → undefined) the way the backend does.
- Keep `getFileType`, `getAppPaths`, `getModulePaths` signatures stable; re-derive their data from a single `BACKEND_MODELS` table.
- Mirror backend quirks verbatim with `// backend quirk:` comments: GraphQuery `(graph_queries|graphql)s?` and DIR_PREFIX `modules/(.+)(private|public)/` missing-slash shape.
- Update `RouteTable.extractRelativePagePath` to drop the `marketplace_builder` alternative.

**Files:**
- `packages/platformos-common/src/path-utils.ts`
- `packages/platformos-common/src/route-table/RouteTable.ts` (drop `marketplace_builder` from regex)
- `packages/platformos-common/CLAUDE.md` (update architecture note)
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 BACKEND_MODELS table mirrors every row in converters_config.rb (Page, InstanceView Page/Layout/Partial split, GraphQuery, AuthorizationPolicy, EmailNotification, ApiCallNotification, SmsNotification, DataMigration, FormConfiguration, Translation, TransactableType, CustomModelType, InstanceProfileType, Asset)
- [ ] #2 DIR_PREFIX accepts only: empty | app/ | modules/{name}/{public|private}/ | app/modules/{name}/{public|private}/. marketplace_builder removed everywhere
- [ ] #3 FULL_PHYSICAL_PATH regex includes the extension suffix where the backend enforces it (.liquid for Liquid types, .graphql for GraphQL, .yml for YAML types)
- [ ] #4 Each BACKEND_MODELS row carries a comment pointing to the exact backend model file:line it mirrors
- [ ] #5 RouteTable.extractRelativePagePath drops the marketplace_builder regex alternative
<!-- AC:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
---
id: TASK-2
title: Add scalar-pattern + ActivityStreams file types
status: To Do
assignee: []
created_date: '2026-05-11 13:02'
labels: []
dependencies:
- TASK-1
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Add new `PlatformOSFileType` enum values for files that converters_config.rb matches with scalar/literal patterns rather than per-model FULL_PHYSICAL_PATH, plus the two ActivityStreams subtypes the backend dispatches.

**New enum values:**
- `InstanceConfig` — `^/?(app/)?config\.yml$` (drops marketplace_builder)
- `UserType` — `\A(app/)?user\.yml`
- `AssetManifest` — `\A(app/)?asset_manifest\.json`
- `AssetsManifest` — `\A(app/|modules/(.+)(private|public)/)assets\.json`
- `ModulesLock` — `^/?(?:(?:app/)?pos-modules\.lock\.json|pos-module\.lock\.json)$`
- `ActivityStreamsHandler` — `activity_streams/handlers/(.+)\.yml`
- `ActivityStreamsGroupingHandler` — `activity_streams/grouping_handlers/(.+)\.yml`

**Source:** `converters_config.rb` lines 7–22 (scalar patterns) plus `app/models/activity_streams/handler.rb` and `app/models/activity_streams/grouping_handler.rb`.

**Design notes:**
- ActivityStreams types extend the `BACKEND_MODELS` table from task 1 (they have normal `PHYSICAL_PATH`).
- Scalar patterns live in a small separate `SCALAR_MATCHERS` table. `getFileType` consults `TYPE_MATCHERS` first, then `SCALAR_MATCHERS`.
- Add `ActivityStreamsHandler` and `ActivityStreamsGroupingHandler` to `YAML_FILE_TYPES`.
- Scalar JSON types (`AssetManifest`, `AssetsManifest`, `ModulesLock`) get classified but are NOT in `YAML_FILE_TYPES` — `isSupportedSourceFile` returns false for them (no JSON checks yet).

**Files:**
- `packages/platformos-common/src/path-utils.ts`
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 Seven new PlatformOSFileType enum values added with backend file:line references
- [ ] #2 getFileType correctly classifies: config.yml, app/config.yml, user.yml, app/user.yml, asset_manifest.json, app/asset_manifest.json, app/assets.json, modules/core/public/assets.json, pos-modules.lock.json, pos-module.lock.json, app/pos-modules.lock.json
- [ ] #3 ActivityStreamsHandler and ActivityStreamsGroupingHandler classify under app/, modules/, and app/modules/ roots
- [ ] #4 YAML_FILE_TYPES set updated to include ActivityStreamsHandler and ActivityStreamsGroupingHandler
<!-- AC:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
---
id: TASK-3
title: Rewrite isSupportedSourceFile to be type-driven
status: To Do
assignee: []
created_date: '2026-05-11 13:09'
labels: []
dependencies:
- TASK-2
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Stop dispatching `isSupportedSourceFile` on file extension first. The current implementation in `packages/platformos-common/src/path-utils.ts` (line 273) duplicates type-membership decisions across three branches. After tasks 1+2 land, replace it with a single type-driven decision that defers to `getFileType` and the LIQUID/GRAPHQL/YAML sets.

**Target behavior:**
```ts
export function isSupportedSourceFile(uri: UriString): boolean {
// Asset partials are compiled by the backend but the linter cannot parse
// them as Liquid — they are CSS/JS/SCSS templates.
if (/\.(?:s?css|js)\.liquid$/.test(uri)) return false;
const type = getFileType(uri);
if (type === undefined) return false;
if (LIQUID_FILE_TYPES.has(type)) return uri.endsWith('.liquid');
if (GRAPHQL_FILE_TYPES.has(type)) return true; // extension enforced by FULL_PHYSICAL_PATH
if (YAML_FILE_TYPES.has(type)) return true; // extension enforced by FULL_PHYSICAL_PATH
return false; // Asset, scalar JSON types, etc.
}
```

**Why the `.liquid` gate stays for Liquid types:** backend `PHYSICAL_PATH` for `Page` and `InstanceView` accepts any extension (`(.+)` with no `\.liquid\z` anchor), so the regex alone is not enough to know whether the linter can parse it.

**Why GraphQL/YAML don't need an extension gate:** their FULL_PHYSICAL_PATH already enforces `\.graphql\z` or `\.yml\z`.

**Files:**
- `packages/platformos-common/src/path-utils.ts`

**Optional cleanup:** `isKnownLiquidFile`, `isKnownGraphQLFile`, `isKnownYAMLFile` become thin wrappers over `getFileType` + a type-set lookup. Keep exported for back-compat; mark `isSupportedSourceFile` as preferred.
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 isSupportedSourceFile uses getFileType + type-set membership; no extension-based dispatch except the asset-partial skip and the Page/InstanceView .liquid gate
- [ ] #2 Returns false for Asset, AssetManifest, AssetsManifest, ModulesLock, InstanceConfig, UserType
- [ ] #3 Returns false for app/views/partials/foo.css.liquid (asset partial in known dir)
- [ ] #4 Returns true for app/views/pages/home.liquid and false for app/views/pages/home.txt
<!-- AC:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
---
id: TASK-4
title: Replace inlined extension filters with isSupportedSourceFile
status: To Do
assignee: []
created_date: '2026-05-11 13:10'
labels: []
dependencies:
- TASK-3
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Now that `isSupportedSourceFile` is the single source of truth (task 3), eliminate the two callers that hand-roll a subset of its logic.

**Caller 1 — `packages/platformos-check-node/src/index.ts:144-163`**

Current code inside `getApp().glob().filter(...)`:
```ts
if (filePath.endsWith('.liquid') && !isKnownLiquidFile(filePath)) return false;
if (filePath.endsWith('.graphql') && !isKnownGraphQLFile(filePath)) return false;
if ((filePath.endsWith('.yml') || filePath.endsWith('.yaml')) && !isKnownYAMLFile(filePath)) return false;
return true;
```
Replace with:
```ts
if (!isSupportedSourceFile(filePath)) return false;
return true;
```
Also drop the now-unused `isKnownLiquidFile`, `isKnownGraphQLFile`, `isKnownYAMLFile` imports.

**Caller 2 — `packages/platformos-language-server-common/src/documents/DocumentManager.ts:84-89`**

Current:
```ts
.filter(sourceCode => sourceCode.type !== SourceCodeType.LiquidHtml || isKnownLiquidFile(sourceCode.uri))
```
This only filters Liquid; GraphQL and YAML files outside known dirs slip through. Replace with `isSupportedSourceFile(sourceCode.uri)` so the LSP `app()` view matches what the CLI sees.

**Behavior change to note:** `DocumentManager.app()` will now drop graphql/yaml files that sit outside known dirs. If any LSP test depends on a misplaced fixture being included, update the fixture path.

**Files:**
- `packages/platformos-check-node/src/index.ts`
- `packages/platformos-language-server-common/src/documents/DocumentManager.ts`
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 platformos-check-node/src/index.ts uses isSupportedSourceFile and drops the three isKnown* imports
- [ ] #2 DocumentManager.app() uses isSupportedSourceFile uniformly
- [ ] #3 yarn build succeeds for both packages with no unused-import errors
<!-- AC:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
---
id: TASK-5
title: Update path-utils spec for backend-mirror behavior
status: To Do
assignee: []
created_date: '2026-05-11 13:10'
labels: []
dependencies:
- TASK-3
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
Rewrite `packages/platformos-common/src/path-utils.spec.ts` to assert the new (stricter, backend-exact) semantics.

**Remove:**
- The entire `describe('marketplace_builder/ legacy root', ...)` block.
- All other `marketplace_builder/...` assertions sprinkled through other describes (search for the literal string).

**Add coverage for new behavior:**
- Wrong-extension rejection now that the regex includes the extension suffix:
- `app/translations/en.json` → `getFileType` undefined
- `app/graphql/x.txt` → undefined
- `app/transactable_types/x.yml.bak` → undefined
- `app/authorization_policies/x.txt` → undefined
- Scalar patterns (from task 2):
- `config.yml`, `app/config.yml` → `InstanceConfig`
- `user.yml`, `app/user.yml` → `UserType`
- `app/asset_manifest.json` → `AssetManifest`
- `app/assets.json`, `modules/core/public/assets.json` → `AssetsManifest`
- `pos-modules.lock.json`, `app/pos-modules.lock.json`, `pos-module.lock.json` → `ModulesLock`
- ActivityStreams:
- `app/activity_streams/handlers/x.yml` → `ActivityStreamsHandler`
- `app/activity_streams/grouping_handlers/x.yml` → `ActivityStreamsGroupingHandler`
- Module versions of the same
- Page/InstanceView with non-`.liquid` extension:
- `app/views/pages/home.html` → `getFileType` returns `Page`, `isSupportedSourceFile` returns `false`
- Asset partials in known dirs:
- `app/views/partials/foo.css.liquid` → `isSupportedSourceFile` returns `false`
- `app/views/partials/foo.js.liquid` → `isSupportedSourceFile` returns `false`
- Backend quirks (mirror verbatim, document with comment):
- `app/graphqls/x.graphql` → `GraphQL` (the `s?` quirk)

**Existing spec sections to keep/adjust:**
- `app/ root — *` describes — keep as is
- `module paths` describe — keep
- `app/modules nested paths` describe — keep
- `false positive prevention` describe — keep all cases
- `getAppPaths` / `getModulePaths` describes — keep
- Convenience predicates — drop the `marketplace_builder/...` assertions inside them

**Files:**
- `packages/platformos-common/src/path-utils.spec.ts`
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 All marketplace_builder/ assertions removed
- [ ] #2 Scalar-pattern, ActivityStreams, wrong-extension, asset-partial, and Page-non-liquid cases added
- [ ] #3 yarn workspace @platformos/platformos-common test passes
<!-- AC:END -->
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
---
id: TASK-6
title: Full build + test pass; fix downstream fallout
status: To Do
assignee: []
created_date: '2026-05-11 13:12'
labels: []
dependencies:
- TASK-4
- TASK-5
---

## Description

<!-- SECTION:DESCRIPTION:BEGIN -->
After tasks 1–5 land, run the full monorepo build and test suite, and fix any breakage caused by:

- Stricter regex (extension-anchored) classification
- `marketplace_builder/` removal — any fixture, snapshot, or spec using that root must be migrated to `app/` or deleted
- `DocumentManager.app()` now filtering graphql/yaml strictly — LSP specs may have fixtures in non-canonical dirs

**Commands:**
```bash
NPM_TOKEN=dummy yarn build
NPM_TOKEN=dummy yarn test
yarn type-check
```

**Likely fallout to expect:**
- `platformos-language-server-common/src/**/*.spec.ts` — fixtures may use `marketplace_builder/` paths
- `platformos-check-common/src/**/*.spec.ts` — likewise
- Snapshot mismatches from the URI-classification change

**Files (anticipated, not exhaustive):**
- Anything that grep `marketplace_builder` still finds under `packages/`
- Anything that grep `isKnownLiquidFile\|isKnownGraphQLFile\|isKnownYAMLFile` outside path-utils still finds (might be more callers we missed)
<!-- SECTION:DESCRIPTION:END -->

## Acceptance Criteria
<!-- AC:BEGIN -->
- [ ] #1 yarn build succeeds with zero TypeScript errors
- [ ] #2 yarn test passes (all 239 test files, 1576+ individual tests)
- [ ] #3 yarn type-check passes
- [ ] #4 grep -rn marketplace_builder packages/ --include=*.ts returns no live (non-comment) hits
<!-- AC:END -->
29 changes: 29 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -151,3 +151,32 @@ export SHOPIFY_TLD_ROOT=/path/to/theme-liquid-docs
theme-docs download
code .
```

<!-- BACKLOG.MD MCP GUIDELINES START -->

<CRITICAL_INSTRUCTION>

## BACKLOG WORKFLOW INSTRUCTIONS

This project uses Backlog.md MCP for all task and project management activities.

**CRITICAL GUIDANCE**

- If your client supports MCP resources, read `backlog://workflow/overview` to understand when and how to use Backlog for this project.
- If your client only supports tools or the above request fails, call `backlog.get_backlog_instructions()` to load the tool-oriented overview. Use the `instruction` selector when you need `task-creation`, `task-execution`, or `task-finalization`.

- **First time working here?** Read the overview resource IMMEDIATELY to learn the workflow
- **Already familiar?** You should have the overview cached ("## Backlog.md Overview (MCP)")
- **When to read it**: BEFORE creating tasks, or when you're unsure whether to track work

These guides cover:
- Decision framework for when to create tasks
- Search-first workflow to avoid duplicates
- Links to detailed guides for task creation, execution, and finalization
- MCP tools reference

You MUST read the overview resource to understand the complete workflow. The information is NOT summarized here.

</CRITICAL_INSTRUCTION>

<!-- BACKLOG.MD MCP GUIDELINES END -->
19 changes: 17 additions & 2 deletions packages/platformos-check-common/src/find-root.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,28 @@ import * as path from './path';

type FileExists = (uri: string) => Promise<boolean>;

function isInsideApp(dir: UriString): boolean {
let current = dir;
while (true) {
const parent = path.dirname(current);
if (parent === current) return false;
if (path.basename(parent) === 'app') return true;
current = parent;
}
}

async function isRoot(dir: UriString, fileExists: FileExists) {
return or(
fileExists(path.join(dir, '.pos')),
fileExists(path.join(dir, '.platformos-check.yml')),
fileExists(path.join(dir, 'app')),
// modules/ is a root indicator only when not inside app/ (app/modules/ is a valid subdirectory)
and(fileExists(path.join(dir, 'modules')), Promise.resolve(path.basename(dir) !== 'app')),
// modules/ is a root indicator only when not inside an app/ subtree.
// app/modules/ is a valid subdirectory, and app/views/partials/modules/ is a
// legitimate partial organization that should not be mistaken for a project root.
and(
fileExists(path.join(dir, 'modules')),
Promise.resolve(path.basename(dir) !== 'app' && !isInsideApp(dir)),
),
);
}

Expand Down
2 changes: 2 additions & 0 deletions packages/platformos-check-common/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,11 +60,13 @@ export {
isFormConfiguration,
isKnownGraphQLFile,
isKnownLiquidFile,
isKnownYAMLFile,
isLayout,
isMigration,
isPage,
isPartial,
isSms,
isSupportedSourceFile,
PlatformOSFileType,
} from '@platformos/platformos-common';
export * from './frontmatter';
Expand Down
2 changes: 1 addition & 1 deletion packages/platformos-check-docs-updater/data/latest.json
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@

{"revision":"05ac4637cfd868fd7f72e2a1ebf40fc93428e114"}
{"revision":"30103f74fe1f031dc5aec3a8f0608339bd2b858d"}
Loading
Loading