dream-wf 0.1.1 → 0.1.2

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.
@@ -0,0 +1,150 @@
1
+ #!/usr/bin/env python3
2
+ # Codex PreToolUse guard。
3
+ # Codex CLI 支持 PreToolUse 阻塞式 hook,配置在 .codex/hooks.json。
4
+ # stdin 收 JSON payload,stdout 返回 permissionDecision,或 exit code 2 表示 block。
5
+
6
+ import json
7
+ import os
8
+ import re
9
+ import sys
10
+ from pathlib import Path
11
+
12
+ # Codex 的变更类 tool name:shell、apply_patch(文件编辑)。
13
+ MUTATING_TOOLS = {"Write", "Edit", "MultiEdit", "Delete", "apply_patch", "shell", "Bash"}
14
+ MUTATING_SHELL = re.compile(r"\b(rm|mv|cp|mkdir|touch|npm\s+install|pnpm\s+add|yarn\s+add|bun\s+add|git\s+commit|git\s+push)\b")
15
+
16
+
17
+ def find_root(cwd):
18
+ current = Path(cwd).resolve()
19
+ for candidate in [current, *current.parents]:
20
+ if (candidate / ".trellis").exists():
21
+ return candidate
22
+ return current
23
+
24
+
25
+ def active_tasks(root):
26
+ tasks_dir = root / ".trellis" / "tasks"
27
+ if not tasks_dir.exists():
28
+ return []
29
+
30
+ result = []
31
+ for task_json in tasks_dir.glob("*/task.json"):
32
+ try:
33
+ data = json.loads(task_json.read_text())
34
+ except Exception:
35
+ continue
36
+ if data.get("status") in {"planning", "in_progress"}:
37
+ result.append((task_json.parent, data))
38
+ return result
39
+
40
+
41
+ def is_prd_confirmed(task_dir):
42
+ prd = task_dir / "prd.md"
43
+ if not prd.exists():
44
+ return False
45
+ text = prd.read_text(errors="ignore").lower()
46
+ markers = ["prd confirmed", "confirmed: true", "status: confirmed", "用户已确认", "已确认"]
47
+ return any(marker in text for marker in markers)
48
+
49
+
50
+ def is_planning_artifact(root, tool_input):
51
+ if not isinstance(tool_input, dict):
52
+ return False
53
+
54
+ candidate = tool_input.get("path") or tool_input.get("file_path") or tool_input.get("target_file") or ""
55
+ if not candidate:
56
+ return False
57
+
58
+ try:
59
+ root_resolved = root.resolve()
60
+ file_path = Path(candidate)
61
+ if not file_path.is_absolute():
62
+ file_path = (root_resolved / file_path)
63
+ relative = file_path.resolve().relative_to(root_resolved).as_posix()
64
+ except Exception:
65
+ return False
66
+
67
+ if not relative.startswith(".trellis/tasks/"):
68
+ return False
69
+
70
+ name = Path(relative).name
71
+ return (
72
+ name in {"prd.md", "design.md", "implement.md", "implement.jsonl", "check.jsonl"}
73
+ or "/research/" in relative
74
+ )
75
+
76
+
77
+ def deny(message):
78
+ print(json.dumps({
79
+ "hookSpecificOutput": {
80
+ "hookEventName": "PreToolUse",
81
+ "permissionDecision": "deny",
82
+ "permissionDecisionReason": message
83
+ }
84
+ }))
85
+ sys.exit(0)
86
+
87
+
88
+ def allow():
89
+ print(json.dumps({
90
+ "hookSpecificOutput": {
91
+ "hookEventName": "PreToolUse",
92
+ "permissionDecision": "allow"
93
+ }
94
+ }))
95
+ sys.exit(0)
96
+
97
+
98
+ def main():
99
+ # 逃生舱:DREAM_WF_MODE=advisory 时跳过 strict 检查。
100
+ if os.environ.get("DREAM_WF_MODE", "").lower() == "advisory":
101
+ allow()
102
+
103
+ try:
104
+ payload = json.load(sys.stdin)
105
+ except Exception:
106
+ allow()
107
+
108
+ tool_name = payload.get("tool_name") or payload.get("tool", "")
109
+ tool_input = payload.get("tool_input") or payload.get("input") or {}
110
+ cwd = payload.get("cwd") or os.environ.get("CODEX_PROJECT_DIR") or os.getcwd()
111
+ root = find_root(cwd)
112
+
113
+ is_mutating = tool_name in MUTATING_TOOLS
114
+ command = tool_input.get("command", "") if isinstance(tool_input, dict) else ""
115
+ if tool_name in {"Shell", "Bash", "shell"} and MUTATING_SHELL.search(command):
116
+ is_mutating = True
117
+
118
+ if not is_mutating:
119
+ allow()
120
+
121
+ tasks = active_tasks(root)
122
+ if not tasks:
123
+ deny("dream-wf strict: mutating actions require an active Trellis task. Create or start a Trellis task first, or switch dream-wf to advisory mode (DREAM_WF_MODE=advisory).")
124
+
125
+ if is_planning_artifact(root, tool_input):
126
+ allow()
127
+
128
+ in_progress_confirmed = any(
129
+ task.get("status") == "in_progress" and is_prd_confirmed(task_dir)
130
+ for task_dir, task in tasks
131
+ )
132
+ if in_progress_confirmed:
133
+ allow()
134
+
135
+ in_progress_any = any(task.get("status") == "in_progress" for _, task in tasks)
136
+ if in_progress_any:
137
+ allow()
138
+
139
+ planning_unconfirmed = [
140
+ task_dir for task_dir, task in tasks
141
+ if task.get("status") == "planning" and not is_prd_confirmed(task_dir)
142
+ ]
143
+ if planning_unconfirmed:
144
+ deny("dream-wf strict: implementation is blocked while all active tasks are in planning and at least one PRD is not confirmed. Continue grill-me PRD clarification first. Planning artifacts under .trellis/tasks/** are allowed.")
145
+
146
+ allow()
147
+
148
+
149
+ if __name__ == "__main__":
150
+ main()
@@ -52,10 +52,12 @@ def is_planning_artifact(root, tool_input):
52
52
  return False
