From fa19ca9e93c063ea55e9fe09a38d60670e259cad Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Sun, 4 Oct 2026 12:02:48 +0530 Subject: [PATCH 1/5] docs: describe demo.script as an executable path --- docs/python-manifest.md | 14 ++++++++++---- 1 file changed, 10 insertions(+), 4 deletions(-) diff --git a/docs/python-manifest.md b/docs/python-manifest.md index 9e999c13..c0c2fed6 100644 --- a/docs/python-manifest.md +++ b/docs/python-manifest.md @@ -248,10 +248,16 @@ clearly when `runner: uv` is selected and `uv` is not on `PATH`. Command strings remain trusted project code with or without a runner. Base does not parse them into a restricted argument array; shell syntax in -`test.command`, `commands.*.command`, `build.targets.*.command`, and -`demo.script` is project-owned behavior. Review manifests from unfamiliar -repositories before running them, and use `--dry-run` or listing commands first -when you only need to inspect the resolved invocation. +`test.command`, `commands.*.command`, and `build.targets.*.command` is +project-owned behavior. `demo.script` is different: it must be a relative, +project-owned executable file path, not a shell command string. Base validates +that the file exists, is executable, and stays inside the project root before +running it. See the [Project Demo Workflow](project-demo-workflow.md) for the +complete `demo.script` contract and passthrough argument behavior. + +Review manifests from unfamiliar repositories before running project-owned +commands, and use `--dry-run` or listing commands first when you only need to +inspect the resolved invocation. ## Relationship To `pyproject.toml` From 2fcd344058a5dd40f1dc3401eb6a352ebee49295 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Mon, 5 Oct 2026 15:09:55 +0530 Subject: [PATCH 2/5] docs: clarify demo script review guidance --- docs/python-manifest.md | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/python-manifest.md b/docs/python-manifest.md index c0c2fed6..295edeaa 100644 --- a/docs/python-manifest.md +++ b/docs/python-manifest.md @@ -256,8 +256,8 @@ running it. See the [Project Demo Workflow](project-demo-workflow.md) for the complete `demo.script` contract and passthrough argument behavior. Review manifests from unfamiliar repositories before running project-owned -commands, and use `--dry-run` or listing commands first when you only need to -inspect the resolved invocation. +commands or demo scripts, and use `--dry-run` or listing commands first when +you only need to inspect the resolved invocation. ## Relationship To `pyproject.toml` From 98ffef209c5a1bc313dfc31851618dda8012f7ee Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:03:40 +0530 Subject: [PATCH 3/5] docs: distinguish demo paths from command strings --- docs/command-reference.md | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/docs/command-reference.md b/docs/command-reference.md index 9b3ca123..35e11fcc 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -410,9 +410,10 @@ Base responsible for participation semantics, readiness diagnostics, explicit execution trust, lifecycle guidance, onboarding, and handoff evidence. Project environments and command execution remain owned by their declared substrates. -Manifest-declared commands are trusted project code. Base executes -`test.command`, `build.targets.*.command`, `commands.*`, `demo.script`, and -`activate.source` entries from the project root. Review manifests from +Manifest-declared execution entries are trusted project code. Base executes +the command strings in `test.command`, `build.targets.*.command`, and +`commands.*`, the executable path in `demo.script`, and the source path in +`activate.source` from the project root. Review manifests from unfamiliar repositories before running `basectl test`, `basectl build`, `basectl run`, `basectl demo`, or manifest-backed `basectl activate`; use `--dry-run` and `--list` first for command surfaces that support read-only From b9d3a1c04fa8ad3869300049a98601deaab283f7 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Mon, 5 Oct 2026 16:04:51 +0530 Subject: [PATCH 4/5] docs: clarify demo script safety in command reference --- docs/command-reference.md | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/docs/command-reference.md b/docs/command-reference.md index 35e11fcc..63f82f3d 100644 --- a/docs/command-reference.md +++ b/docs/command-reference.md @@ -412,8 +412,11 @@ environments and command execution remain owned by their declared substrates. Manifest-declared execution entries are trusted project code. Base executes the command strings in `test.command`, `build.targets.*.command`, and -`commands.*`, the executable path in `demo.script`, and the source path in -`activate.source` from the project root. Review manifests from +`commands.*`, the executable file path in `demo.script`, and the source path in +`activate.source` from the project root. `demo.script` is not a shell command +string: Base validates that it is a relative, executable file inside the +project root before running it. See the [Project Demo Workflow](project-demo-workflow.md) +for the complete `demo.script` contract. Review manifests from unfamiliar repositories before running `basectl test`, `basectl build`, `basectl run`, `basectl demo`, or manifest-backed `basectl activate`; use `--dry-run` and `--list` first for command surfaces that support read-only From 7716ecfca35df1571c08278642d59a6d778320c4 Mon Sep 17 00:00:00 2001 From: Ramesh Padmanabhaiah <22363102+codeforester@users.noreply.github.com> Date: Mon, 5 Oct 2026 18:28:38 +0530 Subject: [PATCH 5/5] docs: complete demo script contract guidance --- docs/contracts.md | 1 + docs/project-demo-workflow.md | 9 ++++++++- docs/python-manifest.md | 8 ++++++-- tests/test_contract_hardening.py | 1 + 4 files changed, 16 insertions(+), 3 deletions(-) diff --git a/docs/contracts.md b/docs/contracts.md index d676e0ed..30777cc1 100644 --- a/docs/contracts.md +++ b/docs/contracts.md @@ -20,6 +20,7 @@ before treating the fix as complete. | Base-owned remote shell installer policy | [Remote Installer Policy](remote-installer-policy.md), `cli/python/base_setup/remote_installers.py`, standalone Homebrew entry points | `tests/test_remote_installer_policy.py`, `cli/python/base_setup/tests/test_remote_installers.py`, focused Homebrew BATS tests | A Base-owned installer bypasses the registry, its documented URL drifts, or a managed uv/mise override executes unverified or different bytes | Security | | CLI local log file privacy | [Local Observability](observability.md), [`base_cli/logging.py`](https://github.com/basefoundry/base-cli/blob/main/lib/python/base_cli/logging.py) | [`base-cli/tests/test_logging.py`](https://github.com/basefoundry/base-cli/blob/main/tests/test_logging.py) | Persistent CLI log files are created with permissive permissions, exposing command details before Base can restrict them | Security | | CLI docs, help, and completion drift | [Command Quick Reference](command-reference.md), `.ai-context/COMMANDS.md`, `bin/basectl`, shell completion scripts | `cli/bash/commands/basectl/tests/docs.bats`, `cli/bash/commands/basectl/tests/help.bats`, `cli/bash/commands/basectl/tests/completions.bats` | Public help, docs shortcut behavior, command reference, AI context, or completions no longer match the shipped command surface | CLI | +| Project demo script contract | [Project Demo Workflow](project-demo-workflow.md), [Python Manifest](python-manifest.md), `cli/python/base_setup/demo.py` | `cli/bash/commands/basectl/tests/demo.bats` | A declared `demo.script` is treated as a shell command, escapes the project root, accepts an invalid path, or loses passthrough arguments | CLI | | Public command and JSON stability tiers | [Stability Tiers](stability-tiers.md), [Command Quick Reference](command-reference.md), [Doctor Finding IDs](doctor-findings.md) | `tests/stability_compatibility.py`, `tests/test_stability_compatibility.py`, `tests/test_stability_tiers_docs.py` | Public command tiers, source-owned help contracts, JSON compatibility rules, or stable finding ID guarantees drift from the accepted v1.8.0 provenance baseline | Product | | Workspace agent-brief JSON schema | [Workspace Manifest](workspace-manifest.md), [`schemas/workspace-agent-brief.json`](schemas/workspace-agent-brief.json) | `tests/contracts/test_workspace_agent_brief_schema.py` | `basectl workspace agent-brief --format json` no longer conforms to its published schema | Workspace | | Workspace update JSON schema | [Workspace Manifest](workspace-manifest.md), [`schemas/workspace-update.json`](schemas/workspace-update.json) | `tests/contracts/test_workspace_update_schema.py` | `basectl workspace update --format json` no longer conforms to its published schema | Workspace | diff --git a/docs/project-demo-workflow.md b/docs/project-demo-workflow.md index fc48ec10..4464f4b3 100644 --- a/docs/project-demo-workflow.md +++ b/docs/project-demo-workflow.md @@ -34,7 +34,14 @@ demo: `demo.script` is required when `demo` is present. It must be a non-empty string, relative to the project root, stay inside the project, point to a file, and be -executable. +executable. Paths may contain spaces; quote the YAML value, for example +`script: "./demo/demo walkthrough.sh"`. + +Keep compound shell logic inside that executable script rather than in +`demo.script`. For example, `demo/demo walkthrough.sh` can run +`./build.sh && ./serve.sh`; pass demo options after `--`, such as +`basectl demo base-demo -- --non-interactive`, and Base forwards them to the +script unchanged. `demo.description` is optional. It is human-facing metadata for documentation and future listing surfaces; Base does not currently display it in command diff --git a/docs/python-manifest.md b/docs/python-manifest.md index 295edeaa..174892cf 100644 --- a/docs/python-manifest.md +++ b/docs/python-manifest.md @@ -252,8 +252,12 @@ does not parse them into a restricted argument array; shell syntax in project-owned behavior. `demo.script` is different: it must be a relative, project-owned executable file path, not a shell command string. Base validates that the file exists, is executable, and stays inside the project root before -running it. See the [Project Demo Workflow](project-demo-workflow.md) for the -complete `demo.script` contract and passthrough argument behavior. +running it. Paths may contain spaces when quoted in the YAML value, such as +`script: "./demo/demo walkthrough.sh"`. Put compound shell logic inside that +script rather than in `demo.script`; arguments after `--`, such as +`basectl demo base-demo -- --non-interactive`, are passed to the script +unchanged. See the [Project Demo Workflow](project-demo-workflow.md) for the +complete `demo.script` contract. Review manifests from unfamiliar repositories before running project-owned commands or demo scripts, and use `--dry-run` or listing commands first when diff --git a/tests/test_contract_hardening.py b/tests/test_contract_hardening.py index 196c0bef..3d1af988 100644 --- a/tests/test_contract_hardening.py +++ b/tests/test_contract_hardening.py @@ -260,6 +260,7 @@ def test_contract_registry_rows_have_complete_enforcement_metadata() -> None: "Workspace test JSON schema", "Workspace update JSON schema", "Native Windows support boundary", + "Project demo script contract", } for row in rows: assert row["Source of truth"], row