Ctrl K
ComunidadGuías…

AGENTS.md

Rama main · réplica de lectura del repositorio original.

Archivo original ↗Preguntar a KiBot
# Bad Decisions contributor guide

> You are about to modify production infrastructure for a card game made from
> questionable decisions. Please make new questionable decisions deliberately.

This is an unofficial, unaffiliated fan project. It is absurd by design; its
validation, licenses, backups, and ZIP-bomb defenses are not.

## Project map

- `src/bad_decisions/`: engine, pack models, CLI, API, and CardDeck support.
- `src/bad_decisions/data/packs/`: bundled, validated JSON packs.
- `client/`: separate dependency-free terminal-client distribution.
- `scripts/`: reproducible import/conversion utilities.
- `man/bad-decisions.1` and `client/man/regret.1`: manual pages, installed by
  each wheel into `share/man/man1`.
- `homebrew/regret.rb`: source of truth for the client's formula in the
  `BytesAndCoffee/homebrew-tap` tap (the suggested macOS/Linux install);
  `scripts/update_homebrew_formula.py` points it at a published release.
- `tests/`: pytest suite; keep behavioral and security coverage here.
- `deploy/` and `deploy.sh`: configurable systemd/nginx deployment tooling;
  `deploy/rollback.sh` implements `deploy.sh rollback`.
- `deploy/object-archive/`: Garage object store, nginx templates, and the
  standalone catalog indexer (`indexer/catalog.py`).
- `patchnotes.md`: checklist and log of review fixes; tick the item and add a
  one-line note for every notable change.
- `.github/workflows/`: CI (`ci.yml`) and PyPI release (`release.yml`);
  `docs/RELEASING.md` documents them and `scripts/check_release_tag.py` gates tags.
- `.github/dependabot.yml`: weekly update PRs for every pinned dependency.
  Keep it current: when you add, move, or remove a `pyproject.toml`, a
  Dockerfile, a compose file that pulls an `image:`, or the workflows
  directory, add or change the matching `updates` entry in the same change.
  `tests/test_dependabot.py` fails if a manifest is not covered.
- Agent adapters (`CLAUDE.md`, `GEMINI.md`, `.cursor/rules/agents.mdc`,
  `.github/copilot-instructions.md`): thin pointers to this file; no rules.

## Working rules

- Add a test with every fix. Archive, import, and catalog changes need
  hostile-input tests.
- Preserve the API's immutable-runtime model. Packs are loaded at startup; do
  not add API endpoints that import, upload, edit, or otherwise mutate packs.
- On a rootless host, registry changes go only through `bad-decisions pack
  replace-local` and the root activator's `replace_pack` action: one explicitly
  named pack, no-overwrite publish, and exact restore on failure. Passing the
  same id updates a pack in place only when its version changes, and the
  service must then report that version. Never make the registry group-writable
  or add another path into it.
- `BAD_DECISIONS_PACK_DIR` replaces the bundled registry. Use `bad-decisions pack init-registry`
  before importing a portable pack when bundled packs should remain available.
- An omitted `packs` selector means every pack in the loaded registry (API
  `/v2/round`, CLI `--packs`, deploy smoke test). Never hard-code a default pack
  id: custom registries may not contain `base`.
- Keep CLI one-shot output clean: no banners, logs, or diagnostics on stdout.
- Maintain strict Pydantic validation and stable JSON error envelopes. Do not
  silently coerce malformed pack data.
- Changes to archive parsing require hostile-input tests. Reject traversal,
  symlinks, unexpected members, checksum failures, oversized content, and
  dangerous compression ratios before writing to disk.
- Do not weaken the non-overwrite and atomic-write behavior of pack imports.
  `import_pack` publishes with `os.link` (fails if the pack exists) after
  fsyncing a temp file. Where hard links are unsupported it falls back to an
  `O_EXCL` lock file (`.<pack>.json.lock`) plus `os.replace`. A lock left by a
  crashed importer is reported, never broken by age; a human removes it.
  `import_index` downloads and validates everything before installing and rolls
  back only files it created (matched by path, device, and inode).
- PYX COPY escapes: octal and hex escapes are raw bytes. Consecutive ones are
  decoded as strict UTF-8 (invalid runs are a `PackConfigurationError`), and
  octal above `\377` is rejected on purpose rather than wrapped.