53
53
 
54
54
  try:
55
+ root_resolved = root.resolve()
55
56
  file_path = Path(candidate)
56
57
  if not file_path.is_absolute():
57
- file_path = (root / file_path).resolve()
58
- relative = file_path.relative_to(root).as_posix()
58
+ file_path = (root_resolved / file_path)
59
+ # 规范化符号链接,避免 /tmp vs /private/tmp 导致 relative_to 失败。
60
+ relative = file_path.resolve().relative_to(root_resolved).as_posix()
59
61
  except Exception:
60
62
  return False
61
63
 
@@ -84,6 +86,10 @@ def allow():
84
86
 
85
87
 
86
88
  def main():
89
+ # 逃生舱:DREAM_WF_MODE=advisory 时跳过 strict 检查。
90
+ if os.environ.get("DREAM_WF_MODE", "").lower() == "advisory":
91
+ allow()
92
+
87
93
  try:
88
94
  payload = json.load(sys.stdin)
89
95
  except Exception:
@@ -104,15 +110,28 @@ def main():
104
110
 
105
111
  tasks = active_tasks(root)
106
112
  if not tasks:
107
- deny("dream-wf strict: mutating actions require an active Trellis task. Create or start a Trellis task first, or explicitly switch dream-wf to advisory mode.")
108
-
109
- planning_tasks = [(task_dir, task) for task_dir, task in tasks if task.get("status") == "planning"]
110
- if planning_tasks:
111
- if is_planning_artifact(root, tool_input):
112
- allow()
113
- unconfirmed = [task for task_dir, task in planning_tasks if not is_prd_confirmed(task_dir)]
114
- if unconfirmed:
115
- deny("dream-wf strict: implementation is blocked while this task is in planning and its PRD is not confirmed. Continue grill-me PRD clarification first. Planning artifacts under .trellis/tasks/** are allowed.")
113
+ deny("dream-wf strict: mutating actions require an active Trellis task. Create or start a Trellis task first, or switch dream-wf to advisory mode (DREAM_WF_MODE=advisory).")
114
+
115
+ if is_planning_artifact(root, tool_input):
116
+ allow()
117
+
118
+ in_progress_confirmed = any(
119
+ task.get("status") == "in_progress" and is_prd_confirmed(task_dir)
120
+ for task_dir, task in tasks
121
+ )
122
+ if in_progress_confirmed:
123
+ allow()
124
+
125
+ in_progress_any = any(task.get("status") == "in_progress" for _, task in tasks)
126
+ if in_progress_any:
127
+ allow()
128
+
129
+ planning_unconfirmed = [
130
+ task_dir for task_dir, task in tasks
131
+ if task.get("status") == "planning" and not is_prd_confirmed(task_dir)
132
+ ]
133
+ if planning_unconfirmed:
134
+ deny("dream-wf strict: implementation is blocked while all active tasks are in planning and at least one PRD is not confirmed. Continue grill-me PRD clarification first. Planning artifacts under .trellis/tasks/** are allowed.")
116
135
 
