@pieai/pro-gov 0.4.9 → 0.5.0

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.
@@ -6,7 +6,7 @@ required:
6
6
  ai_in_the_loop:
7
7
  target: docs/policy/shared-rules/ai-in-the-loop.md
8
8
  source: external shared-rule SSOT
9
- agents_routing: docs/governance/agents-routing/doc-only-v0.9.md
9
+ agents_routing: docs/governance/agents-routing/doc-only-v1.0.md
10
10
  current_work: docs/reference/execution/current-work.md
11
11
  project_local_required:
12
12
  - docs/policy/best-practice-for-this-project.md
@@ -6,9 +6,9 @@ Use for non-runtime projects: IP development, AI media production, research vaul
6
6
 
7
7
  - `packages/doc-gov`
8
8
  - `packages/pro-gov`
9
- - `docs/governance/ssot-v0.9.md`
9
+ - `docs/governance/ssot-v1.0.md`
10
10
  - external AI-in-the-Loop policy linked by target projects under `docs/policy/shared-rules/ai-in-the-loop.md`
11
- - `docs/governance/agents-routing/doc-only-v0.9.md`
11
+ - `docs/governance/agents-routing/doc-only-v1.0.md`
12
12
  - starter `docs/governance/` and `docs/policy/` templates
13
13
 
14
14
  ## Does Not Include By Default
@@ -6,7 +6,7 @@ required:
6
6
  ai_in_the_loop:
7
7
  target: docs/policy/shared-rules/ai-in-the-loop.md
8
8
  source: external shared-rule SSOT
9
- agents_routing: docs/governance/agents-routing/engineering-runtime-v0.9.md
9
+ agents_routing: docs/governance/agents-routing/engineering-runtime-v1.0.md
10
10
  current_work: docs/reference/execution/current-work.md
11
11
  optional:
12
12
  mattpocock_skills: integrations/mattpocock-skills.md
@@ -6,9 +6,9 @@ Use for code-heavy projects: apps, games, runtimes, services, browser products,
6
6
 
7
7
  - `packages/doc-gov`
8
8
  - `packages/pro-gov`
9
- - `docs/governance/ssot-v0.9.md`
9
+ - `docs/governance/ssot-v1.0.md`
10
10
  - external AI-in-the-Loop policy linked by target projects under `docs/policy/shared-rules/ai-in-the-loop.md`
11
- - `docs/governance/agents-routing/engineering-runtime-v0.9.md`
11
+ - `docs/governance/agents-routing/engineering-runtime-v1.0.md`
12
12
  - `integrations/mattpocock-skills.md`
13
13
  - starter `docs/governance/` and `docs/policy/` templates
14
14
 
@@ -41,6 +41,6 @@ guardrails are also consumer-owned when installed; `doc-gov doctor` checks that
41
41
  their required governance commands remain present.
42
42
 
43
43
  For non-trivial engineering work, run `pro-gov learn recall --query "<task
44
- summary>"` before changing files so existing `docs/solutions/**` and
44
+ summary>"` before changing files so existing `docs/reference/learnings/**` and
45
45
  `CONCEPTS.md` lessons can shape the work. After verified work, use the
46
46
  `capture-learning` skill only when a reusable lesson exists.
@@ -2,7 +2,7 @@
2
2
 
3
3
  ## PGS Router Block
4
4
 
5
- <!-- PGS-ROUTER:BEGIN v0.9 -->
5
+ <!-- PGS-ROUTER:BEGIN v1.0 -->
6
6
 
7
7
  ## Boundary
8
8
 
@@ -19,7 +19,7 @@
19
19
  ## Documentation Tasks
20
20
 
21
21
  When the task creates, edits, moves, deletes, or governs documentation, read
22
- `docs/governance/boundary.md`, `docs/governance/ssot-v0.9.md`,
22
+ `docs/governance/boundary.md`, `docs/governance/ssot-v1.0.md`,
23
23
  `docs/governance/doc-agent-rules.md`, `docs/governance/doc-types.md`, the
24
24
  selected agents routing file, and all Markdown under `docs/policy/**/*.md`.
25
25
  That policy set includes subdirectories and symlinked shared-rule files; keep
@@ -1,6 +1,6 @@
1
1
  ---
2
- id: GOV-AGENTS-ROUTING-DOC-ONLY-V0-9
3
- title: Doc-Only Agents Routing v0.9
2
+ id: GOV-AGENTS-ROUTING-DOC-ONLY-V1-0
3
+ title: Doc-Only Agents Routing v1.0
4
4
  type: policy
5
5
  status: stable
6
6
  canonical: true
@@ -14,13 +14,13 @@ tags:
14
14
  - workflow
15
15
  pinned: true
16
16
  related:
17
- - GOV-SSOT-V0-9
17
+ - GOV-SSOT-V1-0
18
18
  - REF-DOC-GOVERNANCE-BOUNDARY
19
19
  supersedes: []