- The catalog indexer is a standalone container image that copies only
  `catalog.py`: it must not import `bad_decisions`, and it imports `boto3`
  lazily. Its size/ratio limits are duplicated from `archive.py` and guarded by
  a drift test. Only errors caused by an archive's own contents go to
  `rejected_archives`; storage and network errors must propagate so the last
  good catalog is served. Validate configuration at startup, not per request.

## Agent conduct

- Use the repo venv: `.venv/bin/python`. Never install into the system Python.
- Run local management commands (`deploy local`, `rollback local`,
  `pack replace-local`) from an independent install (pipx, or the checkout's
  `.venv`), never through a link into `$APP_ROOT/current`.
- Do not run `deploy.sh`, `bad-decisions deploy`, `twine upload`, or `git push`
  unless the user asks. Pushing a `v*` tag publishes both packages to PyPI, so
  treat it like `twine upload`. Leave changes uncommitted unless asked to commit.
- The only remote is `origin`, the public Bad Decisions repository on GitHub
  (the old Cards Against Coffee repository is archived and is not a remote).
  Everything pushed there is public, so push only when asked.
- Privileged (`sudo`) steps belong to the human. Agents have no sudo, and sudo
  tickets are per terminal, so `sudo -v` elsewhere does not reach an agent's
  shell. Do not work around this (no sudoers edits, no cached-ticket tricks).
  The approved route: the user starts `screen -S deploy` and either runs `sudo -v`
  there in the current turn or explicitly confirms that its existing ticket remains
  valid. Then read the window first
  (`screen -S deploy -X hardcopy <file>`), send only the agreed command with
  `screen -S deploy -X stuff`, watch the output, and verify from outside
  afterward. Type nothing else into that window.
- Never read or print secrets such as service env files.
- Machine-specific facts (host names, ports, site files, production paths) stay
  out of tracked files. They live in the git-ignored `AGENTS.local.md` at the
  repo root; read it at the start of any session that touches deployment, if it
  exists. Do not keep them only in one tool's private memory or config.
- Review tools can report unreliable line numbers; re-read the code before
  fixing. When parallelizing work, give each agent a disjoint file set and keep
  `patchnotes.md` and the final full test run for one coordinator.

## Keeping agent guides in sync

People should get the same experience with any AI coding platform, so this file
is the single source of truth for rules and project facts. Never keep a rule
only in one tool's private config, memory, or settings.

- Add, change, or remove rules here only. The adapters listed in the project map
  contain no rules of their own; they just point to this file (and to
  `AGENTS.local.md`). If an agent finds a rule in an adapter, move it here.
- Supporting a new platform means adding a thin adapter and listing it in
  `tests/test_agent_guides.py`, not copying rules into it.
- `tests/test_agent_guides.py` enforces this: every adapter references this
  file, stays short, and carries no rules or machine-specific details.

## Content, attribution, and licensing

- Preserve card text, order, provenance, and licensing when migrating a source
  pack unless the task explicitly authorizes a content transformation.
- Do not add third-party card data without recorded source, license evidence,
  attribution, and redistribution compatibility.
- MAHA and owner-authorized custom material are CC BY-SA 4.0.
  The bundled `base` pack remains CC BY-NC-SA 2.0; combined distributions must
  honor its more restrictive terms.
- Do not publish raw source-message corpora or personal data. The Coffee pack
  intentionally retains only compact source references.
- The project is an unofficial, unaffiliated fan project. Do not add Cards
  Against Humanity logos, trade dress, or claims of endorsement.

## Validation

Run before committing code changes:

```bash
.venv/bin/python -m pytest
PYTHONPATH=client/src .venv/bin/python -m pytest client/tests
bash -n deploy.sh
bash -n deploy/rollback.sh
```

Deployment scripts are tested without root: `tests/test_deploy_script.py` runs
`deploy/rollback.sh` in a temp `APP_ROOT` with stub `systemctl` and `curl`, and
checks `deploy.sh` statically. Tests must never run the real `deploy.sh`.

For a server release, build and validate the exact artifacts:

```bash
.venv/bin/python -m build .
.venv/bin/python -m build client
.pypi-venv/bin/twine check dist/bad_decisions-* client/dist/bad_decisions_client-*
```

## Deployment and publishing

- `deploy.sh` must remain portable: do not hard-code a host name, domain, user
  home, site file, or provider-specific network configuration.
- Require explicit configuration for an nginx site and public URL. Keep the
  upstream loopback-only unless a deployment task explicitly says otherwise.