117
136
  allow()
118
137
 
@@ -1,13 +1,18 @@
1
1
  import fs from 'node:fs';
2
2
  import path from 'node:path';
3
3
 
4
- const mutatingTools = new Set(['Write', 'Edit', 'MultiEdit', 'Delete']);
4
+ const mutatingTools = new Set(['Write', 'Edit', 'MultiEdit', 'Delete', 'apply_patch', 'shell']);
5
5
  const mutatingShell = /\b(rm|mv|cp|mkdir|touch|npm\s+install|pnpm\s+add|yarn\s+add|bun\s+add|git\s+commit|git\s+push)\b/;
6
6
 
7
7
  export default function dreamWfGuard() {
8
8
  return {
9
9
  name: 'dream-wf-guard',
10
- async 'tool.before'(input) {
10
+ async 'tool.execute.before'(input) {
11
+ // 逃生舱:DREAM_WF_MODE=advisory 时跳过 strict 检查。
12
+ if (process.env.DREAM_WF_MODE?.toLowerCase() === 'advisory') {
13
+ return;
14
+ }
15
+
11
16
  const toolName = input?.tool || input?.tool_name || '';
12
17
  const toolInput = input?.input || input?.tool_input || {};
13
18
  const cwd = input?.cwd || process.cwd();
@@ -21,17 +26,29 @@ export default function dreamWfGuard() {
21
26
 
22
27
  const tasks = activeTasks(root);
23
28
  if (tasks.length === 0) {
24
- throw new Error('dream-wf strict: mutating actions require an active Trellis task. Create or start a Trellis task first, or switch dream-wf to advisory mode.');
29
+ throw new Error('dream-wf strict: mutating actions require an active Trellis task. Create or start a Trellis task first, or switch dream-wf to advisory mode (DREAM_WF_MODE=advisory).');
30
+ }
31
+
32
+ // 规划产物始终允许,便于在 planning 阶段编写 prd/design 等。
33
+ if (isPlanningArtifact(root, toolInput)) {
34
+ return;
35
+ }
36
+
37
+ // 若存在已确认的 in_progress 任务,允许实现操作,不被其它 stale planning 任务阻塞。
38
+ const hasConfirmedInProgress = tasks.some(({ taskDir, task }) => task.status === 'in_progress' && isPrdConfirmed(taskDir));
39
+ if (hasConfirmedInProgress) {
40
+ return;
25
41
  }
26
42
 
27
- const hasPlanningTask = tasks.some(({ task }) => task.status === 'planning');
28
- if (hasPlanningTask && isPlanningArtifact(root, toolInput)) {
43
+ const hasAnyInProgress = tasks.some(({ task }) => task.status === 'in_progress');
44
+ if (hasAnyInProgress) {
29
45
  return;
30
46
  }
31
47
 
32
- const hasUnconfirmedPlanningTask = tasks.some(({ taskDir, task }) => task.status === 'planning' && !isPrdConfirmed(taskDir));
33
- if (hasUnconfirmedPlanningTask) {
34
- throw new Error('dream-wf strict: implementation is blocked while this task is in planning and its PRD is not confirmed. Continue grill-me PRD clarification first. Planning artifacts under .trellis/tasks/** are allowed.');
48
+ // 剩余情况:所有活跃任务都是 planning。若任一未确认 PRD,则阻塞实现。
49
+ const hasUnconfirmedPlanning = tasks.some(({ taskDir, task }) => task.status === 'planning' && !isPrdConfirmed(taskDir));
50
+ if (hasUnconfirmedPlanning) {
51
+ throw new Error('dream-wf strict: implementation is blocked while all active tasks are in planning and at least one PRD is not confirmed. Continue grill-me PRD clarification first. Planning artifacts under .trellis/tasks/** are allowed.');
35
52
  }
36
53
  }
37
54
  };
@@ -93,8 +110,9 @@ function isPlanningArtifact(root, toolInput) {
93
110
 
94
111
  let relative;
95
112
  try {
96
- const filePath = path.isAbsolute(candidate) ? candidate : path.resolve(root, candidate);
97
- relative = path.relative(root, filePath).split(path.sep).join('/');
113
+ const rootResolved = fs.realpathSync(root);
114
+ const filePath = path.isAbsolute(candidate) ? candidate : path.resolve(rootResolved, candidate);
115
+ relative = path.relative(rootResolved, fs.realpathSync(filePath)).split(path.sep).join('/');
98
116
  } catch {
99
117
  return false;
100
118
  }
@@ -13,6 +13,8 @@ For every software engineering request in this project, use the Dream WF profile
13
13
 
14
14
  During planning, do not start by drafting and writing a speculative PRD. Use `dream-wf-grill-prd` behavior first: inspect available context, ask exactly one high-value question at a time, provide 2-3 options and a recommended answer, then update `prd.md` after the user answers.
15
15
 
16
+ Before requesting PRD confirmation, verify technical assumptions against latest knowledge using `grok-search-mcp` (`web_search`, `web_fetch`). Record results in the `## Knowledge Verification` section of `prd.md`. Correct any outdated assumptions. Add `knowledge verified` to the PRD after verification is complete.
17
+
16
18
  Do not start implementation until the active Trellis task has a confirmed PRD. Planning artifacts such as `prd.md`, `design.md`, `implement.md`, `implement.jsonl`, `check.jsonl`, and `research/**` are allowed during planning.
17
19
 
18
20
  ## MCP Policy
@@ -0,0 +1,43 @@
1
+ <!-- DREAM-WF:START -->
2
+ # Dream WF Entry
3
+
4
+ For every software engineering request in this project, use the Dream WF profile on top of Trellis by default. The user does not need to mention Trellis or dream-wf.
5
+
6
+ ## Default Routing
7
+
8
+ - First classify the request using Trellis task classification.
9
+ - For conversation-only or tiny inline work, ask whether a Trellis task is needed only if durable tracking would help.
10
+ - For feature work, bug fixes with uncertainty, refactors, architecture decisions, multi-file changes, or any unclear request, create/use a Trellis planning task before implementation.
11
+
12
+ ## PRD First, Grill-Me Style
13
+
14
+ During planning, do not start by drafting and writing a speculative PRD. Use `dream-wf-grill-prd` behavior first: inspect available context, ask exactly one high-value question at a time, provide 2-3 options and a recommended answer, then update `prd.md` after the user answers.
15
+
16
+ Before requesting PRD confirmation, verify technical assumptions against latest knowledge using `grok-search-mcp` (`web_search`, `web_fetch`). Record results in the `## Knowledge Verification` section of `prd.md`. Correct any outdated assumptions. Add `knowledge verified` to the PRD after verification is complete.
17
+
18
+ Do not start implementation until the active Trellis task has a confirmed PRD. Planning artifacts such as `prd.md`, `design.md`, `implement.md`, `implement.jsonl`, `check.jsonl`, and `research/**` are allowed during planning.
19
+
20
+ ## MCP Policy
21
+
22
+ - Use `fast-context-mcp` for codebase semantic context.
23
+ - Use `grok-search-mcp` for external docs, live web information, and webpage fetch.
24
+ - If a preferred MCP is unavailable, state the fallback reason before using another tool.
25
+
26
+ ## Accuracy Policy
27
+
28
+ - Do not guess or fabricate facts, APIs, package behavior, release status, or external documentation when the answer is not already known from model knowledge or project context.
29
+ - When current or missing knowledge is required, actively use the preferred web search/fetch MCP tools, or ask the user for authoritative information.
30
+ - Continue searching or asking until the information is accurate enough to proceed safely.
31
+
32
+ ## Language Policy
33
+
34
+ - Write README and project documentation in Chinese.
35
+ - Write code comments in Chinese when comments are necessary.
36
+ - Avoid obvious comments; only explain non-obvious intent, constraints, or trade-offs.
37
+
38
+ ## Naming Policy
39
+
40
+ - Prefer concise file names.
41
+ - Use one word when one word clearly describes the purpose, such as `pipeline`.
42
+ - When multiple words are necessary, use lowercase snake_case, such as `paper_extract`.
43
+ <!-- DREAM-WF:END -->
@@ -25,7 +25,8 @@ Instead:
25
25
  4. Provide 2-3 options and a recommended answer.
26
26
  5. After the user answers, update `prd.md`.
27
27
  6. Repeat until blocking open questions are resolved.
28
- 7. Ask for explicit PRD confirmation before implementation.
28
+ 7. **Knowledge Verification**: before asking for PRD confirmation, verify technical assumptions against latest knowledge using `grok-search-mcp` (`web_search`, `web_fetch`). Record results in the `## Knowledge Verification` section of `prd.md`. Correct any outdated assumptions. Add `knowledge verified` to the PRD after verification is complete.
29
+ 8. Ask for explicit PRD confirmation before implementation.
29
30
 
30
31
  ## Implementation Gate
31
32
 
@@ -13,6 +13,8 @@ For every software engineering request in this project, use the Dream WF profile
13
13
 
14
14
  During planning, do not start by drafting and writing a speculative PRD. Use `dream-wf-grill-prd` behavior first: inspect available context, ask exactly one high-value question at a time, provide 2-3 options and a recommended answer, then update `prd.md` after the user answers.
15
15
 
16
+ Before requesting PRD confirmation, verify technical assumptions against latest knowledge using `grok-search-mcp` (`web_search`, `web_fetch`). Record results in the `## Knowledge Verification` section of `prd.md`. Correct any outdated assumptions. Add `knowledge verified` to the PRD after verification is complete.
17
+
16
18
  Do not start implementation until the active Trellis task has a confirmed PRD. Planning artifacts such as `prd.md`, `design.md`, `implement.md`, `implement.jsonl`, `check.jsonl`, and `research/**` are allowed during planning.
17
19
 
18
20
  ## MCP Policy
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  name: dream-wf-grill-prd
3
3
  description: |
4
- Use during Trellis planning when creating or refining a PRD. Applies grill-me style clarification: ask one question at a time, inspect code before asking, provide options and a recommended answer, update prd.md after each decision, and require PRD confirmation before implementation.
4
+ Use during Trellis planning when creating or refining a PRD. Applies grill-me style clarification: ask one question at a time, inspect code before asking, provide options and a recommended answer, update prd.md after each decision, verify technical facts against latest knowledge with grok-search-mcp before confirmation, and require PRD confirmation before implementation.
5
5
  ---
6
6
 
7
7
  # Dream WF Grill PRD
@@ -33,7 +33,52 @@ If the task is a small inline change and the user declined Trellis task creation
33
33
  8. Move confirmed answers into `Requirements`, `Acceptance Criteria`, `Decisions`, `Technical Notes`, or `Out of Scope`.
34
34
  9. Add spec candidates when a user answer or design decision should become a project convention.
35
35
  10. Continue until no blocking open questions remain.
36
- 11. Show the complete PRD and ask for explicit confirmation before implementation starts.
36
+ 11. **Knowledge Verification** before asking the user for PRD confirmation, perform a knowledge verification pass (see below).
37
+ 12. Show the complete PRD (including the Knowledge Verification section) and ask for explicit confirmation before implementation starts.
38
+
39
+ ## Knowledge Verification
40
+
41
+ Before requesting PRD confirmation, verify that the technical assumptions in the PRD are aligned with the latest knowledge. This prevents executing on outdated or incorrect information.
42
+
43
+ ### When to Verify
44
+
45
+ Identify technical points in the PRD that could be outdated or wrong:
46
+
47
+ - API names, signatures, or behavior of external packages or services.
48
+ - Framework or library version-specific behavior, deprecations, or breaking changes.
49
+ - Tool configuration formats, hook event names, or feature flags.
50
+ - Platform-specific conventions (e.g., Codex hook events, Cursor hooks.json format, Claude Code PreToolUse schema).
51
+ - Release status or availability of packages, features, or APIs.
52
+ - Any fact that the PRD relies on which, if wrong, would invalidate the plan.
53
+
54
+ ### How to Verify
55
+
56
+ 1. Prefer `grok-search-mcp` (`web_search`, `web_fetch`) for external docs, live technical information, and release notes.
57
+ 2. If `grok-search-mcp` is unavailable, state the fallback reason before using another web tool.
58
+ 3. For each verified point, record: what was searched, the source, and whether the PRD assumption was confirmed or corrected.
59
+ 4. If a search reveals that a PRD assumption is wrong or outdated, update the relevant PRD section immediately and note the correction.
60
+ 5. If a technical point cannot be verified (no reliable source found), flag it as an open question rather than assuming it is correct.
61
+
62
+ ### Recording Results
63
+
64
+ Write results in the `## Knowledge Verification` section of `prd.md`:
65
+
66
+ ```markdown
67
+ ## Knowledge Verification
68
+
69
+ - Verified: <what was checked>
70
+ Search: <query or topic searched>
71
+ Source: <URL or reference>
72
+ Result: confirmed | corrected | inconclusive
73
+ Correction: <if corrected, what changed>
74
+ ```
75
+
76
+ ### Stop Condition for Verification
77
+
78
+ - All identified technical risk points have been searched and recorded, OR
79
+ - Remaining unverified points have been moved to `Open Questions` with a clear note that they need verification before implementation.
80
+
81
+ Only after this step is complete, add `knowledge verified` to the PRD and ask the user for final confirmation.
37
82
 
38
83
  ## PRD Structure
39
84
 
@@ -65,6 +110,13 @@ Use or preserve these sections:
65
110
  ## Technical Notes
66
111
  - ...
67
112
 
113
+ ## Knowledge Verification
114
+ - Verified: ...
115
+ Search: ...
116
+ Source: ...
117
+ Result: confirmed | corrected | inconclusive
118
+ Correction: ...
119
+
68
120
  ## Spec Candidates
69
121
  - Candidate: ...
70
122
  Evidence: ...
@@ -12,11 +12,24 @@ This project uses `dream-wf` on top of Trellis.
12
12
  - Inspect code, docs, config, existing specs, and task history before asking the user.
13
13
  - Update `prd.md` only after each confirmed answer, confirmed existing fact, or explicit decision.
14
14
  - Treat task creation consent and implementation approval as separate gates.
15
+ - **Before PRD confirmation, verify technical assumptions against latest knowledge using `grok-search-mcp` (`web_search`, `web_fetch`).** Record verification results in the `## Knowledge Verification` section of `prd.md`. If a search reveals an outdated assumption, correct it immediately. If a point cannot be verified, move it to `Open Questions`.
15
16
  - Do not start implementation until the PRD is confirmed.
16
17
  - Write README and project documentation in Chinese.
17
18
  - Write code comments in Chinese when comments are necessary, and avoid obvious comments.
18
19
  - Prefer concise file names: one word when clear, or lowercase snake_case for necessary multi-word names.
19
20
 
21
+ ## Knowledge Verification
22
+
23
+ Before requesting PRD confirmation, identify technical risk points that could be outdated or wrong, and verify them with `grok-search-mcp`:
24
+
25
+ - API names, signatures, behavior of external packages or services.
26
+ - Framework or library version-specific behavior, deprecations, or breaking changes.
27
+ - Tool configuration formats, hook event names, or feature flags.
28
+ - Platform-specific conventions (e.g., Codex hook events, Cursor hooks.json format, Claude Code PreToolUse schema).
29
+ - Release status or availability of packages, features, or APIs.
30
+
31
+ Record each verification in `prd.md` under `## Knowledge Verification`. Add `knowledge verified` to the PRD after all risk points have been verified or moved to `Open Questions`.
32
+
20
33
  ## Initial Spec Candidates
21
34
 
22
35
  During bootstrap or planning, spec candidates may come from:
@@ -25,6 +38,7 @@ During bootstrap or planning, spec candidates may come from:
25
38
  - PRD decisions.
26
39
  - Design decisions.
27
40
  - Verified codebase facts.
41
+ - Knowledge verification results (confirmed or corrected).
28
42
  - Existing tests, configs, docs, and conventions.
29
43
 
30
44
  Initial spec candidates require user review before they become stable project conventions.