20
20
  superseded_by: null
21
21
  ---
22
22
 
23
- # Doc-Only Agents Routing v0.9
23
+ # Doc-Only Agents Routing v1.0
24
24
 
25
25
  Shared routing algorithm for non-runtime projects such as AI media, IP development, research, and asset governance workspaces.
26
26
 
@@ -1,6 +1,6 @@
1
1
  ---
2
- id: GOV-AGENTS-ROUTING-ENGINEERING-RUNTIME-V0-9
3
- title: Engineering Runtime Agents Routing v0.9
2
+ id: GOV-AGENTS-ROUTING-ENGINEERING-RUNTIME-V1-0
3
+ title: Engineering Runtime Agents Routing v1.0
4
4
  type: policy
5
5
  status: stable
6
6
  canonical: true
@@ -14,13 +14,13 @@ tags:
14
14
  - workflow
15
15
  pinned: true
16
16
  related:
17
- - GOV-SSOT-V0-9
17
+ - GOV-SSOT-V1-0
18
18
  - REF-DOC-GOVERNANCE-BOUNDARY
19
19
  supersedes: []
20
20
  superseded_by: null
21
21
  ---
22
22
 
23
- # Engineering Runtime Agents Routing v0.9
23
+ # Engineering Runtime Agents Routing v1.0
24
24
 
25
25
  Shared routing algorithm for app, game, runtime, and code-heavy projects.
26
26
 
@@ -53,13 +53,14 @@ project-specific AI development habits live.
53
53
  | --- | --- |
54
54
  | AI startup entry | `AGENTS.md` |
55
55
  | Agent routing algorithm | `docs/governance/agents-routing/` |
56
- | Governed SSOT rules | `docs/governance/ssot-v0.9.md` |
56
+ | Governed SSOT rules | `docs/governance/ssot-v1.0.md` |
57
57
  | Project AI development practices | `docs/policy/best-practice-for-this-project.md` |
58
58
  | Current work index | `docs/reference/execution/current-work.md` |
59
59
  | Product or world truth | `docs/canon/` |
60
+ | Durable decisions | `docs/adr/`, the single Matt-compatible decision surface |
60
61
  | Active implementation work | `docs/plans/active/` |
61
62
  | Completed proof records | `docs/plans/completed/` |
62
- | Reusable project learnings | `docs/solutions/**` as learning artifacts |
63
+ | Reusable project learnings | `docs/reference/learnings/**` as governed reference documents |
63
64
  | Product artifacts outside governed docs | Project package, workbench, assets, runtime config, or source tree |
64
65
 
65
66
  When unsure, ask this question: is this rule about how the document system works,
@@ -76,5 +77,6 @@ product packages, workbenches, prompt libraries, source folders, or asset
76
77
  folders can be product artifacts. Keep those files where the product package
77
78
  expects them unless a project explicitly opts them into doc-gov.
78
79
 
79
- Learning artifacts under `docs/solutions/**` are not governed docs by default.
80
- They remain searchable project artifacts with their own lightweight metadata.
80
+ Matt-native files under `docs/adr/**` and `docs/agents/**` are not governed docs
81
+ by default. `docs/adr/**` is nevertheless the only durable decision surface;
82
+ do not copy ADRs into a second PGS decision directory.
@@ -42,7 +42,6 @@ If a canonical document exists, update it instead of creating a parallel documen
42
42
  Use only:
43
43
 
44
44
  - `policy`
45
- - `decision`
46
45
  - `spec`
47
46
  - `plan`
48
47
  - `canon`
@@ -57,14 +56,11 @@ Normal documents:
57
56
  draft -> active -> completed -> stable -> superseded -> archived
