Skip to content
46 changes: 23 additions & 23 deletions docs/prompting_guide.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ When an architecture.json entry includes a `context_urls` array, the `generate_p

```json
"context_urls": [
{"url": "https://fastapi.tiangolo.com/tutorial/first-steps/", "purpose": "FastAPI routing patterns"}
{{"url": "https://fastapi.tiangolo.com/tutorial/first-steps/", "purpose": "FastAPI routing patterns"}}
]
```

Expand Down Expand Up @@ -312,50 +312,50 @@ The `<pdd-interface>` tag supports four interface types, matching the architectu

**Module Interface** (Python modules with functions):
```json
{
{{
"type": "module",
"module": {
"module": {{
"functions": [
{"name": "func_name", "signature": "(arg1, arg2)", "returns": "Type"}
{{"name": "func_name", "signature": "(arg1, arg2)", "returns": "Type"}}
]
}
}
}}
}}
```

**CLI Interface** (Command-line interfaces):
```json
{
{{
"type": "cli",
"cli": {
"cli": {{
"commands": [
{"name": "cmd_name", "description": "What it does"}
{{"name": "cmd_name", "description": "What it does"}}
]
}
}
}}
}}
```

**Command Interface** (PDD commands):
```json
{
{{
"type": "command",
"command": {
"command": {{
"commands": [
{"name": "cmd_name", "description": "What it does"}
{{"name": "cmd_name", "description": "What it does"}}
]
}
}
}}
}}
```

**Frontend Interface** (UI pages):
```json
{
{{
"type": "frontend",
"frontend": {
"frontend": {{
"pages": [
{"name": "page_name", "route": "/path"}
{{"name": "page_name", "route": "/path"}}
]
}
}
}}
}}
```

### Sync Workflow
Expand Down Expand Up @@ -823,9 +823,9 @@ Constraints:
- Output a unified diff only

Snippet:
export function parseUserId(input: string) {
export function parseUserId(input: string) {{
return input.trim().split(":")[1];
}
}}
```

PDD‑style prompt (source of truth):
Expand Down
4 changes: 0 additions & 4 deletions pdd/commands/generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -147,10 +147,6 @@ def generate(
if item in os.environ:
env_vars[item] = os.environ[item]

# Update os.environ for the duration of this command
# This allows the code generator and template expansion to see these values
os.environ.update(env_vars)

# 4. Call Code Generator
generated_code, is_incremental, cost, model = code_generator_main(
ctx=ctx,
Expand Down
37 changes: 36 additions & 1 deletion tests/test_commands_generate.py
Original file line number Diff line number Diff line change
Expand Up @@ -268,4 +268,39 @@ def add(a, b):
# Print success message
print(f"Successfully generated code at {output_path}")
except Exception as e:
pytest.fail(f"Real generation test failed: {e}")
pytest.fail(f"Real generation test failed: {e}")


@patch('pdd.core.cli.auto_update')
@patch('pdd.commands.generate.code_generator_main')
def test_issue_409_env_vars_do_not_pollute_os_environ(mock_main, mock_auto_update, runner, create_dummy_files, monkeypatch):
files = create_dummy_files("pollution_test.prompt")
mock_main.return_value = ('code', False, 0.0, 'model')
test_vars = {
'TEST_VAR_409': 'test_value',
'SECRET_KEY_409': 'secret123',
'API_TOKEN_409': 'token456'
}

for key in test_vars.keys():
monkeypatch.delenv(key, raising=False)
cmd_args = ["generate"]
for key, value in test_vars.items():
cmd_args.extend(["-e", f"{key}={value}"])
cmd_args.append(str(files["pollution_test.prompt"]))
result = runner.invoke(cli.cli, cmd_args)
assert result.exit_code == 0, f"Command failed: {result.output}"
call_kwargs = mock_main.call_args.kwargs
assert call_kwargs["env_vars"] == test_vars, "env_vars parameter should contain test variables"
pollution_detected = []

for key in test_vars.keys():
if key in os.environ:
pollution_detected.append(f"{key}={os.environ[key]}")

assert len(pollution_detected) == 0, (
f"BUG DETECTED (Issue #409): Environment variables persist after command completion! "
f"Polluted variables: {pollution_detected}. "
f"These variables were set via -e flag and should NOT exist in os.environ after the command. "
f"Root cause: pdd/commands/generate.py:152 calls os.environ.update(env_vars) without cleanup."
)