diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 00000000..24a83481 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,2 @@ +# Default review ownership for Collar +* @logohere diff --git a/.github/ISSUE_TEMPLATE/bug_report.md b/.github/ISSUE_TEMPLATE/bug_report.md new file mode 100644 index 00000000..45c66c4e --- /dev/null +++ b/.github/ISSUE_TEMPLATE/bug_report.md @@ -0,0 +1,26 @@ +--- +name: Bug report +about: Report a reproducible bug +labels: bug +--- + +## What happened? + +## Expected behavior + +## Reproduction steps + +1. +2. +3. + +## Environment + +- Collar version: +- OS: +- Branch/commit: + +## Notes + +- Keep reports focused and reproducible. +- If this touches agent behavior, mention the relevant `.dag` path. diff --git a/.github/ISSUE_TEMPLATE/feature_request.md b/.github/ISSUE_TEMPLATE/feature_request.md new file mode 100644 index 00000000..de2cbb91 --- /dev/null +++ b/.github/ISSUE_TEMPLATE/feature_request.md @@ -0,0 +1,18 @@ +--- +name: Feature request +about: Propose a small, focused improvement +labels: enhancement +--- + +## What problem are you solving? + +## Proposed change + +## Why this scope? + +## Alternatives considered + +## Notes + +- Prefer updating existing systems over adding new layers. +- If this affects agent behavior, include the `.dag` path involved. diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md new file mode 100644 index 00000000..cd4a804d --- /dev/null +++ b/.github/pull_request_template.md @@ -0,0 +1,11 @@ +# Pull request template + +PRs must use the repo template: + +- `.github/pull_request_template.md` + +Keep it lean: +- explain why the change exists +- note `.dag` / dotdog impact +- show tests or verification +- call out any intentional scope cuts diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index b6f58437..ea45abda 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -4,10 +4,12 @@ DAG-first. `Entity→Target:verb(card)`. ## Rules -1. **NEVER read `.dog`** — agents query `.dag` via the router -2. **DAG-path notation** — all behavioral guidance in compact format -3. **Compile before commit** — run `dag-regen.sh --all` after any `.md`/`.dog` change -4. **Test harness fallback** — simulate fresh install, verify constants load from `dags/` +1. **Query `.dag` first** — agent behavior comes from `.dag` routes, not prose +2. **Keep dotdog in the loop** — compile/spec changes through the dotdog path +3. **Use the PR template** — every PR should use `.github/pull_request_template.md` +4. **DAG-path notation** — all behavioral guidance in compact format +5. **Compile before commit** — run `dag-regen.sh --all` after any `.md`/`.dog` change +6. **Test harness fallback** — simulate fresh install, verify constants load from `dags/` ## Project Structure diff --git a/README.md b/README.md index 02fbbb9d..1aaa4197 100644 --- a/README.md +++ b/README.md @@ -88,6 +88,24 @@ collar setup # first-time setup wizard - [dotdog](https://github.com/specdog/dotdog) — `.dog` spec format + compiler - [specdog](https://github.com/specdog) — the org +- PRs use an agentic template: `.github/pull_request_template.md` + +## PR system + +Collar PRs should stay lean and agentic: + +- use `.github/pull_request_template.md` +- explain the reasoning, not just the diff +- prefer updating existing systems over adding new ones +- keep `.dag` as the first-read source for agent behavior +- avoid adding skills or layers that inflate token use unless the change clearly needs them +- include tests or verification for behavior changes + +## Governance + +- Branch protection on `main` requires PR review and a passing check +- Security issues should go through `SECURITY.md` +- Bugs and features have issue templates in `.github/ISSUE_TEMPLATE/` ## Dogfood diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 00000000..3fb13cec --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,25 @@ +# Security Policy + +## Supported Versions + +Security fixes land on the default branch and current release line. + +## Reporting a Vulnerability + +Do not open a public issue for security bugs. + +Use one of these instead: +- GitHub Security Advisory for this repository +- Direct contact with the maintainers through the private security channel + +Include: +- affected version or commit +- impact +- reproduction steps +- any suggested mitigation + +## Review Expectations + +- prefer minimal, targeted fixes +- avoid adding new systems unless the fix requires them +- keep `.dag`/dotdog paths lean when changing agent behavior diff --git a/dag_cli/main.py b/dag_cli/main.py index d3302e0b..d1ecec02 100644 --- a/dag_cli/main.py +++ b/dag_cli/main.py @@ -531,7 +531,17 @@ def _resolve_sudo_user_profile_env(name: str) -> str | None: if _cfg_path.exists(): with open(_cfg_path, encoding="utf-8") as _f: _early_cfg_raw = _yaml_early.safe_load(_f) or {} - if "DAG_REDACT_SECRETS" not in os.environ: + # Managed scope: overlay administrator-pinned values so a managed + # security.redact_secrets / network.force_ipv4 wins here too. This early + # bridge reads config.yaml directly (before load_config is usable), so + # without the overlay a managed redact_secrets toggle would be ignored. + # Fail-open via the shared helper. + try: + from hermes_cli import managed_scope + _early_cfg_raw = managed_scope.apply_managed_overlay(_early_cfg_raw) + except Exception: + pass + if "DAG_REDACT_SECRETS" not in os.environ and "HERMES_REDACT_SECRETS" not in os.environ: _early_sec_cfg = _early_cfg_raw.get("security", {}) if isinstance(_early_sec_cfg, dict): _early_redact = _early_sec_cfg.get("redact_secrets") diff --git a/dag_cli/send_cmd.py b/dag_cli/send_cmd.py index d502ac24..ea7bbe21 100644 --- a/dag_cli/send_cmd.py +++ b/dag_cli/send_cmd.py @@ -276,6 +276,14 @@ def _load_dag_env() -> None: except Exception: pass + # Managed scope: overlay administrator-pinned values before bridging to env, + # so a managed top-level scalar wins here too. Fail-open via the helper. + try: + from hermes_cli import managed_scope + raw = managed_scope.apply_managed_overlay(raw if isinstance(raw, dict) else {}) + except Exception: + pass + if not isinstance(raw, dict): return diff --git a/gateway/run.py b/gateway/run.py index d5393060..9d0c8c8e 100644 --- a/gateway/run.py +++ b/gateway/run.py @@ -1104,6 +1104,15 @@ def _reload_runtime_env_preserving_config_authority() -> None: cfg = _yaml.safe_load(f) or {} from dag_cli.config import _expand_env_vars cfg = _expand_env_vars(cfg) + # Managed scope: keep administrator-pinned values authoritative on every + # turn too. This per-turn reload re-bridges config→env, so without the + # overlay a managed agent.max_turns / timezone / redact_secrets would be + # replaced by the user's value after the first turn. Fail-open. + try: + from hermes_cli import managed_scope + cfg = managed_scope.apply_managed_overlay(cfg) + except Exception: + pass except Exception: return @@ -1126,6 +1135,17 @@ def _reload_runtime_env_preserving_config_authority() -> None: # Expand ${ENV_VAR} references before bridging to env vars. from dag_cli.config import _expand_env_vars _cfg = _expand_env_vars(_cfg) + # Managed scope: overlay administrator-pinned values BEFORE bridging to + # env vars, so a managed timezone / redact_secrets / max_turns / terminal + # setting wins over the user's value at the env layer too. This bridge + # reads config.yaml directly (not via load_config), so without the + # overlay every HERMES_*/TERMINAL_* env var below would carry the user's + # value even when an administrator pinned it. Fail-open via the helper. + try: + from hermes_cli import managed_scope + _cfg = managed_scope.apply_managed_overlay(_cfg) + except Exception: + pass # Top-level simple values (fallback only — don't override .env) for _key, _val in _cfg.items(): if isinstance(_val, (str, int, float, bool)) and _key not in os.environ: diff --git a/skills/github/github-pr-workflow/templates/pr-body-bugfix.md b/skills/github/github-pr-workflow/templates/pr-body-bugfix.md index c80f220c..c8fd32ce 100644 --- a/skills/github/github-pr-workflow/templates/pr-body-bugfix.md +++ b/skills/github/github-pr-workflow/templates/pr-body-bugfix.md @@ -1,22 +1,29 @@ ## Bug Description - - -Fixes # +- ## Root Cause - +- ## Fix - +- -- +## Why this fix -## How to Verify +- Why this approach? +- Why this scope? +- Why not a larger refactor? + +## DAG / spec impact - +- [ ] Uses existing `.dag` / dotdog systems +- [ ] No new skill unless required +- [ ] Keeps token overhead low +- [ ] Updates existing behavior instead of adding layers + +## How to Verify 1. 2. @@ -24,12 +31,12 @@ Fixes # ## Test Plan -- [ ] Added regression test for this bug +- [ ] Added regression test - [ ] Existing tests still pass - [ ] Manual verification of the fix ## Risk Assessment - - -Low / Medium / High — +- Impact: +- Rollback: +- Notes: diff --git a/skills/github/github-pr-workflow/templates/pr-body-feature.md b/skills/github/github-pr-workflow/templates/pr-body-feature.md index 495aa162..0ca0f37e 100644 --- a/skills/github/github-pr-workflow/templates/pr-body-feature.md +++ b/skills/github/github-pr-workflow/templates/pr-body-feature.md @@ -1,33 +1,40 @@ ## Summary - +- -- +## Why this change -## Motivation +- Why now? +- What user or upstream need does it address? +- Why this scope and not more? - +## DAG / spec impact -Closes # +- [ ] Uses existing `.dag` / dotdog systems +- [ ] No new skill unless required +- [ ] Keeps token overhead low +- [ ] Updates existing behavior instead of adding layers -## Changes +If any box is unchecked, explain why. - +## Changes -- +- -## Test Plan +## Reasoning - +- What was the design choice? +- What alternative was rejected? +- What did you intentionally leave out to keep Collar lean? -- [ ] Unit tests pass (`pytest`) -- [ ] Manual testing of new functionality -- [ ] No regressions in existing behavior - -## Screenshots / Examples +## Test Plan - +- [ ] Unit tests pass +- [ ] Manual verification completed +- [ ] No regression in existing behavior -## Notes for Reviewers +## Risk - +- Impact: +- Rollback: +- Notes: