@agentxm/workspace-lint 0.28.4 → 0.28.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,30 +1,15 @@
1
1
  /**
2
- * Catalog barrel aggregates the per-namespace rule catalogs for consumers
3
- * that want the full v1 rule set.
4
- *
5
- * Phase 3a lands `skillRules`; Phase 3b lands `packRules`; Phase 3c will
6
- * append `workspaceRules` without touching existing imports.
7
- *
8
- * `allCatalogRuleIds` is the concatenation used by the rule-id snapshot test
9
- * (`./rule-ids.snapshot.test.ts`). Per-catalog consumers (registry publish,
10
- * `axm lint`) should import the specific catalog they need so the other
11
- * catalogs tree-shake out.
2
+ * Complete executable lint catalog and its observable metadata.
12
3
  *
13
4
  * @experimental This API is unstable and may change without notice.
14
5
  * @packageDocumentation
15
6
  */
7
+ import type { LintCatalogRuleMetadata } from "@agentxm/registry-protocol/unstable/lint/catalog-metadata";
16
8
  export { liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules } from "./workspace.js";
17
9
  export { buildLintWorkspace, buildNativeInstalledSkillInfo, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, type BuildLintWorkspaceArgs, type BuildInstalledPackInfoArgs, type BuildAcquiredInstalledSkillInfoArgs, type BuildInstalledSkillInfoNativeArgs, type LintWorkspace, type LintWorkspaceView, } from "./workspace-read-model/lint-workspace.js";
18
- /**
19
- * Concatenated rule-id array across every currently-exported catalog, in the
20
- * order catalogs were introduced (Phase 3a: `skillRules`; Phase 3b:
21
- * `packRules`; Phase 3c: `workspaceRules`).
22
- *
23
- * Used by the rule-id snapshot test; production callers should not depend on
24
- * this value.
25
- *
26
- * @experimental This API is unstable and may change without notice.
27
- */
10
+ /** Metadata derived from the executable rules and their view catalogs. */
11
+ export declare const allCatalogRuleMetadata: ReadonlyArray<LintCatalogRuleMetadata>;
12
+ /** Every executable lint-rule identity, in catalog/reporting order. */
28
13
  export declare const allCatalogRuleIds: ReadonlyArray<string>;
29
14
  /** Error-severity rule identities that require an exhaustive recovery contract. */
30
15
  export declare const allCatalogErrorRuleIds: ReadonlyArray<string>;
@@ -1,14 +1,5 @@
1
1
  /**
2
- * Catalog barrel aggregates the per-namespace rule catalogs for consumers
3
- * that want the full v1 rule set.
4
- *
5
- * Phase 3a lands `skillRules`; Phase 3b lands `packRules`; Phase 3c will
6
- * append `workspaceRules` without touching existing imports.
7
- *
8
- * `allCatalogRuleIds` is the concatenation used by the rule-id snapshot test
9
- * (`./rule-ids.snapshot.test.ts`). Per-catalog consumers (registry publish,
10
- * `axm lint`) should import the specific catalog they need so the other
11
- * catalogs tree-shake out.
2
+ * Complete executable lint catalog and its observable metadata.
12
3
  *
13
4
  * @experimental This API is unstable and may change without notice.
14
5
  * @packageDocumentation
@@ -21,40 +12,39 @@ import { hookRules } from "@agentxm/registry-protocol/unstable/lint/catalog/hook
21
12
  import { ruleRules } from "@agentxm/registry-protocol/unstable/lint/catalog/rule";
22
13
  import { knowledgeRules } from "@agentxm/registry-protocol/unstable/lint/catalog/knowledge";
23
14
  import { workspaceRules } from "./workspace.js";
15
+ import { repositoryWorkspaceRules } from "./workspace.js";
24
16
  export { liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules } from "./workspace.js";
25
- // Phase 3c workspace read-model builder helpers.
17
+ // Workspace read-model builder helpers.
26
18
  export { buildLintWorkspace, buildNativeInstalledSkillInfo, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, } from "./workspace-read-model/lint-workspace.js";
27
- /**
28
- * Concatenated rule-id array across every currently-exported catalog, in the
29
- * order catalogs were introduced (Phase 3a: `skillRules`; Phase 3b:
30
- * `packRules`; Phase 3c: `workspaceRules`).
31
- *
32
- * Used by the rule-id snapshot test; production callers should not depend on
33
- * this value.
34
- *
35
- * @experimental This API is unstable and may change without notice.
36
- */
37
- export const allCatalogRuleIds = [
38
- ...skillRules.map((r) => r.id),
39
- ...packRules.map((r) => r.id),
40
- ...subagentRules.map((r) => r.id),
41
- ...mcpServerRules.map((r) => r.id),
42
- ...hookRules.map((r) => r.id),
43
- ...ruleRules.map((r) => r.id),
44
- ...knowledgeRules.map((r) => r.id),
45
- ...workspaceRules.map((r) => r.id),
46
- ];
19
+ const repositoryViews = Object.freeze(["workspace", "git-index"]);
20
+ const liveWorkspaceView = Object.freeze(["workspace"]);
21
+ const describeRules = (group, rules, views) => rules.map((rule) => ({
22
+ id: rule.id,
23
+ defaultSeverity: rule.severity,
24
+ group,
25
+ views,
26
+ }));
27
+ const repositoryWorkspaceRuleIds = new Set(repositoryWorkspaceRules.map((rule) => rule.id));
28
+ /** Metadata derived from the executable rules and their view catalogs. */
29
+ export const allCatalogRuleMetadata = Object.freeze([
30
+ ...describeRules("skill", skillRules, repositoryViews),
31
+ ...describeRules("pack", packRules, repositoryViews),
32
+ ...describeRules("subagent", subagentRules, repositoryViews),
33
+ ...describeRules("mcp-server", mcpServerRules, repositoryViews),
34
+ ...describeRules("hook", hookRules, repositoryViews),
35
+ ...describeRules("rule", ruleRules, repositoryViews),
36
+ ...describeRules("knowledge", knowledgeRules, repositoryViews),
37
+ ...workspaceRules.map((rule) => ({
38
+ id: rule.id,
39
+ defaultSeverity: rule.severity,
40
+ group: "workspace",
41
+ views: repositoryWorkspaceRuleIds.has(rule.id) ? repositoryViews : liveWorkspaceView,
42
+ })),
43
+ ]);
44
+ /** Every executable lint-rule identity, in catalog/reporting order. */
45
+ export const allCatalogRuleIds = Object.freeze(allCatalogRuleMetadata.map((entry) => entry.id));
47
46
  /** Error-severity rule identities that require an exhaustive recovery contract. */