- Updating an existing install: use `CONFIGURE_NGINX=0` and set `PORT` to the
  running service's port (the default is 8000). A wrong `PORT` makes the health
  check probe the wrong service, so the deploy fails and rolls itself back.
- Releases are immutable directories under `$APP_ROOT/releases/<UTC id>`. A
  failed deploy restores config backups and the previous release automatically.
- `sudo ./deploy.sh rollback [RELEASE_ID]` (root only) repoints `current`,
  restarts, and waits for `/healthz`; if the target is unhealthy it restores
  the release that was serving. It changes only the release, not env, unit, or
  nginx files (backups are in `$APP_ROOT/backups/`). Unknown `deploy.sh`
  arguments are an error, so a typo cannot start a deploy.
- `$APP_ROOT/good-releases` is the rollback watermark: release IDs, oldest
  first, appended only after every deploy health check passes (last 20; seeded
  from the serving release on first use). The default rollback target is the
  newest listed release that is not current. A rollback to T keeps only entries
  `<= T`, so repeating it steps back and never rolls forward. Hosts without the
  file fall back to the newest older release with a warning.
- Local deploys are gated on the host: `deploy local` refuses a command older
  than PyPI's latest (`--allow-older`) and a pending request, and the activator
  refuses the version already serving (`--redeploy`), low disk space, and a
  release that cannot load the live `PACK_DIR` registry. Changes to
  `deploy/activate-release.py` reach a host only when a human reruns
  `sudo ./deploy.sh bootstrap-rootless`; `bad-decisions doctor --url URL` is the
  read-only post-deploy check.
- The object-archive catalog service is deployed separately with
  `deploy/object-archive/docker-compose.catalog-v3.yml`, not by `deploy.sh`.
- PyPI versions are immutable. Check the current published version and bump it
  before uploading; never rebuild different contents under an existing version.
- Releases publish from CI, not from a laptop: bump every version source (four
  files; web asset cache busters are injected from a bundle digest at runtime),
  merge to `main` with CI green, then push a `vX.Y.Z` tag to
  `origin`. `release.yml` verifies the tag matches the versions, is on `main`,
  and has a `## [X.Y.Z]` section in both changelogs, runs the tests, builds and
  `twine check`s both packages once, smoke-tests those exact wheels in a clean
  venv (`scripts/check_installed.py`), and publishes
  those files through PyPI Trusted Publishing behind the `pypi` environment
  approval. There are no PyPI tokens; never add one. See `docs/RELEASING.md`.
  Locally built artifacts are for checking only, not for uploading.
- Before tagging a release, update the manual pages (`man/bad-decisions.1`,
  `client/man/regret.1`): set the `.TH` date and version, and document every
  command, option, environment variable, and file the release adds or changes.
  Check the rendering with `man -l <page>`. `tests/test_manpages.py` fails if a
  page's version, commands, or settings drift, but it cannot check the prose.
- After a release is on PyPI, update the Homebrew formula (docs/RELEASING.md
  step 7): the tap is public, so pushing it follows the same ask-first rule as
  `git push`. Installers run no code after installing, so shell integration is
  checked by `regret doctor`, never by install-time hooks.
- Workflows must stay least-privilege: pin every action to a full commit SHA,
  default to `contents: read`, and grant `id-token: write` only to the publish
  job. `tests/test_release_workflows.py` enforces this.
- Never commit virtual environments, build artifacts, dotenv files, tokens, or
  generated local exports.
<!-- BEGIN AWS Agent Toolkit rules -->
# AWS Guidance
- Where these AWS rules conflict with the project's own instructions, the project's instructions take precedence.
- Prefer the AWS MCP Server for AWS interactions; if unavailable, use the AWS CLI directly.
- Before starting an AWS task, check for a relevant AWS skill and prefer its guidance.
- When uncertain about AWS details, verify against documentation rather than guessing.
- Prefer AWS CDK or CloudFormation over direct CLI commands when creating infrastructure.
- Follow AWS Well-Architected Framework principles.
- Do not use em dashes in AWS resource names or descriptions. Use hyphens instead.

## Secret Safety
- Load the `aws-secrets-manager` skill first for any secret, credential, API key, token, or password task.
- Do not call Secrets Manager value-retrieval APIs or access the Secrets Manager Agent directly.
- Resolve secrets at runtime with the approved `{{resolve:secretsmanager:...}}` mechanism.
<!-- END AWS Agent Toolkit rules -->