58
57
  ```
59
58
 
60
- Decision documents:
61
-
62
- ```text
63
- proposed -> accepted -> rejected | superseded
64
- ```
65
-
66
59
  `completed` is for finished execution records or specs that should no longer appear in active folders.
67
60
 
61
+ Durable decisions belong only in `docs/adr/**`. They use Matt's concise ADR
62
+ format and remain outside the Doc Gov status machine.
63
+
68
64
  ## Upstream Rule
69
65
 
70
66
  Do not locally invent doc-gov core changes. Propose core changes upstream in
@@ -28,7 +28,6 @@ Allowed types:
28
28
  | Type | Default path |
29
29
  | --- | --- |
30
30
  | `policy` | `docs/policy/` for project policy; `docs/governance/` for doc-system policy |
31
- | `decision` | `docs/decisions/` |
32
31
  | `spec` | `docs/specs/active/` or `docs/specs/completed/` |
33
32
  | `plan` | `docs/plans/active/` or `docs/plans/completed/` |
34
33
  | `canon` | `docs/canon/` |
@@ -43,8 +42,5 @@ Normal documents use:
43
42
  draft -> active -> completed -> stable -> superseded -> archived
44
43
  ```
45
44
 
46
- Decision documents use:
47
-
48
- ```text
49
- proposed -> accepted -> rejected | superseded
50
- ```
45
+ Durable decisions use the Matt-compatible `docs/adr/**` surface and stay
46
+ outside the Doc Gov schema. Do not duplicate them in another decision folder.
@@ -1,6 +1,6 @@
1
1
  ---
2
- id: GOV-SSOT-V0-9
3
- title: SSOT Documentation Governance Rules v0.9
2
+ id: GOV-SSOT-V1-0
3
+ title: SSOT Documentation Governance Rules v1.0
4
4
  type: policy
5
5
  status: stable
6
6
  canonical: true
@@ -19,7 +19,7 @@ supersedes: []
19
19
  superseded_by: null
20
20
  ---
21
21
 
22
- # SSOT Documentation Governance Rules v0.9
22
+ # SSOT Documentation Governance Rules v1.0
23
23
 
24
24
  This rule defines how projects using Project Governance System handle
25
25
  **SSOT (Single Source of Truth)** for governed documentation.
@@ -37,7 +37,7 @@ Project Governance System governs:
37
37
  - `AGENTS.md` and equivalent AI entry files.
38
38
  - Governed Markdown under `docs/**`.
39
39
  - Documentation governance rules, agent routing rules, templates, manifests,
40
- plans, specs, decisions, references, canon, and archives that live under
40
+ plans, specs, references, canon, and archives that live under
41
41
  `docs/**`.
42
42
 
43
43
  Project Governance System does **not** automatically govern every Markdown file
@@ -50,10 +50,12 @@ Markdown.
50
50
 
51
51
  Extra governed roots are allowed only when a project explicitly opts in.
52
52
 
53
- Learning and optional-tool artifacts are explicitly outside the default Doc Gov
54
- schema:
53
+ Matt-native and optional-tool artifacts are explicitly outside the default Doc
54
+ Gov schema:
55
55
 
56
- - `docs/solutions/**`
56
+ - `CONTEXT.md` and `CONTEXT-MAP.md`
57
+ - `docs/adr/**`
58
+ - `docs/agents/**`
57
59
  - `docs/brainstorms/**`
58
60
  - `docs/pulse-reports/**`
59
61
 
@@ -83,7 +85,7 @@ Before changing docs, look for project-local guidance in this order:
83
85
 
84
86
  1. `AGENTS.md`, `CLAUDE.md`, or equivalent AI router/config adapter.
85
87
  2. `docs/governance/boundary.md`.
86
- 3. `docs/governance/ssot-v0.9.md`.
88
+ 3. `docs/governance/ssot-v1.0.md`.
87
89
  4. `docs/governance/agents-routing/` and the project's selected agents-routing file.
88
90
  5. `docs/reference/documentation-map.md`, root `README.md` for human-facing
89
91
  orientation, or another project-defined documentation index.
@@ -114,9 +116,10 @@ works for governed docs:
114
116
  | Agents-routing rules | `docs/governance/agents-routing/` |
115
117
  | Doc-system rules, templates, and manifest | `docs/governance/` |
116
118
  | Project AI/development policy | `docs/policy/` |
119
+ | Durable decisions | `docs/adr/`, the single Matt-compatible decision surface outside Doc Gov schema |
117
120
  | Product or feature requirement | `docs/specs/` |
118
121
  | Step-by-step implementation work | `docs/plans/` |
119
- | Reusable learning records | `docs/solutions/**`, outside Doc Gov schema |
122
+ | Reusable learning records | `docs/reference/learnings/**`, governed as `reference` documents and recalled on demand |
120
123
  | Durable workspace/system truth | `docs/canon/` |
121
124
  | How-to guides, architecture maps, tool notes | `docs/reference/` |
122
125
  | Retired governed history | `docs/archive/` |
@@ -1,23 +1,3 @@
1
- ---
2
- id: REPLACE-ME
3
- title: Replace Me
4
- type: decision
5
- status: proposed
6
- canonical: true
7
- owner: human
8
- created: YYYY-MM-DD
9
- last_reviewed: YYYY-MM-DD
10
- domain: meta
11
- tags:
12
- - replace-me
13
- pinned: false
14
- related: []
15
- ---
1
+ # Replace Me
16
2
 
17
- # REPLACE-ME: Replace Me
18
-
19
- ## Context
20
-
21
- ## Decision
22
-
23
- ## Consequences
3
+ Record the context, decision, and why in one to three sentences.
@@ -24,10 +24,10 @@ Use `AGENTS.md` for startup reading. It should point agents to:
24
24
 
25
25
  - `docs/policy/*.md`
26
26
  - `docs/governance/boundary.md`
27
- - `docs/governance/ssot-v0.9.md`
27
+ - `docs/governance/ssot-v1.0.md`
28
28
  - `docs/governance/doc-agent-rules.md`
29
29
  - `docs/governance/doc-types.md`
30
- - `docs/governance/agents-routing/<selected-profile>-v0.9.md`
30
+ - `docs/governance/agents-routing/<selected-profile>-v1.0.md`
31
31
  - `docs/reference/execution/current-work.md`
32
32
 
33
33
  ## Areas
@@ -35,12 +35,12 @@ Use `AGENTS.md` for startup reading. It should point agents to:
35
35
  | Area | Purpose |
36
36
  | --- | --- |
37
37
  | `docs/policy/` | Project policy and AI development rules |
38
- | `docs/decisions/` | Durable decisions |
38
+ | `docs/adr/` | The single Matt-compatible durable decision surface, outside Doc Gov schema |
39
39
  | `docs/specs/active/` | Active requirements |
40
40
  | `docs/specs/completed/` | Completed specs |
41
41
  | `docs/plans/active/` | Active implementation plans |
42
42
  | `docs/plans/completed/` | Completed execution records |
43
- | `docs/solutions/` | Reusable learning records, outside Doc Gov schema |
43
+ | `docs/reference/learnings/` | Governed reusable learning references, recalled only when relevant |
44
44
  | `docs/canon/` | Durable project truth |
45
45
  | `docs/reference/` | Guides and references |
46
46
  | `docs/archive/` | Retired history |
@@ -51,6 +51,6 @@ project-package canon, generated media notes, and source-package files stay in
51
51
  their product/workbench structure unless this project explicitly opts them into
52
52
  doc-gov.
53
53
 
54
- Optional skills may create `docs/solutions/**`, `docs/brainstorms/**`, or
55
- `docs/pulse-reports/**`. Those are external artifacts, not Doc Gov canonical
56
- documents.
54
+ Optional skills may create `docs/brainstorms/**` or `docs/pulse-reports/**` as
55
+ external artifacts. Capture Learning writes governed references under
56
+ `docs/reference/learnings/**`.
package/cli-guide.md CHANGED
@@ -48,7 +48,7 @@ commands without requiring byte-for-byte template equality. It infers the
48
48
  installed profile when exactly one route exists; `--profile` resolves an empty
49
49
  or temporarily ambiguous target. PGS does not install or modify AI-host hooks.
50
50
 
51
- `learn recall` searches `docs/solutions/**` and
51
+ `learn recall` searches `docs/reference/learnings/**` and
52
52
  `CONCEPTS.md` in the target repository and returns the most relevant prior
53
53
  lessons for the current task. Use it before non-trivial implementation,
54
54
  debugging, release, architecture, or portfolio-sync work. It is read-only and
@@ -56,7 +56,7 @@ does not require a full local PGS checkout.
56
56
 
57
57
  `learn capture` is the small native capture path. Use it only after verified
58
58
  work produced a reusable lesson that is not already covered by an existing
59
- `docs/solutions/**` record:
59
+ `docs/reference/learnings/**` record:
60
60
 
61
61
  ```bash
62
62
  pro-gov learn capture \
package/dist/cli.js CHANGED
@@ -1654,9 +1654,9 @@ import { createRequire } from "node:module";
1654
1654
  import { dirname as dirname6, join as join12 } from "node:path";
1655
1655
  var REQUIRED_ASSETS = [
1656
1656
  "starter/AGENTS.template.md",
1657
- "starter/docs/governance/ssot-v0.9.md",
1658
- "starter/docs/governance/agents-routing/engineering-runtime-v0.9.md",
1659
- "starter/docs/governance/agents-routing/doc-only-v0.9.md",
1657
+ "starter/docs/governance/ssot-v1.0.md",
1658
+ "starter/docs/governance/agents-routing/engineering-runtime-v1.0.md",
1659
+ "starter/docs/governance/agents-routing/doc-only-v1.0.md",
1660
1660
  "profiles/engineering-runtime/profile.md",
1661
1661
  "profiles/doc-only/profile.md"
1662
1662
  ];
@@ -1738,7 +1738,7 @@ function classifyOwnership(targetPath) {
1738
1738
  return "shared";
1739
1739
  }
1740
1740
  function isOtherProfileRouting(targetPath, profile) {
1741
- return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v0.9.md`;
1741
+ return targetPath.startsWith("docs/governance/agents-routing/") && targetPath !== `docs/governance/agents-routing/${profile}-v1.0.md`;
1742
1742
  }
1743
1743
  function starterTargetPath(sourcePath) {
1744
1744
  if (sourcePath === "starter/AGENTS.template.md") return "AGENTS.md";
@@ -1800,7 +1800,7 @@ function applyStarterFiles(files, profile) {
1800
1800
  return 0;
1801
1801
  }
1802
1802
  function renderAgentsTemplate(template, projectName, profile) {
1803
- const selectedRoute = `docs/governance/agents-routing/${profile}-v0.9.md`;
1803
+ const selectedRoute = `docs/governance/agents-routing/${profile}-v1.0.md`;
1804
1804
  return template.replace("# PROJECT_NAME AI Router", `# ${projectName} AI Router`).replace("`PROFILE_NAME`", `\`${profile}\``).replace("`PROFILE_ROUTE`", `\`${selectedRoute}\``).replace(/\n{3,}/g, "\n\n");
1805
1805
  }
1806
1806
  function readFlag(args, flag) {
@@ -1834,18 +1834,22 @@ function recallLearnings(root, options) {
1834
1834
  return { query, hits };
1835
1835
  }
1836
1836
  function loadLearningRecords(root) {
1837
- const records = [];
1838
- const solutionsDir = join14(root, "docs/solutions");
1839
- if (existsSync14(solutionsDir)) {
1840
- for (const path of listMarkdownFiles(solutionsDir)) {
1841
- records.push(readLearningRecord(root, path));
1837
+ const recordsByTitle = /* @__PURE__ */ new Map();
1838
+ for (const relativeDir of ["docs/reference/learnings", "docs/solutions"]) {
1839
+ const learningDir = join14(root, relativeDir);
1840
+ if (!existsSync14(learningDir)) continue;
1841
+ for (const path of listMarkdownFiles(learningDir)) {
1842
+ const record = readLearningRecord(root, path);
1843
+ const key = record.title.trim().toLowerCase();
1844
+ if (!recordsByTitle.has(key)) recordsByTitle.set(key, record);
1842
1845
  }
1843
1846
  }