48
- export const allCatalogErrorRuleIds = [
49
- ...skillRules,
50
- ...packRules,
51
- ...subagentRules,
52
- ...mcpServerRules,
53
- ...hookRules,
54
- ...ruleRules,
55
- ...knowledgeRules,
56
- ...workspaceRules,
57
- ]
58
- .filter((rule) => rule.severity === "error")
59
- .map((rule) => rule.id);
47
+ export const allCatalogErrorRuleIds = Object.freeze(allCatalogRuleMetadata
48
+ .filter((entry) => entry.defaultSeverity === "error")
49
+ .map((entry) => entry.id));
60
50
  //# sourceMappingURL=index.js.map
@@ -42,11 +42,15 @@ export const agentsDetectedDeclaredRule = {
42
42
  return EMPTY_ADVISORY_FINDINGS;
43
43
  }
44
44
  const detected = yield* scoped.agents.detected;
45
+ const outputs = context.agentOutputs === undefined ? undefined : yield* context.agentOutputs;
45
46
  const findings = [];
46
47
  for (const detection of detected) {
47
48
  if (detection.status !== "unmanaged-present") {
48
49
  continue;
49
50
  }
51
+ if (outputs?.outputs.some((output) => output.ownership === "owned" && output.claimantAgentIds.includes(detection.agentId)) === true) {
52
+ continue;
53
+ }
50
54
  findings.push({
51
55
  kind: "advisory",
52
56
  ruleId: RULE_ID,
@@ -0,0 +1,5 @@
1
+ /** `workspace/agents-projections-stale` — AXM-owned agent output has no desired claimant. */
2
+ import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
+ import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
4
+ export declare const agentsProjectionsStaleRule: AdvisoryRule<WorkspaceRuleContext>;
5
+ //# sourceMappingURL=agents-projections-stale.d.ts.map
@@ -0,0 +1,20 @@
1
+ /** `workspace/agents-projections-stale` — AXM-owned agent output has no desired claimant. */
2
+ import * as Effect from "effect/Effect";
3
+ import { EMPTY_ADVISORY_FINDINGS } from "./helpers/empty.js";
4
+ const RULE_ID = "workspace/agents-projections-stale";
5
+ export const agentsProjectionsStaleRule = {
6
+ id: RULE_ID,
7
+ description: "AXM-owned agent projections have a desired claimant and expected unit name.",
8
+ kind: "advisory",
9
+ severity: "warning",
10
+ check: (context) => context.agentOutputs === undefined
11
+ ? Effect.succeed(EMPTY_ADVISORY_FINDINGS)
12
+ : context.agentOutputs.pipe(Effect.map((inventory) => inventory.ownedResidue.map((output) => ({
13
+ kind: "advisory",
14
+ ruleId: RULE_ID,
15
+ severity: "warning",
16
+ message: `AXM-owned ${output.extensionType} projection '${output.entryName}' is no longer desired.`,
17
+ location: { file: output.path },
18
+ })))),
19
+ };
20
+ //# sourceMappingURL=agents-projections-stale.js.map
@@ -1,4 +1,5 @@
1
1
  import * as Effect from "effect/Effect";
2
+ import * as Option from "effect/Option";
2
3
  import * as Result from "effect/Result";
3
4
  import { formatAxmSkillCompatibilityTarget } from "@agentxm/extension-workspace";
4
5
  import { canonicalDisplayRoot } from "./display-paths.js";
@@ -28,7 +29,9 @@ export const axmSkillCompatibleRule = {
28
29
  },
29
30
  ];
30
31
  }
31
- const compatibility = compatibilityResult.success;
32
+ if (Option.isNone(compatibilityResult.success))
33
+ return EMPTY_ADVISORY_FINDINGS;
34
+ const compatibility = compatibilityResult.success.value;
32
35
  if (compatibility.status === "compatible")
33
36
  return EMPTY_ADVISORY_FINDINGS;
34
37
  const recovery = compatibility.recovery.nextAction;
@@ -0,0 +1,4 @@
1
+ import type { AdvisoryRule } from "@agentxm/registry-protocol/unstable/lint/rule";
2
+ import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
+ export declare const axmSkillDeclaredRule: AdvisoryRule<WorkspaceRuleContext>;
4
+ //# sourceMappingURL=axm-skill-declared.d.ts.map
@@ -0,0 +1,29 @@
1
+ import * as Effect from "effect/Effect";
2
+ import * as Option from "effect/Option";
3
+ import * as Result from "effect/Result";
4
+ import { EMPTY_ADVISORY_FINDINGS } from "./helpers/empty.js";
5
+ const RULE_ID = "workspace/axm-skill-declared";
6
+ export const axmSkillDeclaredRule = {
7
+ id: RULE_ID,
8
+ description: "The workspace declares the official AXM skill.",
9
+ kind: "advisory",
10
+ severity: "info",
11
+ check: (context) => Effect.gen(function* () {
12
+ if (context.axmSkillCompatibility === undefined)
13
+ return EMPTY_ADVISORY_FINDINGS;
14
+ const compatibilityResult = yield* Effect.result(context.axmSkillCompatibility);
15
+ if (Result.isFailure(compatibilityResult) || Option.isSome(compatibilityResult.success)) {
16
+ return EMPTY_ADVISORY_FINDINGS;
17
+ }
18
+ return [
19
+ {
20
+ kind: "advisory",
21
+ ruleId: RULE_ID,
22
+ severity: "info",
23
+ message: "This workspace does not declare the official AXM skill. Install it with `axm skills install @agentxm/skills/axm --bundled`.",
24
+ location: { file: "axm.json" },
25
+ },
26
+ ];
27
+ }),
28
+ };
29
+ //# sourceMappingURL=axm-skill-declared.js.map
@@ -2,6 +2,7 @@ import * as Effect from "effect/Effect";
2
2
  import * as Option from "effect/Option";
3
3
  import * as Result from "effect/Result";
4
4
  import { inferInlineRemoteTransport, resolveSharedMcpTarget, groupConfiguredMcpTargets, } from "@agentxm/extension-workspace";
5
+ import { isMcpServerApplicableToAgent } from "@agentxm/workspace-state";
5
6
  import { settingsDisplayPath } from "./display-paths.js";
6
7
  const RULE_ID = "workspace/mcps-shared-target-compatible";
7
8
  const transportFor = (entry) => {
@@ -45,6 +46,19 @@ export const mcpServerSharedTargetCompatibleRule = {
45
46
  if (transport === undefined)
46
47
  return [];
47
48
  return groups.flatMap((group) => {
49
+ const applicableAgentIds = group.members
50
+ .filter((member) => isMcpServerApplicableToAgent(entry, member.agentId))
51
+ .map((member) => member.agentId);
52
+ if (applicableAgentIds.length === 0)
53
+ return [];
54
+ if (applicableAgentIds.length < group.members.length) {
55
+ const untargetedAgentIds = group.members
56
+ .filter((member) => !isMcpServerApplicableToAgent(entry, member.agentId))
57
+ .map((member) => member.agentId);
58
+ return [
59
+ findingFor(serverName, group.path, `MCP target policy cannot be represented; targeted agents ${applicableAgentIds.join(", ")} share it with untargeted agents ${untargetedAgentIds.join(", ")}.`, settingsDisplayPath(context.subject.scope)),
60
+ ];
61
+ }
48
62
  const resolution = resolveSharedMcpTarget({ members: group.members, transport });
49
63
  return resolution._tag === "conflict"
50
64
  ? [
@@ -29,10 +29,15 @@ export const packsDependenciesResolvedRule = {
29
29
  };
30
30
  const findings = [];
31
31
  for (const node of graphResult.success.nodes) {
32
+ const hasAcceptedResolution = node.type === "mcp-server"
33
+ ? lockedNames["mcp-server"].has(node.identity)
34
+ : node.type === "pack"
35
+ ? false
36
+ : lockedNames[node.type].has(node.name);
32
37
  if (node.type === "pack" ||
33
38
  node.identity.startsWith("workspace:") ||
34
39
  !node.origins.some((origin) => origin.type === "pack") ||
35
- lockedNames[node.type].has(node.name)) {
40
+ hasAcceptedResolution) {
36
41
  continue;
37
42
  }
38
43
  findings.push({
@@ -0,0 +1,10 @@
1
+ import { type ProjectionInvariantFact } from "@agentxm/extension-workspace";
2
+ import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
+ import type { AdvisoryRule, LintFinding } from "@agentxm/registry-protocol/unstable/lint/rule";
4
+ /**
5
+ * Formatting and body edits are not lint concerns for generated documents.
6
+ * Report only ownership proof that AXM cannot safely interpret or replace.
7
+ */
8
+ export declare const projectionOwnershipValidRule: AdvisoryRule<WorkspaceRuleContext>;
9
+ export declare const findingsForProjectionOwnership: (facts: ReadonlyArray<ProjectionInvariantFact>) => ReadonlyArray<LintFinding>;
10
+ //# sourceMappingURL=projection-ownership-valid.d.ts.map
@@ -0,0 +1,27 @@
1
+ import * as Effect from "effect/Effect";
2
+ import { projectionFactHasInvalidOwnership, } from "@agentxm/extension-workspace";
3
+ import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
4
+ const RULE_ID = "workspace/projection-ownership-valid";
5
+ /**
6
+ * Formatting and body edits are not lint concerns for generated documents.
7
+ * Report only ownership proof that AXM cannot safely interpret or replace.
8
+ */
9
+ export const projectionOwnershipValidRule = {
10
+ id: RULE_ID,
11
+ description: "AXM-managed projections carry valid, supported ownership proof.",
12
+ kind: "advisory",
13
+ severity: "error",
14
+ check: (context) => Effect.gen(function* () {
15
+ if (context.projections === undefined)
16
+ return EMPTY_LINT_FINDINGS;
17
+ return findingsForProjectionOwnership(yield* context.projections.facts);
18
+ }),
19
+ };
20
+ export const findingsForProjectionOwnership = (facts) => facts.filter(projectionFactHasInvalidOwnership).map((fact) => ({
21
+ kind: "advisory",
22
+ ruleId: RULE_ID,
23
+ severity: "error",
24
+ message: fact.observation.message ??
25
+ `The AXM ownership proof at ${fact.subject.path} is invalid or unsupported.`,
26
+ }));
27
+ //# sourceMappingURL=projection-ownership-valid.js.map
@@ -10,8 +10,8 @@ const lockedEntries = (lockfile) => [
10
10
  extension: `skill:${name}`,
11
11
  entry,
12
12
  })),
13
- ...Object.entries(lockfile.mcpServers ?? {}).map(([name, entry]) => ({
14
- extension: `mcp-server:${name}`,
13
+ ...Object.values(lockfile.mcpServers ?? {}).map((entry) => ({
14
+ extension: `mcp-server:${entry.workspaceName}`,
15
15
  entry,
16
16
  })),
17
17
  ...Object.entries(lockfile.subagents ?? {}).map(([name, entry]) => ({
@@ -1,38 +1,10 @@
1
1
  /**
2
- * `workspace/*` rule catalog — the v1 rule set.
2
+ * Executable `workspace/*` rule catalog.
3
3
  *
4
- * Per the lint design, `axm lint` (locally only
5
- * never publish) runs exactly these rules against each workspace read model.
6
- * Rules are grouped by classification invariant foundation first, then
7
- * one group per invariant that install-family rules enforce:
8
- *
9
- * | ID | Severity | Autofix |
10
- * | --------------------------------------- | -------- | ----------- |
11
- * | `workspace/initialized` | error | — |
12
- * | `workspace/settings-schema-valid` | error | — |
13
- * | `workspace/settings-keys-recognized` | error | — |
14
- * | `workspace/lockfile-valid` | error | autofixing |
15
- * | `workspace/desired-state-reconcilable` | error | — |
16
- * | `workspace/agents-recognized` | error | — |
17
- * | `workspace/agents-detected-declared` | warning | — |
18
- * | `workspace/skills-declarations-valid` | error | — |
19
- * | `workspace/packs-declarations-valid` | error | — |
20
- * | `workspace/knowledge-state-valid` | error | — |
21
- * | `workspace/skills-lockfile-aligned` | error | autofixing |
22
- * | `workspace/skills-integrity-valid` | error | autofixing |
23
- * | `workspace/skills-artifacts-correct` | error | autofixing |
24
- * | `workspace/skills-managed` | error | — |
25
- * | `workspace/packs-dependencies-resolved` | error | — |
26
- * | `workspace/recommended-packs-retained` | warning | — |
27
- *
28
- * Autofix rides per-extension Operations only (§6). No `editFile`,
29
- * `writeFile`, or `syncWorkspace()` reference — arms whose remediation can't
30
- * be expressed in the operation vocabulary ship as `AdvisoryFinding` with a
31
- * CLI suggestion instead.
32
- *
33
- * Rule ids are **registered with the lint config allowlist at module-load
34
- * time**, so importing this catalog extends the set of accepted
35
- * `axm.json` `lint.rules` keys.
4
+ * Rules are ordered for deterministic reporting and partitioned positively by
5
+ * the evidence available in repository and live-workspace views. Identity,
6
+ * default severity, and view membership are specified in the static catalog
7
+ * metadata and checked against these executable values.
36
8
  *
37
9
  * @experimental This API is unstable and may change without notice.
38
10
  * @packageDocumentation
@@ -40,13 +12,9 @@
40
12
  import type { LintRule } from "@agentxm/registry-protocol/unstable/lint/rule";
41
13
  import type { WorkspaceRuleContext } from "../workspace-context.js";
42
14
  /**
43
- * Ordered v1 `workspace/*` rule catalog. Declaration order is the evaluation
44
- * order within a single `evaluateContexts` call (deterministic ordering is
45
- * test-observable; see `evaluate.ts`). The catalog groups foundation rules
46
- * first, then install-family rules by classification invariant: declaration
47
- * validity, lockfile alignment, integrity, artifact correctness, the
48
- * unmanaged-class-empty check, pack dependency resolution, and implicit
49
- * retention.
15
+ * Ordered repository-safe `workspace/*` rule catalog. Declaration order is
16
+ * the evaluation order within a single `evaluateContexts` call. It groups
17
+ * foundation rules first, then install-family rules by invariant.
50
18
  *
51
19
  * @experimental This API is unstable and may change without notice.
52
20
  */
@@ -1,54 +1,26 @@
1
1
  /**
2
- * `workspace/*` rule catalog — the v1 rule set.
2
+ * Executable `workspace/*` rule catalog.
3
3
  *
4
- * Per the lint design, `axm lint` (locally only
5
- * never publish) runs exactly these rules against each workspace read model.
6
- * Rules are grouped by classification invariant foundation first, then
7
- * one group per invariant that install-family rules enforce:
8
- *
9
- * | ID | Severity | Autofix |
10
- * | --------------------------------------- | -------- | ----------- |
11
- * | `workspace/initialized` | error | — |
12
- * | `workspace/settings-schema-valid` | error | — |
13
- * | `workspace/settings-keys-recognized` | error | — |
14
- * | `workspace/lockfile-valid` | error | autofixing |
15
- * | `workspace/desired-state-reconcilable` | error | — |
16
- * | `workspace/agents-recognized` | error | — |
17
- * | `workspace/agents-detected-declared` | warning | — |
18
- * | `workspace/skills-declarations-valid` | error | — |
19
- * | `workspace/packs-declarations-valid` | error | — |
20
- * | `workspace/knowledge-state-valid` | error | — |
21
- * | `workspace/skills-lockfile-aligned` | error | autofixing |
22
- * | `workspace/skills-integrity-valid` | error | autofixing |
23
- * | `workspace/skills-artifacts-correct` | error | autofixing |
24
- * | `workspace/skills-managed` | error | — |
25
- * | `workspace/packs-dependencies-resolved` | error | — |
26
- * | `workspace/recommended-packs-retained` | warning | — |
27
- *
28
- * Autofix rides per-extension Operations only (§6). No `editFile`,
29
- * `writeFile`, or `syncWorkspace()` reference — arms whose remediation can't
30
- * be expressed in the operation vocabulary ship as `AdvisoryFinding` with a
31
- * CLI suggestion instead.
32
- *
33
- * Rule ids are **registered with the lint config allowlist at module-load
34
- * time**, so importing this catalog extends the set of accepted
35
- * `axm.json` `lint.rules` keys.
4
+ * Rules are ordered for deterministic reporting and partitioned positively by
5
+ * the evidence available in repository and live-workspace views. Identity,
6
+ * default severity, and view membership are specified in the static catalog
7
+ * metadata and checked against these executable values.
36
8
  *
37
9
  * @experimental This API is unstable and may change without notice.
38
10
  * @packageDocumentation
39
11
  */
40
- import { registerLintRuleIds } from "@agentxm/registry-protocol/unstable/lint/config";
41
12
  import { initializedRule } from "./workspace/initialized.js";
42
13
  import { settingsSchemaValidRule } from "./workspace/settings-schema-valid.js";
43
14
  import { settingsKeysRecognizedRule } from "./workspace/settings-keys-recognized.js";
44
15
  import { lockfileValidRule } from "./workspace/lockfile-valid.js";
45
16
  import { agentsRecognizedRule } from "./workspace/agents-recognized.js";
46
17
  import { agentsDetectedDeclaredRule } from "./workspace/agents-detected-declared.js";
18
+ import { agentsProjectionsStaleRule } from "./workspace/agents-projections-stale.js";
47
19
  import { instructionsSourcePresentRule } from "./workspace/instructions-source-present.js";
48
20
  import { instructionsTargetCurrentRule } from "./workspace/instructions-target-current.js";
49
21
  import { instructionsTargetUnownedRule } from "./workspace/instructions-target-unowned.js";
50
22
  import { instructionsTargetStaleRule } from "./workspace/instructions-target-stale.js";
51
- import { projectionsCurrentRule } from "./workspace/projections-current.js";
23
+ import { projectionOwnershipValidRule } from "./workspace/projection-ownership-valid.js";
52
24
  import { instructionsAgentSupportedRule } from "./workspace/instructions-agent-supported.js";
53
25
  import { instructionsGitignoreCurrentRule } from "./workspace/instructions-gitignore-current.js";
54
26
  import { skillsDeclarationsValidRule } from "./workspace/skills-declarations-valid.js";
@@ -65,18 +37,15 @@ import { mcpServerSharedTargetCompatibleRule } from "./workspace/mcps-shared-tar
65
37
  import { mcpServerAgentOrphanedRule } from "./workspace/mcps-agent-orphaned.js";
66
38
  import { desiredStateReconcilableRule } from "./workspace/desired-state-reconcilable.js";
67
39
  import { knowledgeStateValidRule } from "./workspace/knowledge-state-valid.js";
40
+ import { axmSkillDeclaredRule } from "./workspace/axm-skill-declared.js";
68
41
  import { axmSkillCompatibleRule } from "./workspace/axm-skill-compatible.js";
69
42
  import { hookOwnershipAmbiguousRule } from "./workspace/hook-ownership-ambiguous.js";
70
43
  import { managedFileUnownedRule } from "./workspace/managed-file-unowned.js";
71
44
  import { sourceEndpointsAlignedRule } from "./workspace/source-endpoints-aligned.js";
72
45
  /**
73
- * Ordered v1 `workspace/*` rule catalog. Declaration order is the evaluation
74
- * order within a single `evaluateContexts` call (deterministic ordering is
75
- * test-observable; see `evaluate.ts`). The catalog groups foundation rules
76
- * first, then install-family rules by classification invariant: declaration
77
- * validity, lockfile alignment, integrity, artifact correctness, the
78
- * unmanaged-class-empty check, pack dependency resolution, and implicit
79
- * retention.
46
+ * Ordered repository-safe `workspace/*` rule catalog. Declaration order is
47
+ * the evaluation order within a single `evaluateContexts` call. It groups
48
+ * foundation rules first, then install-family rules by invariant.
80
49
  *
81
50
  * @experimental This API is unstable and may change without notice.
82
51
  */
@@ -88,6 +57,7 @@ export const repositoryWorkspaceRules = [
88
57
  lockfileValidRule,
89
58
  sourceEndpointsAlignedRule,
90
59
  desiredStateReconcilableRule,
60
+ axmSkillDeclaredRule,
91
61
  axmSkillCompatibleRule,
92
62
  agentsRecognizedRule,
93
63
  instructionsSourcePresentRule,
@@ -113,15 +83,16 @@ export const repositoryWorkspaceRules = [
113
83
  /** Rules whose evidence exists only in the live managed workspace. */
114
84
  export const liveOnlyWorkspaceRules = [
115
85
  agentsDetectedDeclaredRule,
86
+ agentsProjectionsStaleRule,
116
87
  instructionsTargetCurrentRule,
117
88
  instructionsTargetUnownedRule,
118
89
  instructionsTargetStaleRule,
119
- projectionsCurrentRule,
90
+ projectionOwnershipValidRule,
120
91
  hookOwnershipAmbiguousRule,
121
92
  managedFileUnownedRule,
122
- skillsArtifactsCorrectRule,
123
93
  mcpServerAgentDriftRule,
124
94
  mcpServerAgentOrphanedRule,
95
+ skillsArtifactsCorrectRule,
125
96
  ];
126
97
  /** Complete workspace-view catalog, retained as the public catalog surface. */
127
98
  export const workspaceRules = [
@@ -131,16 +102,18 @@ export const workspaceRules = [
131
102
  lockfileValidRule,
132
103
  sourceEndpointsAlignedRule,
133
104
  desiredStateReconcilableRule,
105
+ axmSkillDeclaredRule,
134
106
  axmSkillCompatibleRule,
135
107
  agentsRecognizedRule,
136
108
  agentsDetectedDeclaredRule,
109
+ agentsProjectionsStaleRule,
137
110
  instructionsSourcePresentRule,
138
111
  instructionsTargetCurrentRule,
139
112
  instructionsTargetUnownedRule,
140
113
  instructionsTargetStaleRule,
141
114
  instructionsAgentSupportedRule,
142
115
  instructionsGitignoreCurrentRule,
143
- projectionsCurrentRule,
116
+ projectionOwnershipValidRule,
144
117
  hookOwnershipAmbiguousRule,
145
118
  managedFileUnownedRule,
146
119
  skillsDeclarationsValidRule,
@@ -157,8 +130,4 @@ export const workspaceRules = [
157
130
  skillsArtifactsCorrectRule,
158
131
  packsDependenciesResolvedRule,
159
132
  ];
160
- // Register ids into the `LintConfig.rules` allowlist. Module-load side effect:
161
- // a consumer that imports this catalog (or the `catalog/index` barrel) enables
162
- // `axm.json` `lint.rules` to reference any of the above rule ids.
163
- registerLintRuleIds(workspaceRules.map((r) => r.id));
164
133
  //# sourceMappingURL=workspace.js.map
package/dist/src/cli.js CHANGED
@@ -699,9 +699,8 @@ const formatGroupedOverviewSentence = (args) => {
699
699
  if (args.fixableCount > 0) {
700
700
  parts.push(`${args.fixableCount} ${pluralize(args.fixableCount, "can", "can")} be fixed automatically.`);
701
701
  }
702
- const manualCount = args.diagnosticCount - args.fixableCount;
703
- if (manualCount > 0) {
704
- parts.push(`${manualCount} ${pluralize(manualCount, "needs", "need")} manual attention.`);
702
+ if (args.manualCount > 0) {
703
+ parts.push(`${args.manualCount} ${pluralize(args.manualCount, "needs", "need")} manual attention.`);
705
704
  }
706
705
  return parts.join(" ");
707
706
  };
@@ -873,6 +872,7 @@ const toGroupedLintHumanBlocks = (args) => {
873
872
  message: formatGroupedOverviewSentence({
874
873
  diagnosticCount: diagnostics.length,
875
874
  fixableCount,
875
+ manualCount: diagnostics.filter((diagnostic) => diagnostic.severity !== "info" && !diagnostic.fixable).length,
876
876
  }),
877
877
  counts: summary.counts,
878
878
  notes: [],
@@ -921,6 +921,7 @@ const toSummaryLintHumanBlocks = (args) => {
921
921
  message: formatGroupedOverviewSentence({
922
922
  diagnosticCount: diagnostics.length,
923
923
  fixableCount,
924
+ manualCount: diagnostics.filter((diagnostic) => diagnostic.severity !== "info" && !diagnostic.fixable).length,
924
925
  }),
925
926
  counts: summary.counts,
926
927
  notes: [],
@@ -1,9 +1,9 @@
1
1
  export type { WorkspaceRuleContext, WorkspaceSubject } from "./workspace-context.js";
2
2
  export { CATALOG_GROUP_ORDER, LIVE_ONLY_LINT_CATALOGS, LINT_CATALOGS, REPOSITORY_LINT_CATALOGS, emptyCatalogRuleContexts, lintCatalogsForView, type CatalogContext, type CatalogGroup, type CatalogRuleContexts, type LintView, } from "./catalog-contexts.js";
3
3
  export { LintInputSchema, LintJsonDocumentSchema, LintJsonFindingSchema, type LintInput, type LintJsonDocument, type LintJsonFinding, } from "./json-schema.js";
4
- export { findingsForProjectionFacts } from "./catalog/workspace/projections-current.js";
4
+ export { findingsForProjectionOwnership } from "./catalog/workspace/projection-ownership-valid.js";
5
5
  export { collectRenderedFindings, countFindings, detectPublishGateDrift, evaluateAllCatalogs, renderFindingsText, resolveLintExitCategory, summarizeEvaluations, toLintHumanBlocks, toLintJsonDocument, type FindingCounts, type GroupEvaluations, type LintHumanBlock, type LintHumanDiagnostic, type LintHumanReporter, type LintExitCategory, type LintSummary, type RenderFindingsArgs, type RenderedFinding, } from "./cli.js";
6
- export { allCatalogErrorRuleIds, allCatalogRuleIds, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, buildLintWorkspace, buildNativeInstalledSkillInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules, type BuildInstalledPackInfoArgs, type BuildAcquiredInstalledSkillInfoArgs, type BuildInstalledSkillInfoNativeArgs, type BuildLintWorkspaceArgs, type LintWorkspace, type LintWorkspaceView, } from "./catalog/index.js";
6
+ export { allCatalogErrorRuleIds, allCatalogRuleMetadata, allCatalogRuleIds, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, buildLintWorkspace, buildNativeInstalledSkillInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules, type BuildInstalledPackInfoArgs, type BuildAcquiredInstalledSkillInfoArgs, type BuildInstalledSkillInfoNativeArgs, type BuildLintWorkspaceArgs, type LintWorkspace, type LintWorkspaceView, } from "./catalog/index.js";
7
7
  export { LintStagingFailed } from "./run/errors.js";
8
8
  export { isolatedGitEnvironment, materializeGitIndexWorkspace, type StagedWorkspace, } from "./run/staged-workspace.js";
9
9
  export { applyDeterminedRepairs, lintConfigFromSettings, loadSettingsDocument, remapLintSummaryPaths, resolveLintRoot, type PathRemapper, } from "./run/settings.js";
package/dist/src/index.js CHANGED
@@ -3,16 +3,13 @@
3
3
  export { CATALOG_GROUP_ORDER, LIVE_ONLY_LINT_CATALOGS, LINT_CATALOGS, REPOSITORY_LINT_CATALOGS, emptyCatalogRuleContexts, lintCatalogsForView, } from "./catalog-contexts.js";
4
4
  // `--json` document schema + derived types
5
5
  export { LintInputSchema, LintJsonDocumentSchema, LintJsonFindingSchema, } from "./json-schema.js";
6
- export { findingsForProjectionFacts } from "./catalog/workspace/projections-current.js";
6
+ export { findingsForProjectionOwnership } from "./catalog/workspace/projection-ownership-valid.js";
7
7
  // Lint runner (Phase 5) — reusable primitives the `axm lint` CLI composes
8
8
  // over. Keeps the CLI command file a thin surface over flag parsing and
9
9
  // rendering per task 5.11.
10
10
  export { collectRenderedFindings, countFindings, detectPublishGateDrift, evaluateAllCatalogs, renderFindingsText, resolveLintExitCategory, summarizeEvaluations, toLintHumanBlocks, toLintJsonDocument, } from "./cli.js";
11
- // Rule catalogs (Phase 3a lands `skillRules`; Phase 3b lands `packRules`;
12
- // Phase 3c lands `workspaceRules`). Importing this index triggers each
13
- // catalog's module-load `registerLintRuleIds(...)` call so
14
- // `axm.json` `lint.rules` keys can reference any exported rule id.
15
- export { allCatalogErrorRuleIds, allCatalogRuleIds, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, buildLintWorkspace, buildNativeInstalledSkillInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules, } from "./catalog/index.js";
11
+ // Rule catalogs and their stable metadata.
12
+ export { allCatalogErrorRuleIds, allCatalogRuleMetadata, allCatalogRuleIds, buildAcquiredInstalledSkillInfo, buildInstalledPackInfo, buildLintWorkspace, buildNativeInstalledSkillInfo, acquiredSkillDisplayRoot, registryNativeSkillDisplayRoot, registryPackDisplayRoot, liveOnlyWorkspaceRules, repositoryWorkspaceRules, workspaceRules, } from "./catalog/index.js";
16
13
  // Lint-run policy: root resolution, settings-derived configuration,
17
14
  // determined repairs, staged Git-index input, and path remapping.
18
15
  export { LintStagingFailed } from "./run/errors.js";
@@ -59,7 +59,7 @@ const LintJsonSummarySchema = Schema.Struct({
59
59
  exitCategory: Schema.Literals(["clean", "warnings", "errors"]).annotate({
60
60
  identifier: "LintExitCategory",
61
61
  title: "Lint Exit Category",
62
- description: "Whether the run was clean, produced warnings, or produced errors.",
62
+ description: "Whether the run has errors, has warnings without errors, or is clean of both; informational findings remain clean.",
63
63
  }),
64
64
  }).annotate({
65
65
  identifier: "LintJsonSummary",
@@ -12,7 +12,7 @@ import type * as Effect from "effect/Effect";
12
12
  import type * as Option from "effect/Option";
13
13
  import type { ExtensionType } from "@agentxm/extension-model/unstable/extensions/common";
14
14
  import type { Handle } from "@agentxm/extension-model/unstable/extensions/handle";
15
- import type { PackDependencyReachability, AxmSkillCompatibility, WorkspaceOwnershipIssue, InstructionProjectionSnapshot } from "@agentxm/extension-workspace";
15
+ import type { PackDependencyReachability, AgentOutputInventory, AxmSkillCompatibility, WorkspaceOwnershipIssue, InstructionProjectionSnapshot } from "@agentxm/extension-workspace";
16
16
  import type { ProjectionInvariantFact } from "@agentxm/extension-workspace";
17
17
  import type { CanonicalObservation } from "@agentxm/workspace-state";
18
18
  import type { DesiredExtensionNode, DesiredStateGraph } from "@agentxm/workspace-state";
@@ -47,6 +47,8 @@ export interface WorkspaceRuleContext {
47
47
  /** Read-back currency for aggregate managed output units. */
48
48
  readonly projections?: WorkspaceProjectionsAccessor;
49
49
  readonly ownership?: Effect.Effect<ReadonlyArray<WorkspaceOwnershipIssue>>;
50
+ /** Owned and unowned agent-native outputs observed from resolved containers. */
51
+ readonly agentOutputs?: Effect.Effect<AgentOutputInventory>;
50
52
  /**
51
53
  * Installed non-pack extension manifests, keyed by nothing — rules walk the
52
54
  * list. Landed for `workspace/recommended-packs-retained`, which needs the
@@ -62,7 +64,7 @@ export interface WorkspaceRuleContext {
62
64
  /** Effective configured owner (project, then user scope), when available. */
63
65
  readonly owner?: Effect.Effect<Option.Option<Handle>>;
64
66
  /** One caller-built evaluation over the authoritative installed AXM skill. */
65
- readonly axmSkillCompatibility?: Effect.Effect<AxmSkillCompatibility, SettingsReadError | LockfileReadError>;
67
+ readonly axmSkillCompatibility?: Effect.Effect<Option.Option<AxmSkillCompatibility>, SettingsReadError | LockfileReadError>;
66
68
  /** Deterministic desired-state preflight used by local reconciliation-health rules. */
67
69
  readonly health?: {
68
70
  readonly desiredState: Effect.Effect<DesiredStateGraph, WorkspaceHealthFailure>;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@agentxm/workspace-lint",
3
- "version": "0.28.4",
3
+ "version": "0.28.5",
4
4
  "description": "AXM workspace-lint feature: workspace facts, lint rules, findings, normalization, and bounded fix planning for the axm CLI. Unstable and unsupported — use the axm.sh CLI.",
5
5
  "type": "module",
6
6
  "license": "FSL-1.1-MIT",
@@ -18,6 +18,7 @@
18
18
  "exports": {
19
19
  ".": {
20
20
  "types": "./dist/src/index.d.ts",
21
+ "axm-source": "./src/index.ts",
21
22
  "default": "./dist/src/index.js"
22
23
  }
23
24
  },
@@ -36,12 +37,12 @@
36
37
  },
37
38
  "dependencies": {
38
39
  "effect": "4.0.0-rc.112",
39
- "@agentxm/extension-sources": "^0.28.4",
40
- "@agentxm/registry-protocol": "^0.28.4",
41
- "@agentxm/extension-workspace": "^0.28.4",
42
- "@agentxm/workspace-operations": "^0.28.4",
43
- "@agentxm/extension-model": "^0.28.4",
44
- "@agentxm/workspace-state": "^0.28.4"
40
+ "@agentxm/extension-sources": "^0.28.5",
41
+ "@agentxm/registry-protocol": "^0.28.5",
42
+ "@agentxm/workspace-state": "^0.28.5",
43
+ "@agentxm/workspace-operations": "^0.28.5",
44
+ "@agentxm/extension-model": "^0.28.5",
45
+ "@agentxm/extension-workspace": "^0.28.5"
45
46
  },
46
47
  "devDependencies": {
47
48
  "@effect/platform-node": "4.0.0-rc.112",
@@ -50,6 +51,6 @@
50
51
  "@typescript/native": "npm:typescript@^7.0.2",
51
52
  "typescript": "npm:@typescript/typescript6@^6.0.2",
52
53
  "vitest": "^4.1.10",
53
- "@agentxm/agent-integration": "^0.28.4"
54
+ "@agentxm/agent-integration": "^0.28.5"
54
55
  }
55
56
  }
@@ -1,15 +0,0 @@
1
- import { type ProjectionInvariantFact } from "@agentxm/extension-workspace";
2
- import type { WorkspaceRuleContext } from "../../workspace-context.js";
3
- import type { AdvisoryRule, LintFinding } from "@agentxm/registry-protocol/unstable/lint/rule";
4
- /**
5
- * Managed output units render their complete contributor sets and content.
6
- *
7
- * Evidence is read back from the outputs themselves: an extension whose
8
- * canonical content is installed and reachable can still be absent from, or
9
- * stale in, its projection. The accessor yields no verdict when currency
10
- * cannot be judged (incomplete desired-state graph, missing canonical
11
- * content); root-cause rules own those conditions.
12
- */
13
- export declare const projectionsCurrentRule: AdvisoryRule<WorkspaceRuleContext>;
14
- export declare const findingsForProjectionFacts: (facts: ReadonlyArray<ProjectionInvariantFact>) => ReadonlyArray<LintFinding>;
15
- //# sourceMappingURL=projections-current.d.ts.map
@@ -1,35 +0,0 @@
1
- import * as Effect from "effect/Effect";
2
- import { projectionFactIsViolation, } from "@agentxm/extension-workspace";
3
- import { EMPTY_LINT_FINDINGS } from "./helpers/empty.js";
4
- const RULE_ID = "workspace/projections-current";
5
- /**
6
- * Managed output units render their complete contributor sets and content.
7
- *
8
- * Evidence is read back from the outputs themselves: an extension whose
9
- * canonical content is installed and reachable can still be absent from, or
10
- * stale in, its projection. The accessor yields no verdict when currency
11
- * cannot be judged (incomplete desired-state graph, missing canonical
12
- * content); root-cause rules own those conditions.
13
- */
14
- export const projectionsCurrentRule = {
15
- id: RULE_ID,
16
- description: "Managed outputs render every enabled reachable contributor exactly once.",
17
- kind: "advisory",
18
- severity: "error",
19
- check: (context) => Effect.gen(function* () {
20
- if (context.projections === undefined)
21
- return EMPTY_LINT_FINDINGS;
22
- return findingsForProjectionFacts(yield* context.projections.facts);
23
- }),
24
- };
25
- const contributorSuffix = (fact) => fact.affectedContributors.length === 0
26
- ? ""
27
- : ` Affected contributors: ${fact.affectedContributors.join(", ")}.`;
28
- export const findingsForProjectionFacts = (facts) => facts.filter(projectionFactIsViolation).map((fact) => ({
29
- kind: "advisory",
30
- ruleId: RULE_ID,
31
- severity: "error",
32
- message: fact.observation.message ??
33
- `The AXM-owned projection at ${fact.subject.path} is ${fact.observation.status}.${contributorSuffix(fact)}`,
34
- }));
35
- //# sourceMappingURL=projections-current.js.map