From fd52b3344469cc0e147ae6c6ea747463498d1299 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:31:35 -0400 Subject: [PATCH 01/35] fix: make scoring module importable from clean checkout --- scoring.py | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/scoring.py b/scoring.py index 775572d..cda292b 100644 --- a/scoring.py +++ b/scoring.py @@ -1,6 +1,7 @@ from datetime import date -from typing import List, Dict -from .models import Task, ProfileConfig, ScoredTask +from typing import Dict, List + +from models import ProfileConfig, ScoredTask, Task def _normalize(value: float, min_value: float, max_value: float) -> float: @@ -20,9 +21,11 @@ def _stress_penalty(stress_impact: str) -> float: def score_tasks(tasks: List[Task], profile: ProfileConfig) -> List[ScoredTask]: - """Score tasks for a given profile. + """Score tasks using an inspectable heuristic for a given profile. - This is a simple, transparent scoring model meant as a solid starting point. + The result is decision support, not an autonomous or high-stakes decision. + Callers remain responsible for reviewing deadlines, safety constraints, + accessibility needs, and other context the heuristic does not represent. """ weights: Dict[str, float] = { @@ -73,5 +76,5 @@ def score_tasks(tasks: List[Task], profile: ProfileConfig) -> List[ScoredTask]: scored.append(ScoredTask(task=task, score=final_score, breakdown=breakdown)) - scored.sort(key=lambda st: st.score, reverse=True) + scored.sort(key=lambda scored_task: scored_task.score, reverse=True) return scored From ae64a953ef6e9cc452ead8ba642cf491f0aa404c Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:31:53 -0400 Subject: [PATCH 02/35] fix: make selector module importable and document limits --- selectors.py | 19 +++++++++---------- 1 file changed, 9 insertions(+), 10 deletions(-) diff --git a/selectors.py b/selectors.py index b7a1d13..f6eb8af 100644 --- a/selectors.py +++ b/selectors.py @@ -1,16 +1,16 @@ from datetime import datetime from typing import List -from .models import ScoredTask, ProfileConfig, Plan, Task + +from models import Plan, ProfileConfig, ScoredTask, Task def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> Plan: - """Turn scored tasks into a daily plan. + """Build a bounded plan from tasks already ordered by score. - Strategy: - - Walk tasks in order of score. - - Add tasks to big_focus until we hit max_big_focus or run out. - - Add additional tasks as support if there is budget left. - - Anything that does not fit within the effort budget is parked. + The selector respects the configured effort budget and focus-task limit. + It does not understand calendar conflicts, dependencies, safety-critical + obligations, or consequences beyond the fields supplied by the caller. + A human should review the resulting plan before acting on it. """ effort_budget = profile.daily_effort_budget_hours @@ -20,7 +20,7 @@ def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> support_tasks: List[Task] = [] parked_tasks: List[Task] = [] - for idx, scored in enumerate(scored_tasks): + for scored in scored_tasks: task = scored.task task_effort = max(0.0, task.effort_estimate) @@ -40,7 +40,7 @@ def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> f"Parked {len(parked_tasks)} task(s)." ) - plan = Plan( + return Plan( profile_name=profile.name, timestamp=datetime.utcnow(), big_focus=big_focus, @@ -48,4 +48,3 @@ def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> parked_tasks=parked_tasks, decision_summary=summary, ) - return plan From d355b9cdfa3712cf4ce0b51c4db4d1c505743788 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:32:31 -0400 Subject: [PATCH 03/35] fix: restore CLI imports, profile paths, and input validation --- dailypilot_cli.py | 117 +++++++++++++++++++++++++++++----------------- 1 file changed, 74 insertions(+), 43 deletions(-) diff --git a/dailypilot_cli.py b/dailypilot_cli.py index ed6707b..2565148 100644 --- a/dailypilot_cli.py +++ b/dailypilot_cli.py @@ -1,67 +1,103 @@ import argparse import json -import os -from datetime import datetime +from datetime import date, timezone +from pathlib import Path from typing import List -from engine.core.models import Task, ProfileConfig, RunLog -from engine.core.scoring import score_tasks -from engine.core.selectors import build_daily_plan +from models import ProfileConfig, RunLog, Task +from scoring import score_tasks +from selectors import build_daily_plan + +BASE_DIR = Path(__file__).resolve().parent def load_profile(profile_name: str) -> ProfileConfig: - base_dir = os.path.join(os.path.dirname(__file__), "..", "..", "engine", "profiles") - path = os.path.join(base_dir, f"{profile_name}.json") - if not os.path.exists(path): + path = BASE_DIR / f"{profile_name}.json" + if not path.exists(): raise SystemExit(f"Profile not found: {profile_name} (expected {path})") - with open(path, "r", encoding="utf-8") as f: - data = json.load(f) + with path.open("r", encoding="utf-8") as profile_file: + data = json.load(profile_file) + + if not isinstance(data, dict): + raise SystemExit(f"Profile must be a JSON object: {path}") + return ProfileConfig(**data) def load_tasks(path: str) -> List[Task]: - with open(path, "r", encoding="utf-8") as f: - raw = json.load(f) + task_path = Path(path) + with task_path.open("r", encoding="utf-8") as task_file: + raw = json.load(task_file) + + if not isinstance(raw, list): + raise SystemExit(f"Task input must be a JSON array: {task_path}") tasks: List[Task] = [] - for item in raw: - tasks.append(Task(**item)) + for index, item in enumerate(raw): + if not isinstance(item, dict): + raise SystemExit(f"Task at index {index} must be a JSON object") + + task_data = dict(item) + due_date = task_data.get("due_date") + if isinstance(due_date, str): + try: + task_data["due_date"] = date.fromisoformat(due_date) + except ValueError as exc: + raise SystemExit( + f"Task at index {index} has an invalid due_date; use YYYY-MM-DD" + ) from exc + + tasks.append(Task(**task_data)) + return tasks +def _utc_timestamp(timestamp) -> str: + if timestamp.tzinfo is None: + timestamp = timestamp.replace(tzinfo=timezone.utc) + return timestamp.astimezone(timezone.utc).isoformat().replace("+00:00", "Z") + + def save_log(plan, profile: ProfileConfig, log_dir: str = "logs") -> str: - os.makedirs(log_dir, exist_ok=True) + log_directory = Path(log_dir) + log_directory.mkdir(parents=True, exist_ok=True) + log = RunLog( timestamp=plan.timestamp, profile_name=profile.name, input_task_count=len(plan.big_focus) + len(plan.support_tasks) + len(plan.parked_tasks), - big_focus_ids=[t.id for t in plan.big_focus], + big_focus_ids=[task.id for task in plan.big_focus], engine_version=plan.engine_version, decision_summary=plan.decision_summary, ) + record = { - "timestamp": log.timestamp.isoformat() + "Z", + "timestamp": _utc_timestamp(log.timestamp), "profile": log.profile_name, "input_task_count": log.input_task_count, "big_focus_ids": log.big_focus_ids, "engine_version": log.engine_version, "decision_summary": log.decision_summary, } + filename = f"dailypilot_run_{log.timestamp.strftime('%Y%m%dT%H%M%S')}.json" - path = os.path.join(log_dir, filename) - with open(path, "w", encoding="utf-8") as f: - json.dump(record, f, indent=2) - return path + path = log_directory / filename + with path.open("w", encoding="utf-8") as log_file: + json.dump(record, log_file, indent=2) + + return str(path) def main() -> None: - parser = argparse.ArgumentParser(description="Run DailyPilot-Engine on a JSON task file.") - parser.add_argument("tasks", help="Path to a JSON file containing a list of tasks.") + parser = argparse.ArgumentParser( + description="Run the DailyPilot reference engine on a JSON task file." + ) + parser.add_argument("tasks", help="Path to a JSON array of tasks") parser.add_argument( "--profile", default="worker_double_shift", - help="Profile name to use (default: worker_double_shift).", + help="Profile JSON name without extension (default: worker_double_shift)", ) args = parser.parse_args() @@ -71,34 +107,29 @@ def main() -> None: scored = score_tasks(tasks, profile) plan = build_daily_plan(scored, profile) - print("") - print("=== DailyPilot-Engine Plan ===") + print("\n=== DailyPilot-Engine Plan ===") print(f"Profile: {plan.profile_name}") - print(f"Timestamp (UTC): {plan.timestamp.isoformat()}Z") - print("") - print("Today’s Focus:") - for t in plan.big_focus: - print(f"- {t.title} (id={t.id})") + print(f"Timestamp (UTC): {_utc_timestamp(plan.timestamp)}") + + print("\nToday’s Focus:") + for task in plan.big_focus: + print(f"- {task.title} (id={task.id})") if plan.support_tasks: - print("") - print("Support Tasks:") - for t in plan.support_tasks: - print(f"- {t.title} (id={t.id})") + print("\nSupport Tasks:") + for task in plan.support_tasks: + print(f"- {task.title} (id={task.id})") if plan.parked_tasks: - print("") - print("Parked Tasks:") - for t in plan.parked_tasks: - print(f"- {t.title} (id={t.id})") + print("\nParked Tasks:") + for task in plan.parked_tasks: + print(f"- {task.title} (id={task.id})") - print("") - print("Summary:") + print("\nSummary:") print(plan.decision_summary) log_path = save_log(plan, profile) - print("") - print(f"Log written to: {log_path}") + print(f"\nLog written to: {log_path}") if __name__ == "__main__": From 361313cf1c8bacc4eb1eec92f6e49d054f423183 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:32:50 -0400 Subject: [PATCH 04/35] test: repair scoring imports and expand evidence --- test_scoring.py | 44 +++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 3 deletions(-) diff --git a/test_scoring.py b/test_scoring.py index d9d7195..ecf1a10 100644 --- a/test_scoring.py +++ b/test_scoring.py @@ -1,6 +1,7 @@ -import datetime -from engine.core.models import Task, ProfileConfig -from engine.core.scoring import score_tasks +from datetime import date, timedelta + +from models import ProfileConfig, Task +from scoring import score_tasks def test_higher_importance_scores_higher(): @@ -9,4 +10,41 @@ def test_higher_importance_scores_higher(): high = Task(id="high", title="High importance", importance=5, urgency=3) scored = score_tasks([low, high], profile) + assert scored[0].task.id == "high" + + +def test_nearer_deadline_scores_higher_when_other_fields_match(): + profile = ProfileConfig(name="test_profile") + due_today = Task( + id="today", + title="Due today", + due_date=date.today(), + stress_impact="LOW", + ) + due_later = Task( + id="later", + title="Due later", + due_date=date.today() + timedelta(days=14), + stress_impact="LOW", + ) + + scored = score_tasks([due_later, due_today], profile) + + assert scored[0].task.id == "today" + + +def test_score_includes_inspectable_breakdown(): + profile = ProfileConfig(name="test_profile") + task = Task(id="task", title="Task") + + result = score_tasks([task], profile)[0] + + assert set(result.breakdown) == { + "importance_n", + "urgency_n", + "effort_n", + "deadline_n", + "stress_penalty", + "base_score", + } From 4840d8a61c9256836ed6f89c29f50adf16a90c07 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:33:05 -0400 Subject: [PATCH 05/35] test: repair selector imports and add parking coverage --- test_selectors.py | 47 +++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 39 insertions(+), 8 deletions(-) diff --git a/test_selectors.py b/test_selectors.py index a8d10e9..e8eef33 100644 --- a/test_selectors.py +++ b/test_selectors.py @@ -1,17 +1,48 @@ -from engine.core.models import Task, ProfileConfig, ScoredTask -from engine.core.selectors import build_daily_plan +from models import ProfileConfig, ScoredTask, Task +from selectors import build_daily_plan -def test_big_focus_respects_budget_and_limit(): - profile = ProfileConfig(name="test_profile", daily_effort_budget_hours=3.0, max_big_focus=2) +def _scored(task_id: str, effort: float, score: float) -> ScoredTask: + return ScoredTask( + task=Task(id=task_id, title=task_id.upper(), effort_estimate=effort), + score=score, + breakdown={}, + ) + +def test_big_focus_respects_budget_and_limit(): + profile = ProfileConfig( + name="test_profile", + daily_effort_budget_hours=3.0, + max_big_focus=2, + ) tasks = [ - ScoredTask(task=Task(id="a", title="A", effort_estimate=1.0), score=1.0, breakdown={}), - ScoredTask(task=Task(id="b", title="B", effort_estimate=1.0), score=0.9, breakdown={}), - ScoredTask(task=Task(id="c", title="C", effort_estimate=2.0), score=0.8, breakdown={}), + _scored("a", 1.0, 1.0), + _scored("b", 1.0, 0.9), + _scored("c", 2.0, 0.8), ] plan = build_daily_plan(tasks, profile) + assert len(plan.big_focus) <= profile.max_big_focus - total_effort = sum(t.effort_estimate for t in plan.big_focus + plan.support_tasks) + total_effort = sum( + task.effort_estimate for task in plan.big_focus + plan.support_tasks + ) assert total_effort <= profile.daily_effort_budget_hours + 1e-6 + + +def test_tasks_that_exceed_budget_are_parked(): + profile = ProfileConfig( + name="test_profile", + daily_effort_budget_hours=1.0, + max_big_focus=1, + ) + tasks = [ + _scored("fits", 1.0, 1.0), + _scored("too_large", 2.0, 0.9), + ] + + plan = build_daily_plan(tasks, profile) + + assert [task.id for task in plan.big_focus] == ["fits"] + assert [task.id for task in plan.parked_tasks] == ["too_large"] From 5bd47c2d3b3be6f7e2adce789df2bf7cc334c853 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:33:15 -0400 Subject: [PATCH 06/35] test: validate profiles at their actual repository paths --- test_profiles.py | 32 +++++++++++++++++++++++--------- 1 file changed, 23 insertions(+), 9 deletions(-) diff --git a/test_profiles.py b/test_profiles.py index 3502b07..ff64484 100644 --- a/test_profiles.py +++ b/test_profiles.py @@ -1,12 +1,26 @@ import json -import os +from pathlib import Path -def test_profiles_are_valid_json(): - base_dir = os.path.join(os.path.dirname(__file__), "..", "engine", "profiles") - for name in os.listdir(base_dir): - if not name.endswith(".json"): - continue - path = os.path.join(base_dir, name) - with open(path, "r", encoding="utf-8") as f: - json.load(f) +PROFILE_NAMES = { + "student.json", + "worker_double_shift.json", + "founder.json", +} + + +def test_profiles_are_valid_json_objects(): + base_dir = Path(__file__).resolve().parent + discovered = {path.name for path in base_dir.glob("*.json") if path.name != "sample_day.json"} + + assert PROFILE_NAMES.issubset(discovered) + + for name in PROFILE_NAMES: + path = base_dir / name + with path.open("r", encoding="utf-8") as profile_file: + profile = json.load(profile_file) + + assert isinstance(profile, dict) + assert profile.get("name") + assert profile.get("daily_effort_budget_hours", 0) > 0 + assert profile.get("max_big_focus", 0) >= 1 From aeb34472fcd21c709b96a093291fb93cd922e82d Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:34:22 -0400 Subject: [PATCH 07/35] docs: make DailyPilot status, usage, and limitations reproducible --- README.md | 183 ++++++++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 158 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 4df383b..74c5937 100644 --- a/README.md +++ b/README.md @@ -1,49 +1,182 @@ -# DailyPilot-Engine +# DailyPilot Engine -DailyPilot-Engine is the human-centered prioritization core of **DailyPilot by AltmanAI (Altman Family Group, LLC)**. - -It transforms messy task lists and shifting schedules into a focused, realistic daily plan that feels calm instead of overwhelming — with explainable, stress-aware scoring logic, not a black box. +> A transparent reference engine for turning a task list into a bounded, reviewable daily plan. **Humanity leads. Intelligence follows.** ---- +DailyPilot Engine is a small Python reference implementation maintained by AltmanAI, an Altman Family Group LLC initiative. It demonstrates explainable task scoring and effort-budgeted plan construction without hiding the decision logic behind a black box. + +## Status + +- **Lifecycle:** Reference implementation +- **Language:** Python 3.10+ +- **Core runtime dependencies:** Python standard library only +- **Production integration:** Separate product integration work may be in progress; this repository alone is not a production service +- **Decision authority:** Human review required + +This engine is not medical, mental-health, legal, financial, employment, education, or safety-critical decision software. It does not understand the full context of a person's obligations, risks, accessibility needs, relationships, or wellbeing. + +## What it does + +1. Accepts tasks expressed through the `Task` data model. +2. Normalizes importance, urgency, effort, and deadline proximity. +3. Applies a visible stress-impact penalty. +4. Produces a score and factor breakdown for every task. +5. Builds a plan constrained by an effort budget and maximum focus-task count. +6. Parks work that does not fit within the configured budget. +7. Writes a structured local run log when the CLI is used. + +## What it does not do + +- autonomously decide what a person must do; +- guarantee that a generated plan is safe, complete, healthy, fair, or optimal; +- resolve calendar conflicts, task dependencies, travel time, emergencies, or hidden obligations; +- infer a person's mental state or diagnose stress; +- replace professional or domain-qualified judgment; +- execute external actions or access accounts, calendars, messages, or private services; +- learn from user data or send task data to an external model provider. + +The current scoring method is an inspectable heuristic. A high-stress task may be deprioritized by the configured penalty even when outside context makes it essential. Users and integrators must review results and preserve a direct correction or override path. + +## Repository layout + +```text +. +├── models.py # Task, profile, score, plan, and run-log data models +├── scoring.py # Transparent scoring heuristic and breakdown +├── selectors.py # Effort-budgeted plan construction +├── dailypilot_cli.py # Local command-line harness +├── sample_day.json # Example task input +├── student.json # Example profile +├── worker_double_shift.json # Example profile +├── founder.json # Example profile +├── test_scoring.py # Scoring behavior tests +├── test_selectors.py # Plan-selection behavior tests +└── test_profiles.py # Profile integrity tests +``` -## What this repo does +The repository currently uses a deliberately small root-module layout. Imports, tests, CLI paths, and documentation must remain consistent with that layout unless a reviewed packaging migration changes them together. -- Takes a raw set of tasks/schedule inputs (see `sample_day.json`, `student.json`, `worker_double_shift.json`, `founder.json` for example profiles) -- Scores and selects a realistic daily plan using `scoring.py` and `selectors.py` -- Exposes a data contract via `models.py` -- Ships a CLI entry point: `dailypilot_cli.py` +## Quick start -## Setup +Clone the repository and enter its directory, then run: ```bash -pip install -r requirements.txt +python dailypilot_cli.py sample_day.json --profile worker_double_shift ``` -## Run the CLI +The CLI prints the plan and writes a local JSON record under `logs/`. The `logs/` directory should not be committed. -```bash -python dailypilot_cli.py --input sample_day.json +### Input format + +The task file must be a JSON array. Each task requires `id` and `title`; other supported fields include: + +```json +{ + "id": "finish_report", + "title": "Finish client report", + "description": "Due soon and important for income.", + "importance": 5, + "urgency": 5, + "effort_estimate": 2.0, + "stress_impact": "MEDIUM", + "due_date": "2026-07-15", + "category": "work" +} ``` +`due_date`, when supplied through JSON, must use `YYYY-MM-DD`. + ## Run tests +Install the test runner in an isolated environment: + ```bash -python -m pytest test_scoring.py test_selectors.py test_profiles.py +python -m venv .venv +source .venv/bin/activate +python -m pip install --upgrade pip +python -m pip install "pytest>=8,<10" +python -m pytest -q ``` -## Docs +On Windows PowerShell, activate with: -- `overview.md` — product overview and scoring philosophy -- `architecture.md` — system architecture -- `api.md` — API contract for app integration -- `IOS_BRIDGE.md` — notes on connecting this engine to the DailyPilot iOS app (moved here from the old root README) +```powershell +.venv\Scripts\Activate.ps1 +``` -## Status +The GitHub Actions workflow runs compilation, tests, and a CLI smoke test across supported Python versions. + +## Scoring model + +Default weights: + +| Factor | Weight | Interpretation | +|---|---:|---| +| Importance | 0.40 | User-supplied significance, normalized from 1–5 | +| Urgency | 0.30 | User-supplied time pressure, normalized from 1–5 | +| Effort | 0.20 | Shorter estimated tasks receive a larger contribution | +| Deadline | 0.10 | Due and near-due tasks receive a larger contribution | + +The profile may override these weights. A stress-impact penalty is then subtracted: + +| Stress impact | Penalty | +|---|---:| +| LOW | 0.00 | +| MEDIUM | 0.10 | +| HIGH | 0.25 | + +Every result includes the normalized factors, base score, and penalty. This supports inspection but does not prove that the weights are universally appropriate. + +## Human-first integration requirements + +Any product integration should provide: + +- clear disclosure that the plan is generated assistance; +- understandable factor explanations; +- direct editing, correction, override, and dismissal controls; +- no coercive language or false certainty; +- no high-stakes use without qualified review and additional safeguards; +- data minimization and explicit privacy controls; +- monitoring for harmful or systematically poor recommendations; +- a documented rollback or disable path; +- an AI System Card and release record for material deployments. + +Review the organization-wide governance and release controls in [`altmanAI/.github`](https://github.com/altmanAI/.github). + +## Documentation + +- [`overview.md`](overview.md) — product and scoring overview +- [`architecture.md`](architecture.md) — current architecture and boundaries +- [`api.md`](api.md) — Python reference usage +- [`IOS_BRIDGE.md`](IOS_BRIDGE.md) — integration planning notes; not a production-readiness claim +- [`SECURITY.md`](SECURITY.md) — private vulnerability reporting + +## Security and privacy + +Do not submit real private task lists, credentials, health details, financial records, customer data, or confidential business information to public issues or pull requests. + +Report security-sensitive findings privately as described in [`SECURITY.md`](SECURITY.md). + +## Contributing + +Focused contributions are welcome. Every material pull request should include: + +- a linked objective or issue; +- tests or reproducible validation; +- capability and limitation updates; +- security, privacy, accessibility, and human-impact review; +- rollback considerations; +- disclosure of material AI assistance. + +## License -This is the reference implementation of the DailyPilot scoring engine — real, working Python logic with tests, not a concept doc. iOS/production integration is in progress; see `IOS_BRIDGE.md` for the current plan. +The repository license controls use and redistribution. Public visibility and reference status do not imply endorsement, certification, fitness for a particular purpose, or authorization for high-stakes deployment. -## Part of the AltmanAI ecosystem +## P.A.I.H.I. -DailyPilot-Engine is one product surface in the broader AltmanAI system. See [altmanAI/.github](https://github.com/altmanAI/.github) for the organization-level overview, and [altmanAI/altmanai-master-ledger](https://github.com/altmanAI/altmanai-master-ledger) for how releases here connect to the public Proof-of-AI-Human-Impact (PAIHI) record. +- **Proof:** Tests and inspectable score breakdowns support bounded behavior claims. +- **Alignment:** The engine assists human prioritization without claiming human authority. +- **Integrity:** Limitations and non-goals are documented explicitly. +- **Humanity:** Integrations must preserve agency, privacy, accessibility, correction, and recourse. +- **Impact:** Useful outcomes should be evaluated with versioned evidence rather than assumed from demonstrations. From 810d30aee8568c3448be4e7eb2b43869fec4c620 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:34:41 -0400 Subject: [PATCH 08/35] docs: align architecture with executable repository layout --- architecture.md | 92 +++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 85 insertions(+), 7 deletions(-) diff --git a/architecture.md b/architecture.md index 31eb25a..53bafd3 100644 --- a/architecture.md +++ b/architecture.md @@ -1,9 +1,87 @@ -# DailyPilot-Engine – Architecture +# DailyPilot Engine Architecture -The engine is intentionally small and composable: +## Current structure -- `engine.core.models` – data models (Task, ProfileConfig, Plan, RunLog) -- `engine.core.scoring` – scoring logic -- `engine.core.selectors` – turns scored tasks into a daily plan -- `engine.profiles` – JSON configs for different life contexts -- `integrations.cli` – command-line harness for experiments and testing +The reference engine intentionally uses a small root-module architecture: + +- `models.py` — task, profile, scored-task, plan, and run-log data models; +- `scoring.py` — transparent factor normalization, weighting, deadline treatment, and stress penalty; +- `selectors.py` — effort-budgeted plan construction; +- `dailypilot_cli.py` — local JSON input, profile loading, output display, and run logging; +- root-level profile JSON files — example configuration for different contexts; +- root-level test files — behavior and integrity evidence. + +This layout is the current source of truth. A future packaging migration must update implementation imports, tests, CLI paths, documentation, and release instructions in the same reviewed change. + +## Data flow + +```text +Task JSON + │ + ▼ +Task data models ──► score_tasks() ──► ordered ScoredTask records + │ +Profile JSON ─────────────────────────────────┤ + ▼ + build_daily_plan() + │ + ▼ + focus / support / parked tasks + │ + ▼ + human review + local run log +``` + +## Trust boundaries + +The reference engine: + +- reads local JSON supplied by the operator; +- performs deterministic local Python calculations except for current-date and timestamp inputs; +- writes local log files; +- does not call external AI models, APIs, accounts, calendars, or messaging services; +- does not authenticate users or manage production data. + +A product integration changes these boundaries and requires a separate security, privacy, evaluation, and release review. + +## Explainability + +Each scored task retains: + +- normalized importance; +- normalized urgency; +- normalized inverse effort; +- normalized deadline proximity; +- stress penalty; +- base score; +- final score. + +This makes the heuristic inspectable. It does not guarantee that the factors or weights are appropriate for every person or context. + +## Human-control requirements + +Integrations must preserve: + +- review before consequential action; +- correction and override; +- clear status and limitation disclosure; +- no false claim of objectivity or optimality; +- no hidden external action; +- an operator-controlled disable or rollback path; +- privacy and data-minimization controls. + +## Known limitations + +The current engine does not model: + +- task dependencies; +- fixed calendar events or travel time; +- emergencies or safety-critical obligations; +- multi-day optimization; +- changing energy across a day; +- fairness across people or teams; +- accessibility accommodations; +- emotional, medical, financial, or legal consequences; +- uncertainty in user-entered importance, urgency, effort, or stress values. + +These are explicit engineering boundaries, not future capability claims. From 3cabc3ec96e18e0b749fd68ce3b748670884d97d Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:34:59 -0400 Subject: [PATCH 09/35] docs: repair Python imports and document API boundaries --- api.md | 109 ++++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 104 insertions(+), 5 deletions(-) diff --git a/api.md b/api.md index 0a1e066..e6786c7 100644 --- a/api.md +++ b/api.md @@ -1,9 +1,108 @@ -# DailyPilot-Engine – API Notes +# DailyPilot Engine Python Reference -Python reference usage: +## Import the current modules ```python -from engine.core.models import Task, ProfileConfig -from engine.core.scoring import score_tasks -from engine.core.selectors import build_daily_plan +from models import ProfileConfig, Task +from scoring import score_tasks +from selectors import build_daily_plan ``` + +The repository currently uses root-level Python modules. This is a reference interface, not a stability guarantee for a published package. A future packaging migration must be versioned and documented as a potentially breaking change. + +## Minimal example + +```python +from models import ProfileConfig, Task +from scoring import score_tasks +from selectors import build_daily_plan + +profile = ProfileConfig( + name="example", + daily_effort_budget_hours=3.0, + max_big_focus=2, +) + +tasks = [ + Task( + id="prepare_demo", + title="Prepare product demo", + importance=5, + urgency=4, + effort_estimate=2.0, + stress_impact="MEDIUM", + ), + Task( + id="organize_notes", + title="Organize notes", + importance=3, + urgency=2, + effort_estimate=0.5, + stress_impact="LOW", + ), +] + +scored = score_tasks(tasks, profile) +plan = build_daily_plan(scored, profile) + +for result in scored: + print(result.task.title, result.score, result.breakdown) + +print([task.title for task in plan.big_focus]) +print([task.title for task in plan.support_tasks]) +print([task.title for task in plan.parked_tasks]) +``` + +## Data contracts + +### `Task` + +Required: + +- `id: str` +- `title: str` + +Selected optional fields: + +- `description: str` +- `importance: int` — intended range 1–5 +- `urgency: int` — intended range 1–5 +- `effort_estimate: float` — hours +- `stress_impact: str` — `LOW`, `MEDIUM`, or `HIGH` +- `due_date: date | None` +- `time_window: str | None` +- `category: str | None` +- `meta: dict` + +The dataclass does not currently enforce every intended range. Integrators must validate untrusted input before constructing tasks. + +### `ProfileConfig` + +- `name: str` +- `daily_effort_budget_hours: float` +- `max_big_focus: int` +- `weights: dict[str, float]` +- `max_stress_load: float` + +`max_stress_load` is present in the data model but is not currently enforced by the selector. Do not represent it as an active safety constraint. + +### `ScoredTask` + +Contains the original task, final score, and factor breakdown. + +### `Plan` + +Contains focus tasks, support tasks, parked tasks, a timestamp, summary, profile name, and engine version. + +## Integration requirements + +Before using this reference logic in a product: + +- validate all untrusted input; +- document model and configuration versions; +- preserve score explanations; +- provide human correction and override; +- add privacy, authentication, authorization, abuse, and incident controls appropriate to the product; +- evaluate behavior on representative and adverse scenarios; +- do not use the output as sole authority for consequential decisions; +- document monitoring and rollback. From cab0585e966e0a21da04fe5c5597007d3b9173b6 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:35:20 -0400 Subject: [PATCH 10/35] security: add DailyPilot private reporting and trust boundaries --- SECURITY.md | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100644 SECURITY.md diff --git a/SECURITY.md b/SECURITY.md new file mode 100644 index 0000000..c392111 --- /dev/null +++ b/SECURITY.md @@ -0,0 +1,52 @@ +# Security Policy + +## Report privately + +Do not open a public issue for vulnerabilities, exposed secrets, privacy incidents, unsafe tool or model behavior, authentication or authorization failures, or unremediated exploit details. + +Report privately to: + +**security@altmanai.tech** + +Include the affected commit or component, reproduction steps, realistic impact, preconditions, and suggested containment when safely possible. Do not send live credentials or unnecessary personal data. + +## Supported code + +The current default branch and explicitly identified active release branches are supported. Historical commits, forks, experiments, and archived work may not receive fixes. + +This repository is a local reference engine. Its presence on GitHub does not establish that any particular version is deployed in production. + +## Security and privacy boundaries + +The current reference implementation: + +- reads local JSON files supplied by the operator; +- performs local Python calculations; +- writes local run logs; +- does not call external models, APIs, accounts, calendars, or messaging services; +- does not provide authentication, authorization, encryption, multi-user isolation, or production data controls. + +Any integration that adds network access, accounts, personal data, external AI models, tools, memory, retrieval, analytics, or cloud storage requires a separate threat model and release review. + +## Contributor requirements + +- Never commit tokens, passwords, private keys, certificates, production exports, or real private task lists. +- Treat task content, profile data, logs, and user routines as potentially sensitive. +- Validate untrusted JSON before use in a product boundary. +- Use least privilege for integrations and external services. +- Preserve human review, correction, override, and disable paths. +- Assess prompt injection, data exfiltration, tool misuse, and provider risk if an AI model or agent is added. +- Add tests for security-sensitive input and failure behavior. +- Document monitoring, containment, and rollback for material releases. + +## Safe research boundaries + +Good-faith research must avoid unauthorized access, data retention, service disruption, social engineering, persistence, destructive testing, or disclosure of private information. Stop and report if you encounter credentials, personal data, confidential information, or evidence of active compromise. + +## High-impact use warning + +Do not use this reference engine as the sole authority for medical, mental-health, legal, financial, employment, education, housing, safety, or other consequential decisions. Such uses require qualified domain review, additional safeguards, validated data, recourse, monitoring, and applicable legal or regulatory assessment. + +## Handling + +Reports are assessed based on evidence, exploitability, affected systems, user impact, and remediation complexity. No fixed response-time commitment is implied. Public disclosure should wait until remediation or a coordinated disclosure plan reduces avoidable risk. From fcd242a68e8bd1e1c765981e764996a116087653 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:35:29 -0400 Subject: [PATCH 11/35] governance: establish DailyPilot code ownership --- .github/CODEOWNERS | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) create mode 100644 .github/CODEOWNERS diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS new file mode 100644 index 0000000..afef3e0 --- /dev/null +++ b/.github/CODEOWNERS @@ -0,0 +1,17 @@ +# Default accountable owner +* @altmanAI + +# Human-first decision logic and data contracts +/models.py @altmanAI +/scoring.py @altmanAI +/selectors.py @altmanAI +/dailypilot_cli.py @altmanAI + +# Profiles, tests, security, documentation, and automation +/*.json @altmanAI +/test_*.py @altmanAI +/SECURITY.md @altmanAI +/README.md @altmanAI +/architecture.md @altmanAI +/api.md @altmanAI +/.github/ @altmanAI From 7a59801910751214e150fb2d8c84c70609d8eb84 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:35:49 -0400 Subject: [PATCH 12/35] ci: add least-privilege compile, test, and CLI checks --- .github/workflows/ci.yml | 48 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 .github/workflows/ci.yml diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 0000000..2a8e39f --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,48 @@ +name: DailyPilot Engine CI + +on: + pull_request: + push: + branches: + - main + +permissions: + contents: read + +concurrency: + group: dailypilot-ci-${{ github.workflow }}-${{ github.ref }} + cancel-in-progress: true + +jobs: + test: + name: Python ${{ matrix.python-version }} + runs-on: ubuntu-latest + timeout-minutes: 10 + + strategy: + fail-fast: false + matrix: + python-version: + - "3.10" + - "3.12" + + steps: + - name: Check out repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + + - name: Install test runner + run: python -m pip install --disable-pip-version-check "pytest>=8,<10" + + - name: Compile Python sources + run: python -m compileall -q . + + - name: Run tests + run: python -m pytest -q + + - name: Run documented CLI smoke test + run: python dailypilot_cli.py sample_day.json --profile worker_double_shift From b5a5d28b4ea76efcd2a5a35b3458f69f6e5507c6 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:36:20 -0400 Subject: [PATCH 13/35] refactor: establish DailyPilot engine package --- engine/__init__.py | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 engine/__init__.py diff --git a/engine/__init__.py b/engine/__init__.py new file mode 100644 index 0000000..2a86f28 --- /dev/null +++ b/engine/__init__.py @@ -0,0 +1,3 @@ +"""DailyPilot Engine reference package.""" + +__version__ = "0.1.0" From f44ea07228c9649dab7e1f2246d76076902183b8 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:36:27 -0400 Subject: [PATCH 14/35] refactor: expose stable core package surface --- engine/core/__init__.py | 15 +++++++++++++++ 1 file changed, 15 insertions(+) create mode 100644 engine/core/__init__.py diff --git a/engine/core/__init__.py b/engine/core/__init__.py new file mode 100644 index 0000000..deded83 --- /dev/null +++ b/engine/core/__init__.py @@ -0,0 +1,15 @@ +"""Core data models, scoring, and plan selection for DailyPilot Engine.""" + +from .models import Plan, ProfileConfig, RunLog, ScoredTask, Task +from .scoring import score_tasks +from .selectors import build_daily_plan + +__all__ = [ + "Plan", + "ProfileConfig", + "RunLog", + "ScoredTask", + "Task", + "build_daily_plan", + "score_tasks", +] From 3bc3010a4060614937ee0ddc0e5c8d912c7e3bd4 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:36:38 -0400 Subject: [PATCH 15/35] refactor: move data contracts into engine.core --- engine/core/models.py | 65 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 engine/core/models.py diff --git a/engine/core/models.py b/engine/core/models.py new file mode 100644 index 0000000..c5e3eea --- /dev/null +++ b/engine/core/models.py @@ -0,0 +1,65 @@ +from dataclasses import dataclass, field +from datetime import date, datetime +from typing import Any, Dict, List, Optional + + +@dataclass +class Task: + """A single unit of work the engine can prioritize.""" + + id: str + title: str + description: str = "" + importance: int = 3 + urgency: int = 3 + effort_estimate: float = 1.0 + stress_impact: str = "MEDIUM" + due_date: Optional[date] = None + time_window: Optional[str] = None + category: Optional[str] = None + meta: Dict[str, Any] = field(default_factory=dict) + + +@dataclass +class ProfileConfig: + """Configuration that tunes engine behavior for a user or context.""" + + name: str + daily_effort_budget_hours: float = 6.0 + max_big_focus: int = 3 + weights: Dict[str, float] = field(default_factory=dict) + max_stress_load: float = 1.0 + + +@dataclass +class ScoredTask: + """A task with a computed score and inspectable factor breakdown.""" + + task: Task + score: float + breakdown: Dict[str, float] + + +@dataclass +class Plan: + """Engine output for a single run.""" + + profile_name: str + timestamp: datetime + big_focus: List[Task] + support_tasks: List[Task] + parked_tasks: List[Task] + decision_summary: str + engine_version: str = "0.1.0" + + +@dataclass +class RunLog: + """Minimal structured record for local audit or ledger export.""" + + timestamp: datetime + profile_name: str + input_task_count: int + big_focus_ids: List[str] + engine_version: str + decision_summary: str From 1a85b663fde59aa7db0c003ed724c384aaa14582 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:36:53 -0400 Subject: [PATCH 16/35] refactor: move scoring into engine.core package --- engine/core/scoring.py | 80 ++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 80 insertions(+) create mode 100644 engine/core/scoring.py diff --git a/engine/core/scoring.py b/engine/core/scoring.py new file mode 100644 index 0000000..b2292a3 --- /dev/null +++ b/engine/core/scoring.py @@ -0,0 +1,80 @@ +from datetime import date +from typing import Dict, List + +from .models import ProfileConfig, ScoredTask, Task + + +def _normalize(value: float, min_value: float, max_value: float) -> float: + if max_value == min_value: + return 0.0 + value = max(min_value, min(max_value, value)) + return (value - min_value) / (max_value - min_value) + + +def _stress_penalty(stress_impact: str) -> float: + mapping = { + "LOW": 0.0, + "MEDIUM": 0.1, + "HIGH": 0.25, + } + return mapping.get(stress_impact.upper(), 0.1) + + +def score_tasks(tasks: List[Task], profile: ProfileConfig) -> List[ScoredTask]: + """Score tasks using an inspectable heuristic for a given profile. + + The result is decision support, not an autonomous or high-stakes decision. + Callers remain responsible for reviewing deadlines, safety constraints, + accessibility needs, and other context the heuristic does not represent. + """ + + weights: Dict[str, float] = { + "importance": 0.4, + "urgency": 0.3, + "effort": 0.2, + "deadline": 0.1, + } + weights.update(profile.weights or {}) + + today = date.today() + scored: List[ScoredTask] = [] + + for task in tasks: + importance_n = _normalize(task.importance, 1, 5) + urgency_n = _normalize(task.urgency, 1, 5) + effort_n = 1.0 - _normalize(task.effort_estimate, 0.25, 8.0) + + if task.due_date: + days_to_due = (task.due_date - today).days + if days_to_due <= 0: + deadline_n = 1.0 + elif days_to_due >= 14: + deadline_n = 0.0 + else: + deadline_n = 1.0 - (days_to_due / 14.0) + else: + deadline_n = 0.0 + + base_score = ( + weights["importance"] * importance_n + + weights["urgency"] * urgency_n + + weights["effort"] * effort_n + + weights["deadline"] * deadline_n + ) + + penalty = _stress_penalty(task.stress_impact) + final_score = max(0.0, base_score - penalty) + + breakdown = { + "importance_n": importance_n, + "urgency_n": urgency_n, + "effort_n": effort_n, + "deadline_n": deadline_n, + "stress_penalty": penalty, + "base_score": base_score, + } + + scored.append(ScoredTask(task=task, score=final_score, breakdown=breakdown)) + + scored.sort(key=lambda scored_task: scored_task.score, reverse=True) + return scored From 9a958f0dd2a89005718278ae41adc0b7a064eb28 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:37:07 -0400 Subject: [PATCH 17/35] refactor: move plan selection into engine.core package --- engine/core/selectors.py | 50 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 engine/core/selectors.py diff --git a/engine/core/selectors.py b/engine/core/selectors.py new file mode 100644 index 0000000..2c66142 --- /dev/null +++ b/engine/core/selectors.py @@ -0,0 +1,50 @@ +from datetime import datetime, timezone +from typing import List + +from .models import Plan, ProfileConfig, ScoredTask, Task + + +def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> Plan: + """Build a bounded plan from tasks already ordered by score. + + The selector respects the configured effort budget and focus-task limit. + It does not understand calendar conflicts, dependencies, safety-critical + obligations, or consequences beyond the fields supplied by the caller. + A human should review the resulting plan before acting on it. + """ + + effort_budget = profile.daily_effort_budget_hours + used_effort = 0.0 + + big_focus: List[Task] = [] + support_tasks: List[Task] = [] + parked_tasks: List[Task] = [] + + for scored in scored_tasks: + task = scored.task + task_effort = max(0.0, task.effort_estimate) + + if used_effort + task_effort <= effort_budget: + used_effort += task_effort + if len(big_focus) < profile.max_big_focus: + big_focus.append(task) + else: + support_tasks.append(task) + else: + parked_tasks.append(task) + + summary = ( + f"Selected {len(big_focus)} big focus task(s) and " + f"{len(support_tasks)} support task(s) within " + f"{used_effort:.1f}h of a {effort_budget:.1f}h budget. " + f"Parked {len(parked_tasks)} task(s)." + ) + + return Plan( + profile_name=profile.name, + timestamp=datetime.now(timezone.utc), + big_focus=big_focus, + support_tasks=support_tasks, + parked_tasks=parked_tasks, + decision_summary=summary, + ) From c13503c8c06d4311942da051189c4e36036a04b8 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:37:14 -0400 Subject: [PATCH 18/35] refactor: move founder profile into engine package --- engine/profiles/founder.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 engine/profiles/founder.json diff --git a/engine/profiles/founder.json b/engine/profiles/founder.json new file mode 100644 index 0000000..98de6e9 --- /dev/null +++ b/engine/profiles/founder.json @@ -0,0 +1,12 @@ +{ + "name": "founder", + "daily_effort_budget_hours": 6.0, + "max_big_focus": 3, + "max_stress_load": 1.0, + "weights": { + "importance": 0.5, + "urgency": 0.25, + "effort": 0.15, + "deadline": 0.1 + } +} From 5506d8d7b503eab427c473420ca3dc2fbbc24b46 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:37:22 -0400 Subject: [PATCH 19/35] refactor: move student profile into engine package --- engine/profiles/student.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 engine/profiles/student.json diff --git a/engine/profiles/student.json b/engine/profiles/student.json new file mode 100644 index 0000000..4a9dd8f --- /dev/null +++ b/engine/profiles/student.json @@ -0,0 +1,12 @@ +{ + "name": "student", + "daily_effort_budget_hours": 5.0, + "max_big_focus": 3, + "max_stress_load": 0.9, + "weights": { + "importance": 0.4, + "urgency": 0.3, + "effort": 0.2, + "deadline": 0.1 + } +} From af5abaf03e29b291e64d370eb88207fc7687ef32 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:37:30 -0400 Subject: [PATCH 20/35] refactor: move worker profile into engine package --- engine/profiles/worker_double_shift.json | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 engine/profiles/worker_double_shift.json diff --git a/engine/profiles/worker_double_shift.json b/engine/profiles/worker_double_shift.json new file mode 100644 index 0000000..0702acb --- /dev/null +++ b/engine/profiles/worker_double_shift.json @@ -0,0 +1,12 @@ +{ + "name": "worker_double_shift", + "daily_effort_budget_hours": 4.5, + "max_big_focus": 3, + "max_stress_load": 0.8, + "weights": { + "importance": 0.35, + "urgency": 0.35, + "effort": 0.2, + "deadline": 0.1 + } +} From f916fce88619c213774e1e823b960dccc07b6a11 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:37:55 -0400 Subject: [PATCH 21/35] refactor: route CLI through engine.core package --- dailypilot_cli.py | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/dailypilot_cli.py b/dailypilot_cli.py index 2565148..93e4348 100644 --- a/dailypilot_cli.py +++ b/dailypilot_cli.py @@ -4,15 +4,16 @@ from pathlib import Path from typing import List -from models import ProfileConfig, RunLog, Task -from scoring import score_tasks -from selectors import build_daily_plan +from engine.core.models import ProfileConfig, RunLog, Task +from engine.core.scoring import score_tasks +from engine.core.selectors import build_daily_plan BASE_DIR = Path(__file__).resolve().parent +PROFILE_DIR = BASE_DIR / "engine" / "profiles" def load_profile(profile_name: str) -> ProfileConfig: - path = BASE_DIR / f"{profile_name}.json" + path = PROFILE_DIR / f"{profile_name}.json" if not path.exists(): raise SystemExit(f"Profile not found: {profile_name} (expected {path})") @@ -97,7 +98,7 @@ def main() -> None: parser.add_argument( "--profile", default="worker_double_shift", - help="Profile JSON name without extension (default: worker_double_shift)", + help="Profile name (default: worker_double_shift)", ) args = parser.parse_args() From 8789125062fda5973a8d2931a264910c664da516 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:38:09 -0400 Subject: [PATCH 22/35] test: route scoring coverage through engine.core --- test_scoring.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_scoring.py b/test_scoring.py index ecf1a10..0813f72 100644 --- a/test_scoring.py +++ b/test_scoring.py @@ -1,7 +1,7 @@ from datetime import date, timedelta -from models import ProfileConfig, Task -from scoring import score_tasks +from engine.core.models import ProfileConfig, Task +from engine.core.scoring import score_tasks def test_higher_importance_scores_higher(): From 2ceb09ef3672490fdc1d2c1975e3fbc5b3a83f75 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:38:23 -0400 Subject: [PATCH 23/35] test: route selector coverage through engine.core --- test_selectors.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_selectors.py b/test_selectors.py index e8eef33..c10cad3 100644 --- a/test_selectors.py +++ b/test_selectors.py @@ -1,5 +1,5 @@ -from models import ProfileConfig, ScoredTask, Task -from selectors import build_daily_plan +from engine.core.models import ProfileConfig, ScoredTask, Task +from engine.core.selectors import build_daily_plan def _scored(task_id: str, effort: float, score: float) -> ScoredTask: From 6335978038b80fd9d44813ab29454bb17063e42c Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:38:31 -0400 Subject: [PATCH 24/35] test: validate packaged profile configuration --- test_profiles.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test_profiles.py b/test_profiles.py index ff64484..49e3c9a 100644 --- a/test_profiles.py +++ b/test_profiles.py @@ -10,8 +10,8 @@ def test_profiles_are_valid_json_objects(): - base_dir = Path(__file__).resolve().parent - discovered = {path.name for path in base_dir.glob("*.json") if path.name != "sample_day.json"} + base_dir = Path(__file__).resolve().parent / "engine" / "profiles" + discovered = {path.name for path in base_dir.glob("*.json")} assert PROFILE_NAMES.issubset(discovered) From 1362131c98cfcd2a5724223c4853fcde6eaf2c25 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:39:03 -0400 Subject: [PATCH 25/35] docs: document the executable engine package and trust boundaries --- README.md | 31 ++++++++++++++++++++++++------- 1 file changed, 24 insertions(+), 7 deletions(-) diff --git a/README.md b/README.md index 74c5937..d1c66fc 100644 --- a/README.md +++ b/README.md @@ -42,20 +42,25 @@ The current scoring method is an inspectable heuristic. A high-stress task may b ```text . -├── models.py # Task, profile, score, plan, and run-log data models -├── scoring.py # Transparent scoring heuristic and breakdown -├── selectors.py # Effort-budgeted plan construction +├── engine/ +│ ├── __init__.py +│ ├── core/ +│ │ ├── __init__.py +│ │ ├── models.py # Task, profile, score, plan, and run-log models +│ │ ├── scoring.py # Transparent scoring heuristic and breakdown +│ │ └── selectors.py # Effort-budgeted plan construction +│ └── profiles/ +│ ├── student.json +│ ├── worker_double_shift.json +│ └── founder.json ├── dailypilot_cli.py # Local command-line harness ├── sample_day.json # Example task input -├── student.json # Example profile -├── worker_double_shift.json # Example profile -├── founder.json # Example profile ├── test_scoring.py # Scoring behavior tests ├── test_selectors.py # Plan-selection behavior tests └── test_profiles.py # Profile integrity tests ``` -The repository currently uses a deliberately small root-module layout. Imports, tests, CLI paths, and documentation must remain consistent with that layout unless a reviewed packaging migration changes them together. +The package layout prevents collisions with Python standard-library modules and provides one consistent import path for code, tests, CLI use, and documentation. ## Quick start @@ -87,6 +92,18 @@ The task file must be a JSON array. Each task requires `id` and `title`; other s `due_date`, when supplied through JSON, must use `YYYY-MM-DD`. +## Python usage + +```python +from engine.core import ProfileConfig, Task, build_daily_plan, score_tasks + +profile = ProfileConfig(name="example", daily_effort_budget_hours=3.0) +tasks = [Task(id="prepare_demo", title="Prepare product demo", importance=5)] + +scored = score_tasks(tasks, profile) +plan = build_daily_plan(scored, profile) +``` + ## Run tests Install the test runner in an isolated environment: From d5e17fc3dc27374d52db2d8d764caf9afb180710 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:39:18 -0400 Subject: [PATCH 26/35] docs: align architecture with engine.core package --- architecture.md | 16 +++++++++------- 1 file changed, 9 insertions(+), 7 deletions(-) diff --git a/architecture.md b/architecture.md index 53bafd3..4491380 100644 --- a/architecture.md +++ b/architecture.md @@ -1,17 +1,17 @@ # DailyPilot Engine Architecture -## Current structure +## Package structure -The reference engine intentionally uses a small root-module architecture: +The reference engine uses an explicit Python package: -- `models.py` — task, profile, scored-task, plan, and run-log data models; -- `scoring.py` — transparent factor normalization, weighting, deadline treatment, and stress penalty; -- `selectors.py` — effort-budgeted plan construction; +- `engine.core.models` — task, profile, scored-task, plan, and run-log data models; +- `engine.core.scoring` — transparent factor normalization, weighting, deadline treatment, and stress penalty; +- `engine.core.selectors` — effort-budgeted plan construction; +- `engine.profiles` — version-controlled example profile JSON files; - `dailypilot_cli.py` — local JSON input, profile loading, output display, and run logging; -- root-level profile JSON files — example configuration for different contexts; - root-level test files — behavior and integrity evidence. -This layout is the current source of truth. A future packaging migration must update implementation imports, tests, CLI paths, documentation, and release instructions in the same reviewed change. +The package boundary avoids collisions with Python standard-library modules and provides one import path for tests, CLI use, and integrations. ## Data flow @@ -84,4 +84,6 @@ The current engine does not model: - emotional, medical, financial, or legal consequences; - uncertainty in user-entered importance, urgency, effort, or stress values. +`ProfileConfig.max_stress_load` is currently recorded but not enforced by the plan selector. It must not be represented as an active constraint until implementation and tests prove that behavior. + These are explicit engineering boundaries, not future capability claims. From 2dd3eda8ff933743c04799c53231120ca544e241 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:39:40 -0400 Subject: [PATCH 27/35] docs: expose the engine.core reference API --- api.md | 24 ++++++++++++++---------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/api.md b/api.md index e6786c7..042d3ce 100644 --- a/api.md +++ b/api.md @@ -1,21 +1,25 @@ # DailyPilot Engine Python Reference -## Import the current modules +## Import the core interface ```python -from models import ProfileConfig, Task -from scoring import score_tasks -from selectors import build_daily_plan +from engine.core import ProfileConfig, Task, build_daily_plan, score_tasks ``` -The repository currently uses root-level Python modules. This is a reference interface, not a stability guarantee for a published package. A future packaging migration must be versioned and documented as a potentially breaking change. +Direct module imports are also available: + +```python +from engine.core.models import ProfileConfig, Task +from engine.core.scoring import score_tasks +from engine.core.selectors import build_daily_plan +``` + +This is a reference interface at version `0.1.0`, not a guarantee of long-term package compatibility. Breaking changes must be versioned and documented. ## Minimal example ```python -from models import ProfileConfig, Task -from scoring import score_tasks -from selectors import build_daily_plan +from engine.core import ProfileConfig, Task, build_daily_plan, score_tasks profile = ProfileConfig( name="example", @@ -92,14 +96,14 @@ Contains the original task, final score, and factor breakdown. ### `Plan` -Contains focus tasks, support tasks, parked tasks, a timestamp, summary, profile name, and engine version. +Contains focus tasks, support tasks, parked tasks, a UTC timestamp, summary, profile name, and engine version. ## Integration requirements Before using this reference logic in a product: - validate all untrusted input; -- document model and configuration versions; +- document code and configuration versions; - preserve score explanations; - provide human correction and override; - add privacy, authentication, authorization, abuse, and incident controls appropriate to the product; From e67e7770544e31451d1fddb7aeb801f2463361f7 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:39:57 -0400 Subject: [PATCH 28/35] governance: align ownership with engine package --- .github/CODEOWNERS | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/.github/CODEOWNERS b/.github/CODEOWNERS index afef3e0..50ac06b 100644 --- a/.github/CODEOWNERS +++ b/.github/CODEOWNERS @@ -1,15 +1,13 @@ # Default accountable owner * @altmanAI -# Human-first decision logic and data contracts -/models.py @altmanAI -/scoring.py @altmanAI -/selectors.py @altmanAI +# Human-first decision logic, data contracts, and profiles +/engine/ @altmanAI /dailypilot_cli.py @altmanAI -# Profiles, tests, security, documentation, and automation -/*.json @altmanAI +# Tests, security, documentation, and automation /test_*.py @altmanAI +/sample_day.json @altmanAI /SECURITY.md @altmanAI /README.md @altmanAI /architecture.md @altmanAI From 2452cb66f0b5a9fb13f49a2704567f6c7c9c79e6 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:09 -0400 Subject: [PATCH 29/35] refactor: remove superseded root data module --- models.py | 65 ------------------------------------------------------- 1 file changed, 65 deletions(-) delete mode 100644 models.py diff --git a/models.py b/models.py deleted file mode 100644 index f21b58d..0000000 --- a/models.py +++ /dev/null @@ -1,65 +0,0 @@ -from dataclasses import dataclass, field -from datetime import datetime, date -from typing import List, Optional, Dict, Any - - -@dataclass -class Task: - """A single unit of work the engine can prioritize.""" - - id: str - title: str - description: str = "" - importance: int = 3 - urgency: int = 3 - effort_estimate: float = 1.0 # in hours - stress_impact: str = "MEDIUM" # LOW | MEDIUM | HIGH - due_date: Optional[date] = None - time_window: Optional[str] = None # free text for now - category: Optional[str] = None - meta: Dict[str, Any] = field(default_factory=dict) - - -@dataclass -class ProfileConfig: - """Configuration that tunes how the engine behaves for a user or context.""" - - name: str - daily_effort_budget_hours: float = 6.0 - max_big_focus: int = 3 - weights: Dict[str, float] = field(default_factory=dict) - max_stress_load: float = 1.0 # 0–1 scale - - -@dataclass -class ScoredTask: - """Wrapper around a Task with a computed score and explanation.""" - - task: Task - score: float - breakdown: Dict[str, float] - - -@dataclass -class Plan: - """Engine output for a single run.""" - - profile_name: str - timestamp: datetime - big_focus: List[Task] - support_tasks: List[Task] - parked_tasks: List[Task] - decision_summary: str - engine_version: str = "0.1.0" - - -@dataclass -class RunLog: - """Minimal structured log record, suitable for AINet / ledger export.""" - - timestamp: datetime - profile_name: str - input_task_count: int - big_focus_ids: List[str] - engine_version: str - decision_summary: str From 91149ce87580c86def227cf48ee0232d27a40057 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:21 -0400 Subject: [PATCH 30/35] refactor: remove superseded root scoring module --- scoring.py | 80 ------------------------------------------------------ 1 file changed, 80 deletions(-) delete mode 100644 scoring.py diff --git a/scoring.py b/scoring.py deleted file mode 100644 index cda292b..0000000 --- a/scoring.py +++ /dev/null @@ -1,80 +0,0 @@ -from datetime import date -from typing import Dict, List - -from models import ProfileConfig, ScoredTask, Task - - -def _normalize(value: float, min_value: float, max_value: float) -> float: - if max_value == min_value: - return 0.0 - value = max(min_value, min(max_value, value)) - return (value - min_value) / (max_value - min_value) - - -def _stress_penalty(stress_impact: str) -> float: - mapping = { - "LOW": 0.0, - "MEDIUM": 0.1, - "HIGH": 0.25, - } - return mapping.get(stress_impact.upper(), 0.1) - - -def score_tasks(tasks: List[Task], profile: ProfileConfig) -> List[ScoredTask]: - """Score tasks using an inspectable heuristic for a given profile. - - The result is decision support, not an autonomous or high-stakes decision. - Callers remain responsible for reviewing deadlines, safety constraints, - accessibility needs, and other context the heuristic does not represent. - """ - - weights: Dict[str, float] = { - "importance": 0.4, - "urgency": 0.3, - "effort": 0.2, - "deadline": 0.1, - } - weights.update(profile.weights or {}) - - today = date.today() - scored: List[ScoredTask] = [] - - for task in tasks: - importance_n = _normalize(task.importance, 1, 5) - urgency_n = _normalize(task.urgency, 1, 5) - effort_n = 1.0 - _normalize(task.effort_estimate, 0.25, 8.0) - - if task.due_date: - days_to_due = (task.due_date - today).days - if days_to_due <= 0: - deadline_n = 1.0 - elif days_to_due >= 14: - deadline_n = 0.0 - else: - deadline_n = 1.0 - (days_to_due / 14.0) - else: - deadline_n = 0.0 - - base_score = ( - weights["importance"] * importance_n - + weights["urgency"] * urgency_n - + weights["effort"] * effort_n - + weights["deadline"] * deadline_n - ) - - penalty = _stress_penalty(task.stress_impact) - final_score = max(0.0, base_score - penalty) - - breakdown = { - "importance_n": importance_n, - "urgency_n": urgency_n, - "effort_n": effort_n, - "deadline_n": deadline_n, - "stress_penalty": penalty, - "base_score": base_score, - } - - scored.append(ScoredTask(task=task, score=final_score, breakdown=breakdown)) - - scored.sort(key=lambda scored_task: scored_task.score, reverse=True) - return scored From b4b31da87a1cb1c91b5b27b5f619304d6dc2a6b8 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:29 -0400 Subject: [PATCH 31/35] refactor: remove stdlib-shadowing root selector module --- selectors.py | 50 -------------------------------------------------- 1 file changed, 50 deletions(-) delete mode 100644 selectors.py diff --git a/selectors.py b/selectors.py deleted file mode 100644 index f6eb8af..0000000 --- a/selectors.py +++ /dev/null @@ -1,50 +0,0 @@ -from datetime import datetime -from typing import List - -from models import Plan, ProfileConfig, ScoredTask, Task - - -def build_daily_plan(scored_tasks: List[ScoredTask], profile: ProfileConfig) -> Plan: - """Build a bounded plan from tasks already ordered by score. - - The selector respects the configured effort budget and focus-task limit. - It does not understand calendar conflicts, dependencies, safety-critical - obligations, or consequences beyond the fields supplied by the caller. - A human should review the resulting plan before acting on it. - """ - - effort_budget = profile.daily_effort_budget_hours - used_effort = 0.0 - - big_focus: List[Task] = [] - support_tasks: List[Task] = [] - parked_tasks: List[Task] = [] - - for scored in scored_tasks: - task = scored.task - task_effort = max(0.0, task.effort_estimate) - - if used_effort + task_effort <= effort_budget: - used_effort += task_effort - if len(big_focus) < profile.max_big_focus: - big_focus.append(task) - else: - support_tasks.append(task) - else: - parked_tasks.append(task) - - summary = ( - f"Selected {len(big_focus)} big focus task(s) and " - f"{len(support_tasks)} support task(s) within " - f"{used_effort:.1f}h of a {effort_budget:.1f}h budget. " - f"Parked {len(parked_tasks)} task(s)." - ) - - return Plan( - profile_name=profile.name, - timestamp=datetime.utcnow(), - big_focus=big_focus, - support_tasks=support_tasks, - parked_tasks=parked_tasks, - decision_summary=summary, - ) From 05914933ab57df28f1ca8374faeab4a75ef7fe13 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:36 -0400 Subject: [PATCH 32/35] refactor: remove superseded root founder profile --- founder.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 founder.json diff --git a/founder.json b/founder.json deleted file mode 100644 index 51fee50..0000000 --- a/founder.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "founder", - "daily_effort_budget_hours": 6.0, - "max_big_focus": 3, - "max_stress_load": 1.0, - "weights": { - "importance": 0.5, - "urgency": 0.25, - "effort": 0.15, - "deadline": 0.1 - } -} \ No newline at end of file From 28804e65b8179f1be3415f4bafcb5c97d0f2f282 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:45 -0400 Subject: [PATCH 33/35] refactor: remove superseded root student profile --- student.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 student.json diff --git a/student.json b/student.json deleted file mode 100644 index de791f8..0000000 --- a/student.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "student", - "daily_effort_budget_hours": 5.0, - "max_big_focus": 3, - "max_stress_load": 0.9, - "weights": { - "importance": 0.4, - "urgency": 0.3, - "effort": 0.2, - "deadline": 0.1 - } -} \ No newline at end of file From 091ad1e8e50b8825dc221ddde64b1da35cf182f7 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:40:54 -0400 Subject: [PATCH 34/35] refactor: remove superseded root worker profile --- worker_double_shift.json | 12 ------------ 1 file changed, 12 deletions(-) delete mode 100644 worker_double_shift.json diff --git a/worker_double_shift.json b/worker_double_shift.json deleted file mode 100644 index 642b4de..0000000 --- a/worker_double_shift.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "name": "worker_double_shift", - "daily_effort_budget_hours": 4.5, - "max_big_focus": 3, - "max_stress_load": 0.8, - "weights": { - "importance": 0.35, - "urgency": 0.35, - "effort": 0.2, - "deadline": 0.1 - } -} \ No newline at end of file From 13e4e39d7ae922baf36f038ae36c5d0353422639 Mon Sep 17 00:00:00 2001 From: AltmanAI Date: Mon, 13 Jul 2026 10:43:20 -0400 Subject: [PATCH 35/35] ci: remove external action dependencies from validation gate --- .github/workflows/ci.yml | 37 +++++++++++++++++++------------------ 1 file changed, 19 insertions(+), 18 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 2a8e39f..86d6ea2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,6 +5,7 @@ on: push: branches: - main + workflow_dispatch: permissions: contents: read @@ -15,34 +16,34 @@ concurrency: jobs: test: - name: Python ${{ matrix.python-version }} + name: Compile, test, and smoke test runs-on: ubuntu-latest timeout-minutes: 10 - strategy: - fail-fast: false - matrix: - python-version: - - "3.10" - - "3.12" - steps: - - name: Check out repository - uses: actions/checkout@v4 + - name: Check out repository without external actions + shell: bash + run: | + set -euo pipefail + git init . + git remote add origin "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}.git" + git fetch --no-tags --depth=1 origin "${GITHUB_REF}" + git checkout --detach FETCH_HEAD + + - name: Report Python runtime + run: python3 --version - - name: Set up Python - uses: actions/setup-python@v5 - with: - python-version: ${{ matrix.python-version }} + - name: Create isolated test environment + run: python3 -m venv .venv - name: Install test runner - run: python -m pip install --disable-pip-version-check "pytest>=8,<10" + run: .venv/bin/python -m pip install --disable-pip-version-check "pytest>=8,<10" - name: Compile Python sources - run: python -m compileall -q . + run: .venv/bin/python -m compileall -q engine dailypilot_cli.py test_*.py - name: Run tests - run: python -m pytest -q + run: .venv/bin/python -m pytest -q - name: Run documented CLI smoke test - run: python dailypilot_cli.py sample_day.json --profile worker_double_shift + run: .venv/bin/python dailypilot_cli.py sample_day.json --profile worker_double_shift