Skip to content

Latest commit

 

History

History
284 lines (232 loc) · 12.9 KB

AGENTS.md

File metadata and controls

284 lines (232 loc) · 12.9 KB

Sennight contributor guide

Sennight is a small, copyable weekly planning system. A scheduled agent reads messages, updates repository state, regenerates a weekly plan, validates it, sends a digest, and commits the result. Keep the implementation readable in one sitting. Prefer direct data files and deterministic checks over services, frameworks, databases, or speculative abstractions.

Repository boundary

This repository contains two distinct surfaces:

  • index.html and CNAME are the public landing page at sennight.agentlab.in. They explain the project and link to the example.
  • template/ is the complete copyable planning system. Users move its contents to the root of their own private repository.
  • README.md documents both surfaces and the setup flow.

Do not confuse the landing page with the planner board:

  • Root index.html is marketing and documentation. It must not read planner state or acquire runtime dependencies.
  • template/index.html is the planner board. It reads plan.json, history.json, and optional activities/<id>.json files directly in the browser. It has no build step.
  • Paths inside template/ must remain valid after template/* is moved to a new repository root. Do not couple them to the enclosing Sennight checkout.
  • A landing page change normally stays at the repository root. A planner, schema, state, workflow, or board change normally stays under template/.
  • If user-facing claims about the template change, keep README.md and the root landing page accurate. Do not mechanically duplicate implementation details unless both surfaces already present them.

Source of truth by file

  • template/CLAUDE.md is the runtime specification and ordered runbook for the scheduled planning agent.
  • template/config.yaml is permanent user configuration: waking span, planning factor, catch-up cap, fixed anchors, activities, quotas, priorities, and activity-specific constraints.
  • template/overrides.yaml contains deviations for one ISO week. Spoken changes belong here unless the user explicitly says permanently or from now on.
  • template/tasks.yaml contains one-off deadline tasks.
  • template/activities/<id>.json optionally stores a locked, long-running program associated with an activity.
  • template/logs/<ISO-week>.yaml is the append-only ledger of completed work.
  • template/inbox/ stores raw transport responses and transport cursors. It is the only message ingestion state.
  • template/plan.json is generated state and the only plan consumed by the board.
  • template/history.json is the append-only summary used for past weeks.
  • template/questions.md records interpretations or contract changes that need human resolution.
  • template/schema/plan.schema.json is the frozen structural contract for plan.json.
  • template/scripts/check_plan.py is the deterministic semantic auditor.
  • template/.github/workflows/plan.yml is the six-hour scheduler and commit path.

When prose and executable behavior disagree, do not silently choose one. Preserve user data, identify the mismatch, and update the relevant contract and checks together only when the requested change authorizes it.

Runtime and state model

The planner has no database and no application server. Repository files are the complete state machine:

  1. The workflow starts on its cron schedule or through manual dispatch.
  2. The agent reads the newest cursor from the last inbox file.
  3. It fetches unread messages from each configured transport and writes one immutable raw inbox file for the run.
  4. It interprets messages into logs, tasks, weekly overrides, permanent configuration, preferences, or open questions.
  5. It computes effective configuration by deep-merging the current ISO week's override onto base configuration.
  6. It replans today through Sunday and rewrites plan.json as a whole.
  7. It runs python3 scripts/check_plan.py from template/ and fixes every failure.
  8. If anything changed, it sends the same compact digest through each configured transport.
  9. The workflow stages, commits, and pushes repository changes. The agent run itself stages changes but does not push.

Messages are processed forward. A newer message wins when messages conflict. Never infer missing logged hours or fabricate completed work. Put ambiguous input in questions.md.

Hard invariants

Preserve these rules in every planner change:

  • Logs are append-only. Correct a prior log with a new entry, including a negative-hour entry when needed. Never rewrite or delete an old entry.
  • Inbox files are verbatim, permanent records. Never rewrite or delete them.
  • Inbox files carry all fetch cursors. Do not introduce hidden cursor state.
  • history.json is append-only. At the first run of a new week, append the prior week's final tally. Never rewrite existing week entries.
  • plan.json is regenerated in full. Never hand-patch a fragment as runtime behavior.
  • Every configured anchor must appear unchanged on its day. A cancellation must be declared under that week's anchors.skip. A one-off anchor must be declared under anchors.add before it appears in the plan.
  • Flexible work uses ordered duration blocks inside free windows. It does not receive invented clock times.
  • Planned non-rest work must not exceed the effective planning_factor of a free window.
  • Deadline tasks are allocated before recurring activities. Recurring activities are allocated by ascending priority number.
  • Unmet quotas are represented honestly in shortfalls; do not hide them by changing quota accounting or overfilling windows.
  • Activity IDs in plans and logs must match configured activity IDs. Deadline tasks use task:<id>. rest is the only reserved non-activity block.
  • activities/*.json programs are locked. The runtime agent may only change a step's done value. It must not reorder, rename, annotate, add, or remove steps.
  • The plan schema is frozen during ordinary planning runs. Proposed schema changes belong in questions.md, not in an opportunistic schema edit.
  • A changed run must send one digest to every configured transport. The text must be the same for all transports.
  • Discord ingestion must ignore messages sent by the bot itself.
  • Missing transport secrets disable only that transport. They do not make the other transport or planning itself fail.

Planning semantics

  • ISO weeks begin on Monday. plan.week_of must be that week's Monday.
  • A normal run plans only the remaining dates from today through Sunday.
  • Free windows fit inside planning.day_start and planning.day_end, do not overlap anchors, and do not overlap one another.
  • Overnight waking spans are represented by allowing configured end times after midnight. The schema permits plan window endings through 24:30.
  • Block order values must be unique and strictly increasing within a window.
  • Logged plus planned hours determine expected quota completion.
  • Task work counts only when logged or planned no later than its deadline.
  • max_catchup_per_day applies per recurring activity per day.
  • DSA weekend minimums include Saturday and Sunday logs plus planned blocks.
  • Gym rules include required days, evening placement, and the configured maximum sessions per day.
  • intern_flex must be placed adjacent to its configured anchor. The auditor treats a window as adjacent when its edge is within 30 minutes of the anchor edge.
  • Prefer explicit shortfalls over unsafe compression or front-loaded catch-up.

Validator contract

Run the validator from template/:

python3 scripts/check_plan.py

Install its only Python dependencies when needed:

python3 -m pip install pyyaml jsonschema

An optional historical week can be selected with:

python3 scripts/check_plan.py --week 2026-W31

The validator checks JSON Schema compliance, Monday week boundaries, weekday and date agreement, anchor preservation, free-window bounds and overlap, planning-factor capacity, block ordering, known activity IDs, daily catch-up caps, quota arithmetic, declared shortfalls, the DSA weekend minimum, parts of the gym rules, intern adjacency, and deadline task placement.

The validator is necessary but not sufficient. Review manually for rules it does not fully prove, including:

  • correct message interpretation and newest-message precedence;
  • raw inbox preservation and correct transport cursor advancement;
  • deep-merge behavior for nested overrides;
  • allocation priority and sensible spreading across remaining days;
  • exact gym session length and weekday session count;
  • activity-specific allowed days not explicitly audited;
  • truthful shortfall reasons and expected_final_h values;
  • digest delivery, identical digest text, and self-message filtering;
  • locked activity fields other than done;
  • correct weekly history rollover.

If a new invariant matters, add a deterministic validator check when practical and update the runtime specification. Do not claim the current auditor checks something it does not.

Safe editing by change type

For landing page work:

  • Edit root index.html and, only when domain routing changes, CNAME.
  • Preserve the static, dependency-free page and the link to ./template/.
  • Check links, responsive layout, and claims against the current template.

For board work:

  • Edit template/index.html without adding a build requirement.
  • Preserve graceful handling when plan.json, history.json, or an optional activity file cannot be loaded.
  • Treat the schema as the rendering contract. Coordinate intentional contract changes across schema, sample data, renderer, validator, runtime spec, and documentation.

For planner behavior:

  • Change template/CLAUDE.md and the auditor together when an invariant or run step changes.
  • Keep the run deterministic where possible. Do not move enforceable rules into prose alone.
  • Preserve the repository-only state model. Adding a database, daemon, or bespoke transport service is an architectural change, not routine cleanup.

For sample state:

  • Remember that checked-in files demonstrate the product immediately after a fork.
  • Keep sample config, overrides, logs, plan, history, and activities mutually coherent.
  • Never treat fictional sample entries as the maintainer's real personal data.

Secrets and privacy

The workflow may receive these repository secrets:

  • CLAUDE_CODE_OAUTH_TOKEN
  • TELEGRAM_BOT_TOKEN
  • TELEGRAM_CHAT_ID
  • DISCORD_BOT_TOKEN
  • DISCORD_CHANNEL_ID

Never print, inspect, persist, commit, or copy secret values. Do not place tokens or identifiers into fixtures, examples, inbox files, logs, plan data, HTML, workflow output, screenshots, or issue text. Preserve empty-secret behavior so a fork can configure Telegram, Discord, both, or neither.

Raw inbox messages and life-planning state may be sensitive even when they do not contain credentials. Do not upload them, paste them into external tools, or expose them through the public landing page. The intended user repository is private unless its owner deliberately chooses otherwise.

Workflow and external-action limits

The workflow has contents: write permission and can fetch private messages, send messages, commit, and push. Treat those as real external side effects.

  • Do not trigger or dispatch the workflow unless explicitly requested.
  • Do not call Telegram, Discord, Claude, GitHub, Pages, DNS, or another remote service merely to test a local edit.
  • Do not send a digest, fetch live messages, commit, push, publish, deploy, or change CNAME unless the task explicitly authorizes that action.
  • Do not broaden workflow permissions or add secrets without explicit need.
  • Keep concurrency.group: planner and cancel-in-progress: false unless a requested scheduling change requires different semantics. Overlapping runs can corrupt cursor and append-only state.
  • Keep the workflow's responsibility split clear: the planning agent performs the standing run and stages changes; the final workflow step owns commit and push.

Use local inspection and the validator for ordinary verification. If a change cannot be verified without a live side effect, report that limitation instead of performing the side effect.

Verification and handoff

For every change under template/:

  1. Run python3 scripts/check_plan.py from template/.
  2. Review all touched YAML and JSON for parseability and cross-file IDs.
  3. Manually check the relevant invariants not covered by the auditor.
  4. For board changes, serve template/ locally and inspect the board at desktop and narrow widths.
  5. For workflow changes, review permissions, secret exposure, shell quoting, concurrency, and which step owns each external action.

For root landing page changes, validate the static page locally and verify the template link. There is no repository-wide build command.

At handoff, report exactly what changed, which checks passed, which checks failed, and which checks were skipped. Preserve unrelated worktree changes. Do not commit or push unless explicitly requested.