@guanlin4924/devflow-pi 0.1.69 → 0.1.70

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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@guanlin4924/devflow-pi",
3
- "version": "0.1.69",
3
+ "version": "0.1.70",
4
4
  "type": "module",
5
5
  "description": "Devflow 专项研发 Pi,基于固定版本 OMP Runtime 组装研发状态机、代码智能、设计、实现与质量门禁。",
6
6
  "license": "UNLICENSED",
@@ -209,6 +209,8 @@ Run the database validator with identifiers returned by the scan:
209
209
  devflow_skill_script(script="project-context-db-validate", args=["--database", "<control.db>", "--project-id", "<project-id>", "--workspace-id", "<workspace-id>", "--run-id", "<run-id>"])
210
210
  ```
211
211
 
212
+ The `--database` value must come from `devflow_project_context(action="scan").database` or the loaded project context `controlDatabasePath`. Never guess `./control.db`, `.devflow/control.db`, `.planning/code-index/index.sqlite`, an OMP agent database, or any SQLite found by searching user directories. Code-index SQLite is not the initialization control database and does not contain the initialization `schema_meta` table.
213
+
212
214
  Completion requires a stable immutable input, matching context snapshot, accounted required categories, evidence for observed facts, a registered immutable index in full mode, revision-safe pointer publication, and separate document statuses. Workflow `STATE` and other non-initialization authorities must remain unchanged.
213
215
 
214
216
  Report partial, unsupported, skipped, failed, and unknown coverage honestly. Initialization success does not mean tests passed, services ran, designs were approved, or requirements were delivered.
@@ -116,6 +116,8 @@ class _PythonVisitor(ast.NodeVisitor):
116
116
 
117
117
  @staticmethod
118
118
  def _signature(node):
119
+ if isinstance(node, ast.ClassDef):
120
+ return f"{node.name}()"
119
121
  arguments = [argument.arg for argument in [*node.args.posonlyargs, *node.args.args, *node.args.kwonlyargs]]
120
122
  if node.args.vararg:
121
123
  arguments.append(f"*{node.args.vararg.arg}")
@@ -97,13 +97,15 @@ def _publish_snapshot(staging: Path, output: Path, counts: Dict[str, int], works
97
97
  os.close(directory_fd)
98
98
 
99
99
 
100
- def _workspace_state(root: Path, files: List[dict], changed: Set[str]) -> Dict[str, object]:
100
+ def _workspace_state(root: Path, files: List[dict], changed: Set[str], discovered_paths: Iterable[str] | None = None) -> Dict[str, object]:
101
101
  indexed_paths = sorted(record["path"] for record in files)
102
+ discovered = sorted(set(discovered_paths or indexed_paths))
102
103
  source_manifest_hash = _hash_lines(f"{record['path']}\0{record['content_hash']}" for record in files)
103
104
  return {
104
105
  "repositoryRevision": _git_output(root, ["rev-parse", "HEAD"]),
105
106
  "contentManifestHash": source_manifest_hash,
106
107
  "indexedPathsHash": _hash_lines(indexed_paths),
108
+ "discoveredPathsHash": _hash_lines(discovered),
107
109
  "dirtyPathsAtBuild": sorted(changed),
108
110
  "extractors": sorted(set(record.get("parser", "unknown") for record in files)),
109
111
  "capabilities": {
@@ -210,12 +212,14 @@ def command_scan(args) -> int:
210
212
 
211
213
  files, symbols, relations, diagnostics = _existing_after_removal(existing, changed)
212
214
  indexed_paths = {record["path"] for record in files}
215
+ discovered_paths: Set[str] = set()
213
216
 
214
217
  for codebase in codebases:
215
218
  if not codebase.root.exists():
216
219
  diagnostics.append(_diagnostic(codebase.name, "", 1, "warning", "skipped_file", f"codebase root does not exist: {codebase.root}", "discovery@1"))
217
220
  continue
218
221
  all_files = scan_files(codebase.root, excludes=args.exclude)
222
+ discovered_paths.update(_workspace_rel(root, path) for path in all_files)
219
223
  if changed:
220
224
  all_files = [path for path in all_files if _workspace_rel(root, path) in changed]
221
225
  manifests = [path for path in all_files if path.name in {"package.json", "go.mod", "Cargo.toml", "pyproject.toml", "requirements.txt", "composer.json", "pubspec.yaml"}]
@@ -280,7 +284,7 @@ def command_scan(args) -> int:
280
284
  "relations": len(relations),
281
285
  "diagnostics": len(diagnostics),
282
286
  },
283
- _workspace_state(root, files, changed),
287
+ _workspace_state(root, files, changed, discovered_paths),
284
288
  )
285
289
  finally:
286
290
  shutil.rmtree(staging, ignore_errors=True)
@@ -406,7 +410,8 @@ def _query_is_fresh(root: Path, snapshot: Dict[str, object], files: List[dict])
406
410
  indexed_paths = sorted(record["path"] for record in files)
407
411
  if not any(path.startswith("../") for path in indexed_paths):
408
412
  current_paths = sorted(_workspace_rel(root, item) for item in scan_files(root))
409
- if _hash_lines(current_paths) != workspace.get("indexedPathsHash"):
413
+ expected_paths_hash = workspace.get("discoveredPathsHash") or workspace.get("indexedPathsHash")
414
+ if _hash_lines(current_paths) != expected_paths_hash:
410
415
  return False
411
416
  return True
412
417
 
package/src/constants.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  export const PRODUCT_NAME = "Devflow Pi";
2
- export const PRODUCT_VERSION = "0.1.69";
2
+ export const PRODUCT_VERSION = "0.1.70";
3
3
  export const PROJECT_SCHEMA_VERSION = 1;
4
4
  export const OMP_RUNTIME_VERSION = "17.2.7";
5
5
 
@@ -629,7 +629,7 @@ function initializationInterfaceContracts(): InterfaceContract[] {
629
629
  name: "scan",
630
630
  purpose: "执行项目初始化扫描并发布上下文快照。",
631
631
  request: { action: "scan", mode: "full|fast", repositories: [{ name: "app", path: ".", role: "frontend|backend|shared|other" }], requiredKinds: FACT_KINDS, documentMode: "template|none", requestId: "stable-id" },
632
- response: { runId: "uuid", snapshotId: "uuid", completeness: "complete|partial", diagnostics: [], documents: [{ targetPath: ".context/project/api.md", status: "complete|conflict|failed" }] },
632
+ response: { runId: "uuid", snapshotId: "uuid", completeness: "complete|partial", database: "<control.db>", projectId: "uuid", workspaceId: "uuid", planningRoot: ".planning", diagnostics: [], documents: [{ targetPath: ".context/project/api.md", status: "complete|conflict|failed" }] },
633
633
  reads: ["source tree", ".devflow/project.json"],
634
634
  writes: ["control.db", ".planning/code-index/", ".planning/document-index.json", ".planning/knowledge-index.json", ".context/", ".planning/PROJECT.md"],
635
635
  },
@@ -932,6 +932,7 @@ function renderKnowledgeDocuments(context: ProjectContext, facts: Fact[], intro:
932
932
  const generation = codeIndexGeneration(snapshot);
933
933
  const outputs: Array<{ targetPath: string; body: string }> = [];
934
934
  outputs.push({ targetPath: ".context/index.md", body: `${frontmatter({ type: "knowledge-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# ${context.manifest.name} 项目知识库\n\n## 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](/project/${type}.md)`).join("\n")}\n\n## 需求级文档\n\n- 新需求使用 [.context/requirements/_template/index.md](/requirements/_template/index.md) 作为文档包模板。\n\n## 机器索引\n\n- 文档索引:\`.planning/document-index.json\`\n- 代码索引:\`.planning/code-index/\`\n` });
935
+ outputs.push({ targetPath: ".context/spec/index.md", body: `${frontmatter({ type: "compat-spec-index", scope: "project", project: context.manifest.name, status: "compat", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 兼容 Spec 入口\n\n本文件是项目初始化的兼容入口,用于旧流程查找 \`.context/spec/index.md\`。新的权威入口如下:\n\n- 项目知识库:[../index.md](../index.md)\n- 项目级文档:[../project/index.md](../project/index.md)\n- OpenWiki 风格知识层:[../knowledge/index.md](../knowledge/index.md)\n- 初始化接口目录:[../knowledge/interfaces/index.md](../knowledge/interfaces/index.md)\n- 文档索引:\`.planning/document-index.json\`\n- 知识索引:\`.planning/knowledge-index.json\`\n- 接口索引:\`.planning/interface-index.json\`\n- 代码索引:\`.planning/code-index/\`\n\n此兼容入口不表示需求、设计或 API 已批准;人工确认前所有生成内容保持 draft/compat 状态。\n` });
935
936
  outputs.push({ targetPath: ".context/project/index.md", body: `${frontmatter({ type: "project-document-index", scope: "project", project: context.manifest.name, status: "draft", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 项目级文档\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type}.md)`).join("\n")}\n` });
936
937
  for (const type of KNOWLEDGE_DOCUMENT_TYPES) outputs.push({ targetPath: `.context/project/${type}.md`, body: projectKnowledgeBody(context, type, facts, intro, snapshotId, knowledge, generation) });
937
938
  outputs.push({ targetPath: ".context/requirements/_template/index.md", body: `${frontmatter({ type: "requirement-document-index", scope: "requirement", project: context.manifest.name, requirement_id: "REQ-TEMPLATE", status: "template", source_snapshot: snapshotId, code_index_generation: generation, managed: true })}# 需求级文档模板\n\n${KNOWLEDGE_DOCUMENT_TYPES.map(type => `- [${documentTitle(type)}](./${type === "requirements" ? "requirement" : type}.md)`).join("\n")}\n` });
@@ -10,7 +10,17 @@ const TEXT_EXTENSIONS = new Set([".ts", ".tsx", ".js", ".jsx", ".py", ".rb", ".g
10
10
 
11
11
  type SourceFile = { repository: string; path: string; absolute: string; contentHash: string; size: number; text?: string };
12
12
  type ScanDiagnostic = { repository: string; path: string; reason: "symlink" | "excluded" | "too_large" | "unreadable"; message?: string };
13
- export type InitializationProgress = { runId: string; snapshotId?: string; completeness?: "complete" | "partial"; diagnostics: string[]; documents?: Array<{ targetPath: string; status: string; outputPath?: string; error?: string }> };
13
+ export type InitializationProgress = {
14
+ runId: string;
15
+ snapshotId?: string;
16
+ completeness?: "complete" | "partial";
17
+ diagnostics: string[];
18
+ documents?: Array<{ targetPath: string; status: string; outputPath?: string; error?: string }>;
19
+ database: string;
20
+ projectId: string;
21
+ workspaceId: string;
22
+ planningRoot: string;
23
+ };
14
24
 
15
25
  function digest(value: string | Uint8Array): string { return `sha256:${createHash("sha256").update(value).digest("hex")}`; }
16
26
  function extension(file: string): string { return path.extname(file).toLowerCase(); }
@@ -189,7 +199,15 @@ export async function runProjectInitialization(context: ProjectContext, input: {
189
199
  const inputSnapshot = store.createInputSnapshot(workspace.id, storedConfig.id, manifest, false);
190
200
  const run = store.startRun({ workspaceId: workspace.id, configId: storedConfig.id, inputSnapshotId: inputSnapshot.id, requestId: input.requestId });
191
201
  activeRun = run;
192
- if (run.reused) return { runId: run.id, snapshotId: store.getRun(run.id)?.publishedSnapshotId as string | undefined, diagnostics };
202
+ if (run.reused) return {
203
+ runId: run.id,
204
+ snapshotId: store.getRun(run.id)?.publishedSnapshotId as string | undefined,
205
+ diagnostics,
206
+ database: context.controlDatabasePath,
207
+ projectId: context.manifest.projectId,
208
+ workspaceId: context.workspaceId,
209
+ planningRoot: context.planningRoot,
210
+ };
193
211
  const grouped = collect(files, context, config); const required = new Set(config.requiredKinds ?? FACT_KINDS);
194
212
  for (const repository of config.repositories) for (const kind of FACT_KINDS) {
195
213
  const repositoryFacts = grouped.get(kind)!.filter(fact => fact.repository === repository.name);
@@ -241,7 +259,17 @@ export async function runProjectInitialization(context: ProjectContext, input: {
241
259
  documents = [{ targetPath: ".planning/initialization/report.md", status: "failed", error: message }];
242
260
  }
243
261
  }
244
- return { runId: run.id, snapshotId: published.snapshotId, completeness: result.snapshot.completeness, diagnostics, documents };
262
+ return {
263
+ runId: run.id,
264
+ snapshotId: published.snapshotId,
265
+ completeness: result.snapshot.completeness,
266
+ diagnostics,
267
+ documents,
268
+ database: context.controlDatabasePath,
269
+ projectId: context.manifest.projectId,
270
+ workspaceId: context.workspaceId,
271
+ planningRoot: context.planningRoot,
272
+ };
245
273
  } catch (error) {
246
274
  const message = error instanceof Error ? error.message : String(error);
247
275
  diagnostics.push(message);