1844
1847
  const conceptsPath = join14(root, "CONCEPTS.md");
1845
1848
  if (existsSync14(conceptsPath)) {
1846
- records.push(readLearningRecord(root, conceptsPath));
1849
+ const record = readLearningRecord(root, conceptsPath);
1850
+ recordsByTitle.set(`concepts:${record.title.toLowerCase()}`, record);
1847
1851
  }
1848
- return records;
1852
+ return [...recordsByTitle.values()];
1849
1853
  }
1850
1854
  function listMarkdownFiles(dir) {
1851
1855
  const files = [];
@@ -1944,7 +1948,7 @@ function cleanMarkdownLine(input) {
1944
1948
 
1945
1949
  // src/learning/capture.ts
1946
1950
  import { existsSync as existsSync15, mkdirSync as mkdirSync5, writeFileSync as writeFileSync4 } from "node:fs";
1947
- import { join as join15, relative as relative6 } from "node:path";
1951
+ import { basename as basename5, join as join15, relative as relative6 } from "node:path";
1948
1952
  function captureLearning(root, options) {
1949
1953
  const title = options.title.trim();
1950
1954
  const summary = options.summary.trim();
@@ -1952,10 +1956,11 @@ function captureLearning(root, options) {
1952
1956
  if (!summary) throw new Error("summary is required");
1953
1957
  const category = slugify(options.category ?? "workflow-issues") || "workflow-issues";
1954
1958
  const moduleName = options.module?.trim() || "PGS learning capture";
1955
- const dir = join15(root, "docs/solutions", category);
1959
+ const dir = join15(root, "docs/reference/learnings", category);
1956
1960
  mkdirSync5(dir, { recursive: true });
1957
1961
  const path = uniquePath(dir, slugify(title) || "learning");
1958
- writeFileSync4(path, renderLearning({ title, summary, category, moduleName }));
1962
+ const idSlug = basename5(path, ".md");
1963
+ writeFileSync4(path, renderLearning({ title, summary, category, moduleName, idSlug }));
1959
1964
  return {
1960
1965
  relativePath: normalizePath2(relative6(root, path)),
1961
1966
  title,
@@ -1965,12 +1970,23 @@ function captureLearning(root, options) {
1965
1970
  function renderLearning(options) {
1966
1971
  return [
1967
1972
  "---",
1973
+ `id: REF-LEARNING-${upperSlug(options.category)}-${upperSlug(options.idSlug)}`,
1968
1974
  `title: ${yamlString(options.title)}`,
1969
- `date: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
1975
+ "type: reference",
1976
+ "status: stable",
1977
+ "canonical: true",
1978
+ "owner: ai-assisted",
1979
+ `created: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
1980
+ `last_reviewed: ${(/* @__PURE__ */ new Date()).toISOString().slice(0, 10)}`,
1981
+ "domain: learning",
1982
+ "tags:",
1983
+ " - learning-recall",
1984
+ ` - ${options.category}`,
1985
+ "pinned: false",
1986
+ "related: []",
1970
1987
  `category: ${options.category}`,
1971
1988
  `module: ${yamlString(options.moduleName)}`,
1972
1989
  "capture_mode: pgs-native",
1973
- "tags: [learning-recall]",
1974
1990
  "---",
1975
1991
  "",
1976
1992
  `# ${options.title}`,
@@ -2004,6 +2020,9 @@ function yamlString(input) {
2004
2020
  function normalizePath2(path) {
2005
2021
  return path.split("\\").join("/");
2006
2022
  }
2023
+ function upperSlug(input) {
2024
+ return slugify(input).toUpperCase() || "LEARNING";
2025
+ }
2007
2026
 
2008
2027
  // src/commands/learn.ts
2009
2028
  function runLearn(args) {
@@ -2173,7 +2192,7 @@ import { dirname as dirname9 } from "node:path";
2173
2192
 
2174
2193
  // src/lens/audit.ts
2175
2194
  import { existsSync as existsSync16, mkdirSync as mkdirSync6, readFileSync as readFileSync10, writeFileSync as writeFileSync5 } from "node:fs";
2176
- import { basename as basename5, dirname as dirname8, join as join16 } from "node:path";
2195
+ import { basename as basename6, dirname as dirname8, join as join16 } from "node:path";
2177
2196
  var REQUIRED_ARTIFACTS = [
2178
2197
  "manifest.md",
2179
2198
  "raw/project-lens/architecture-lens.md",
@@ -2191,7 +2210,7 @@ function createProjectLensAuditPackage(targetDir, auditDir) {
2191
2210
  version: 1,
2192
2211
  target: {
2193
2212
  path: targetDir,
2194
- name: basename5(targetDir) || "target"
2213
+ name: basename6(targetDir) || "target"
2195
2214
  },
2196
2215
  requiredArtifacts: [...REQUIRED_ARTIFACTS]
2197
2216
  };
@@ -3511,7 +3530,7 @@ import {
3511
3530
  statSync as statSync4,
3512
3531
  writeFileSync as writeFileSync7
3513
3532
  } from "node:fs";
3514
- import { dirname as dirname12, join as join20, relative as relative8, resolve as resolve4 } from "node:path";
3533
+ import { dirname as dirname12, join as join20, relative as relative8, resolve as resolve4, sep } from "node:path";
3515
3534
  import { fileURLToPath as fileURLToPath4 } from "node:url";
3516
3535
  function inspectPortfolioAiHealth(options) {
3517
3536
  const endpoints = collectEndpoints(options.manifest);
@@ -3602,9 +3621,11 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
3602
3621
  if (skills.claudeCompatibility === "duplicate-directory") recommendations.push("`.claude/skills` \u662F\u72EC\u7ACB\u526F\u672C\uFF1B\u5EFA\u8BAE\u94FE\u63A5\u5230 `.agents/skills`\uFF0C\u907F\u514D\u53CC\u4EFD\u6280\u80FD\u6F02\u79FB\u3002");
3603
3622
  if (skills.claudeCompatibility === "dangling-symlink") recommendations.push("`.claude/skills` \u662F\u65AD\u5F00\u7684\u94FE\u63A5\u3002");
3604
3623
  if (hasWorkflowReminderHooks(hooks)) recommendations.push("\u53D1\u73B0 Stop/SubagentStop hook\uFF1B\u786E\u8BA4\u5B83\u662F\u5426\u4ECD\u6709\u9879\u76EE\u4E13\u5C5E\u7528\u9014\uFF0C\u5E76\u79FB\u9664\u9000\u4F11\u7684 PGS \u5DE5\u4F5C\u6D41\u63D0\u9192\u3002");
3605
- const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template);
3624
+ const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
3606
3625
  if (liveEnv.some((file) => file.tracked)) recommendations.push("\u53D1\u73B0\u88AB Git \u8DDF\u8E2A\u7684\u771F\u5B9E\u73AF\u5883\u6587\u4EF6\uFF1B\u5E94\u7ACB\u5373\u786E\u8BA4\u5176\u4E2D\u662F\u5426\u5305\u542B\u51ED\u636E\u5E76\u8FC1\u51FA\u4ED3\u5E93\u3002");
3607
- else if (liveEnv.length > 0 && secrets.centralDirectory === "absent") recommendations.push("\u4ED3\u5E93\u6709\u672C\u5730\u73AF\u5883\u6587\u4EF6\uFF0C\u4F46\u4E2D\u592E `.secrets` \u4E2D\u6CA1\u6709\u5BF9\u5E94\u76EE\u5F55\uFF1B\u786E\u8BA4\u662F\u5426\u9700\u8981\u7EB3\u5165\u5206\u5C42\u7BA1\u7406\u3002");
3626
+ if (liveEnv.length > 0 && secrets.centralDirectory === "absent") recommendations.push("\u4ED3\u5E93\u6709\u672C\u5730\u73AF\u5883\u6587\u4EF6\uFF0C\u4F46\u4E2D\u592E `.secrets` \u4E2D\u6CA1\u6709\u5BF9\u5E94\u76EE\u5F55\uFF1B\u786E\u8BA4\u662F\u5426\u9700\u8981\u7EB3\u5165\u5206\u5C42\u7BA1\u7406\u3002");
3627
+ else if (liveEnv.some((file) => !file.centralized)) recommendations.push("\u53D1\u73B0\u771F\u5B9E\u73AF\u5883\u6587\u4EF6\u5C1A\u672A\u8FDE\u63A5\u5230\u672C\u9879\u76EE\u7684\u4E2D\u592E `.secrets` \u76EE\u5F55\uFF1B\u533A\u5206\u53EF\u4E22\u5F03\u751F\u6210\u7269\u4E0E\u672C\u5730\u4E3B\u6765\u6E90\uFF0C\u9700\u4FDD\u7559\u7684\u6765\u6E90\u5E94\u96C6\u4E2D\u540E\u518D\u63A5\u56DE\u9879\u76EE\u3002");
3628
+ if (hasUnsafeCentralSecretPermissions(secrets)) recommendations.push("\u4E2D\u592E\u5BC6\u94A5\u76EE\u5F55\u6216\u6587\u4EF6\u6743\u9650\u8FC7\u5BBD\uFF1B\u76EE\u5F55\u5E94\u4E3A 0700\uFF0C\u914D\u7F6E\u6587\u4EF6\u5E94\u4E3A 0600\u3002");
3608
3629
  if (!docs.packages.aligned) recommendations.push(`PGS \u5305\u7248\u672C\u672A\u4E0E\u6267\u884C\u5F15\u64CE ${docs.packages.expected ?? "\u672A\u77E5\u7248\u672C"} \u5BF9\u9F50\uFF1B\u53D1\u5E03\u4E0A\u6E38\u540E\u518D\u540C\u6B65\u76EE\u6807\u4ED3\u5E93\u3002`);
3609
3630
  if (role === "target" && !docs.manifest) recommendations.push("\u7F3A\u5C11 docs/governance/MANIFEST.yml\uFF1B\u6587\u6863\u6E05\u5355\u65E0\u6CD5\u8BC1\u660E\u5DF2\u540C\u6B65\u3002");
3610
3631
  if (technologyGovernance && !projectModel.projectType) recommendations.push("\u672A\u58F0\u660E\u9879\u76EE\u7C7B\u578B\uFF1B\u65E0\u6CD5\u628A\u5B9E\u9645\u6280\u672F\u4E0E\u4EA7\u54C1\u7EBF\u57FA\u7EBF\u8FDB\u884C\u6BD4\u8F83\u3002");
@@ -3630,10 +3651,12 @@ function inspectRepository(endpoint, role, secretsRoot, expectedPackageVersion,
3630
3651
  };
3631
3652
  }
3632
3653
  function deriveStatus(entries, git, hooks, skills, secrets, projectModel, technologyGovernanceConfigured) {
3633
- if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || skills.canonical.some((item) => item.kind === "dangling-symlink") || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template)) return "unhealthy";
3654
+ if (!git.isRepository || entries.agents === "missing" || entries.claude === "dangling-symlink" || entries.gemini === "dangling-symlink" || skills.canonical.some((item) => item.kind === "dangling-symlink") || secrets.repositoryEnvFiles.some((file) => file.tracked && !file.template && !file.fixture) || hasUnsafeCentralSecretPermissions(secrets)) return "unhealthy";
3634
3655
  const missingBaseline = projectModel.baseline.some((technology) => !technology.detected && !hasBaselineException(projectModel, technology.id));
3635
3656
  const missingSelected = projectModel.optionalCapabilities.some((technology) => technology.selected && !technology.detected);
3636
- if (entries.agents !== "pgs-router" || entries.claude !== "agents-symlink" || hasWorkflowReminderHooks(hooks) || skills.claudeCompatibility === "duplicate-directory" || skills.claudeCompatibility === "dangling-symlink" || git.branches.length > 1 || git.worktrees.length > 1 || git.dirtyPaths.length > 0 || (git.ahead ?? 0) > 0 || technologyGovernanceConfigured && !projectModel.projectType || missingBaseline || missingSelected) return "attention";
3657
+ const liveEnv = secrets.repositoryEnvFiles.filter((file) => !file.template && !file.fixture);
3658
+ const secretMaterializationNeedsReview = liveEnv.some((file) => !file.centralized);
3659
+ if (entries.agents !== "pgs-router" || entries.claude !== "agents-symlink" || hasWorkflowReminderHooks(hooks) || skills.claudeCompatibility === "duplicate-directory" || skills.claudeCompatibility === "dangling-symlink" || git.branches.length > 1 || git.worktrees.length > 1 || git.dirtyPaths.length > 0 || (git.ahead ?? 0) > 0 || secretMaterializationNeedsReview || technologyGovernanceConfigured && !projectModel.projectType || missingBaseline || missingSelected) return "attention";
3637
3660
  return "healthy";
3638
3661
  }
3639
3662
  function inspectProjectModel(root, endpoint, governance) {
@@ -3795,11 +3818,14 @@ function inspectClaudeSkillRoot(root) {
3795
3818
  }
3796
3819
  function inspectRepositorySecrets(root, id, secretsRoot, isRepository) {
3797
3820
  const centralPath = join20(secretsRoot, id);
3821
+ const centralRealPath = safeRealpath(centralPath);
3798
3822
  const envFiles = collectEnvironmentFiles(root).map((path) => ({
3799
3823
  path,
3800
3824
  tracked: isRepository ? gitTracks(root, path) : false,
3801
- template: /(?:example|sample|template|defaults?)$/i.test(basenameOnly(path)),
3802
- symlink: lstatSync6(join20(root, path)).isSymbolicLink()
3825
+ template: isEnvironmentTemplate(path),
3826
+ fixture: isEnvironmentFixture(path),
3827
+ symlink: lstatSync6(join20(root, path)).isSymbolicLink(),
3828
+ centralized: pointsInside(join20(root, path), centralRealPath)
3803
3829
  }));
3804
3830
  return {
3805
3831
  centralDirectory: existsSync21(centralPath) ? "present" : "absent",
@@ -3816,7 +3842,7 @@ function collectEnvironmentFiles(root, current = root, depth = 0) {
3816
3842
  for (const entry of readdirSync8(current, { withFileTypes: true })) {
3817
3843
  if (entry.isDirectory()) {
3818
3844
  if (!SKIP_ENV_DIRECTORIES.has(entry.name)) found.push(...collectEnvironmentFiles(root, join20(current, entry.name), depth + 1));
3819
- } else if (entry.name === ".env" || entry.name.startsWith(".env.")) {
3845
+ } else if (isEnvironmentFilename(entry.name) && !isProviderGeneratedEnvironmentFile(entry.name)) {
3820
3846
  found.push(relative8(root, join20(current, entry.name)));
3821
3847
  }
3822
3848
  }
@@ -3843,6 +3869,35 @@ function basenameOnly(path) {
3843
3869
  const parts = path.split(/[\\/]/);
3844
3870
  return parts[parts.length - 1] ?? path;
3845
3871
  }
3872
+ function isEnvironmentFilename(name) {
3873
+ return name === ".env" || name.startsWith(".env.") || name.endsWith(".env");
3874
+ }
3875
+ function isProviderGeneratedEnvironmentFile(name) {
3876
+ return name.endsWith(".pulled");
3877
+ }
3878
+ function isEnvironmentTemplate(path) {
3879
+ return /(^|[._-])(example|sample|template|defaults?)([._-]|$)/i.test(basenameOnly(path));
3880
+ }
3881
+ function isEnvironmentFixture(path) {
3882
+ return /(^|[\\/])(?:tests?|__tests__)[\\/]fixtures?[\\/]/i.test(path) || /(^|[\\/])__fixtures__[\\/]/i.test(path);
3883
+ }
3884
+ function safeRealpath(path) {
3885
+ try {
3886
+ return realpathSync2(path);
3887
+ } catch {
3888
+ return void 0;
3889
+ }
3890
+ }
3891
+ function pointsInside(path, expectedRoot) {
3892
+ if (!expectedRoot || !lstatSync6(path).isSymbolicLink()) return false;
3893
+ const target = safeRealpath(path);
3894
+ if (!target) return false;
3895
+ const fromRoot = relative8(expectedRoot, target);
3896
+ return fromRoot === "" || fromRoot !== ".." && !fromRoot.startsWith(`..${sep}`);
3897
+ }
3898
+ function hasUnsafeCentralSecretPermissions(secrets) {
3899
+ return secrets.centralDirectory === "present" && (secrets.centralMode !== "700" || secrets.centralFiles.some((file) => file.mode !== "600"));
3900
+ }
3846
3901
  function inspectSecretsRoot(path) {
3847
3902
  return existsSync21(path) ? { path, exists: true, mode: modeString(statSync4(path).mode) } : { path, exists: false };
3848
3903
  }
@@ -4471,7 +4526,7 @@ function normalizeMarkdownTableCell(cell) {
4471
4526
  }
4472
4527
  function inferInstalledProfile(root) {
4473
4528
  const installed = ["engineering-runtime", "doc-only"].filter(
4474
- (profile) => existsSync23(join22(root, `docs/governance/agents-routing/${profile}-v0.9.md`))
4529
+ (profile) => existsSync23(join22(root, `docs/governance/agents-routing/${profile}-v1.0.md`))
4475
4530
  );
4476
4531
  return installed.length === 1 ? installed[0] : void 0;
4477
4532
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@pieai/pro-gov",
3
- "version": "0.4.9",
3
+ "version": "0.5.0",
4
4
  "description": "Project-level distribution kit for Project Governance System.",
5
5
  "keywords": [
6
6
  "ai-agents",
@@ -35,7 +35,7 @@
35
35
  "access": "public"
36
36
  },
37
37
  "dependencies": {
38
- "@pieai/doc-gov": "^0.4.9"
38
+ "@pieai/doc-gov": "^0.5.0"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@pieai/swimmer-ui-kit": "1.0.1",