Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 13 additions & 2 deletions .agents/skills/codex-agent-init/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ Before the first external write, show a compact plan containing:
- the current or proposed local device ID and its private `50-devices/<device-id>/` scope
- the fact that `.agent-memory.local.json` and `.agent-vault` remain machine-local and ignored by Git
- the fact that `.codex/hooks.json` is reviewed public source and Codex will require one-time trust before its startup commands run
- the hourly device-local scheduler that permanently deletes expired files from the ignored seven-day `output/` delivery outbox

Resolve any correction, then run one matching command from the repository root:

Expand All @@ -64,6 +65,15 @@ python scripts/agent-memory.py setup --vault-root <path> --project-id <id>

Do not supply a device ID unless the user explicitly needs a stable custom value. Let the tool generate a unique local ID.

When Python is available, install or refresh the device-local outbox sweeper from the repository root. Before the first install, inspect `output/`. If it contains pre-policy entries and no activation state, show the exact directory and item count, then require the user to review and move that legacy directory outside the repository; never enroll it automatically.

```text
python scripts/artifactctl.py sweep --json
python scripts/artifactctl.py install-sweeper --json
```

The install command activates only an empty outbox, uses the current user's Windows Task Scheduler on Windows and the current user's crontab on macOS or Linux, pins the current script hash, and affects only this checkout's ignored `output/` path. Explain this local scheduled write before running it. Use `python scripts/artifactctl.py uninstall-sweeper` when intentionally decommissioning the checkout.

If permissions prevent writing the selected external folder or creating the local link, request only the narrow permission needed for those exact paths. Never weaken the repository's ignore or publication rules.

## 3. Verify the result
Expand All @@ -73,8 +83,8 @@ If permissions prevent writing the selected external folder or creating the loca
3. Confirm that the project ID, device ID, agent profile ID, vault path, and eligible sources are internally consistent. The full `INDEX.md` and `MEMORY_POLICY.md` are routing-only documents and must not be treated as automatic model context.
4. Confirm that `doctor` bounded its budget scan to the working-set areas, stopped once review was certain or a hard traversal cap was reached, and did not inventory large cold storage. Review any warning it emitted. Run the exhaustive read-only `budget` command when setup adopted a non-empty vault or storage size is otherwise relevant. Its default working-set, whole-vault, and single-file thresholds are 1 MiB/250 files, 100 MiB/1,000 files, and 16 KiB, with warnings at 80%; a successful aggregate report must not expose private filenames or contents.
5. With Python, run `python scripts/agent-startup-context.py --check`. Confirm `status=ready`, both event profiles, the expected compact sections, the 12 KiB and 8 KiB rendered ceilings, the expected connection mode, and a hook/script hash match. Do not print private rendered content as a setup report.
6. Confirm that `.codex/hooks.json` contains both `SessionStart` and `SubagentStart`. Explain that Codex skips new or changed non-managed hooks until the user reviews and trusts them. Use the surface's hook review UI when available; in the CLI, direct the user to `/hooks`.
7. Explain that hook trust and startup injection begin on a new or resumed task. When Python is available and the current setup turn needs the normal session profile without invoking a hook, use `python scripts/agent-startup-context.py --render --event SessionStart`.
6. Confirm that `.codex/hooks.json` contains both `SessionStart` and `SubagentStart`, that `SessionStart` also pins `scripts/artifactctl.py sweep`, and that the hourly local sweeper is installed. Explain that Codex skips new or changed non-managed hooks until the user reviews and trusts them. Use the surface's hook review UI when available; in the CLI, direct the user to `/hooks`.
7. Explain that hook trust, startup injection, and catch-up outbox cleanup begin on a new or resumed task. The independent hourly scheduler handles expiry without spending a model turn. When Python is available and the current setup turn needs the normal session profile without invoking a hook, use `python scripts/agent-startup-context.py --render --event SessionStart`.
8. Confirm the event profiles: `SessionStart` uses compact Soul, project, and durable-preference sections plus available current-device/current-handoff sections; `SubagentStart` uses only compact project plus available current-device/current-handoff sections. Confirm that only the current device profile can be selected and another device's folder is never adopted automatically. If an older vault lacks the current profile, explain that setup can add it without overwriting shared memory.
9. For a new vault, confirm that `40-documents/` is a cold, optional preservation area and is absent from bounded startup context. Older compatible vaults may omit it until the first explicitly approved original is preserved. Documents and archives still count toward the whole-vault budget even though they are never automatic context.
10. If validation fails, explain the failed invariant and preserve the existing files. Budget warnings require review but never authorize setup to delete, truncate, rewrite, move, or archive private material automatically.
Expand Down Expand Up @@ -108,6 +118,7 @@ Report:
- the selected lifecycle runner
- whether personalization is complete
- whether the startup hook is ready and trusted, and whether a new task is needed to activate it
- whether the hourly temporary-delivery sweeper is installed and that `output/` files expire permanently after seven days
- whether the connection is local to this checkout or inherited safely from a sibling worktree
- the current device ID and whether its device-scoped profile is ready
- that Drive-only coordination cannot reliably detect every active writer and parallel vault writes should be avoided
Expand Down
9 changes: 9 additions & 0 deletions .agents/skills/portable-agent-memory/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,15 @@ Read [schema-and-policy.md](references/schema-and-policy.md) before creating, pr
6. Treat all other device folders as cold, non-applicable data. Drive synchronizes them, but their contents must not configure the current device.
7. Never store credentials, cookies, authentication state, secret values, or sensitive local identifiers. Device scoping controls applicability, not who can read the synchronized file.

## Promote from the temporary delivery outbox deliberately

1. Treat an activated repository `output/` as a fixed seven-day user-delivery outbox, never as memory or a reusable library. Managed packages and unmanaged entries are permanently deleted by `scripts/artifactctl.py sweep`; do not add a `keep` marker or durable subfolder there. If legacy entries prevent activation, review and move the exact legacy directory outside the repository instead of enrolling it.
2. Before finishing a task that delivered files, decide whether future work needs the exact file or only the knowledge it contains. Summarize knowledge through the smallest justified memory record instead of retaining a large artifact.
3. When the exact file may deserve preservation, run `python scripts/artifactctl.py inspect <delivered-file>` to obtain the exact source, byte size, SHA-256 digest, and expiry. Use that evidence in the preservation proposal.
4. Do not preserve the file merely because the agent predicts reuse. Show the user the exact file, size, reason, intended relative destination under `40-documents/`, and privacy review, then require explicit approval for that file and destination.
5. After approval, acquire the advisory lease and follow the original-document workflow below. The approved preservation copy is durable; the delivery copy remains subject to its original permanent-deletion time.
6. Include the outbox path and exact expiry in the user-facing delivery. Never create a current handoff whose continuation depends on an expiring outbox file.

## Preserve an original document selectively

1. Never scan, collect, or upload local documents automatically.
Expand Down
15 changes: 11 additions & 4 deletions .codex/hooks.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,17 @@
"hooks": [
{
"type": "command",
"command": "python3 \"$(git rev-parse --show-toplevel)/scripts/agent-startup-context.py\" --expected-sha256 5a5a730c4c186e18f37419a1e367997be2476c8a6719fc45b9f528da5a3c907e",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root = (& git rev-parse --show-toplevel).Trim(); & python (Join-Path $root 'scripts/agent-startup-context.py') --expected-sha256 5a5a730c4c186e18f37419a1e367997be2476c8a6719fc45b9f528da5a3c907e\"",
"command": "python3 \"$(git rev-parse --show-toplevel)/scripts/agent-startup-context.py\" --expected-sha256 933a84ec9fe44059a672dd3edaebf0f4b681983ed904c8dfe799106671de3b6d",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root = (& git rev-parse --show-toplevel).Trim(); & python (Join-Path $root 'scripts/agent-startup-context.py') --expected-sha256 933a84ec9fe44059a672dd3edaebf0f4b681983ed904c8dfe799106671de3b6d\"",
"timeout": 30,
"statusMessage": "Loading Codex Agent memory"
},
{
"type": "command",
"command": "python3 \"$(git rev-parse --show-toplevel)/scripts/artifactctl.py\" sweep --quiet --expected-sha256 045c2042e5b649cf3caba3ab6e98a48ccf0bd70368d3c3abbe61eba4dc9b72ba",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root = (& git rev-parse --show-toplevel).Trim(); & python (Join-Path $root 'scripts/artifactctl.py') sweep --quiet --expected-sha256 045c2042e5b649cf3caba3ab6e98a48ccf0bd70368d3c3abbe61eba4dc9b72ba\"",
"timeout": 30,
"statusMessage": "Cleaning expired delivery artifacts"
}
]
}
Expand All @@ -19,8 +26,8 @@
"hooks": [
{
"type": "command",
"command": "python3 \"$(git rev-parse --show-toplevel)/scripts/agent-startup-context.py\" --expected-sha256 5a5a730c4c186e18f37419a1e367997be2476c8a6719fc45b9f528da5a3c907e",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root = (& git rev-parse --show-toplevel).Trim(); & python (Join-Path $root 'scripts/agent-startup-context.py') --expected-sha256 5a5a730c4c186e18f37419a1e367997be2476c8a6719fc45b9f528da5a3c907e\"",
"command": "python3 \"$(git rev-parse --show-toplevel)/scripts/agent-startup-context.py\" --expected-sha256 933a84ec9fe44059a672dd3edaebf0f4b681983ed904c8dfe799106671de3b6d",
"commandWindows": "powershell -NoProfile -ExecutionPolicy Bypass -Command \"$root = (& git rev-parse --show-toplevel).Trim(); & python (Join-Path $root 'scripts/agent-startup-context.py') --expected-sha256 933a84ec9fe44059a672dd3edaebf0f4b681983ed904c8dfe799106671de3b6d\"",
"timeout": 30,
"statusMessage": "Loading Codex Agent memory for subagent"
}
Expand Down
2 changes: 2 additions & 0 deletions .github/workflows/smoke.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@ jobs:
python-version: "3.x"
- name: Run cross-platform Python lifecycle test
run: python tests/smoke.py
- name: Test temporary delivery expiry
run: python tests/artifactctl_test.py
- name: Verify Python and PowerShell interoperability
shell: pwsh
run: ./tests/interop.ps1
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ secrets/
# Test and editor noise.
/.tmp/
/tmp/
/output/
__pycache__/
*.pyc
*.log
Expand Down
18 changes: 18 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,24 @@ When the trusted repository hook injects approved portable memory, apply it only
- Treat the project lease as advisory. Use only one writing computer/task per project-ID folder at a time and wait for Google Drive to finish syncing before switching computers.
- Before editing `00-system/SOUL.md`, warn that an unsynchronized writer may exist. Ask the user to confirm that other vault-writing tasks are stopped and Drive is up to date, or to explicitly accept the concurrency risk. After that confirmation and exact content approval, acquire the advisory lease and proceed; do not turn the warning into a permanent block.

## Temporary delivery outbox

- Treat `output/` only as an expiring outbox for files being delivered to the user. It is ignored by Git, publication-blocked, and must never be used as durable memory, a reusable library, or the only copy of resumable work.
- Put deliverables into a managed seven-day package with `python scripts/artifactctl.py deliver --slug <short-name> <files...>`. The lifetime is fixed, not caller-selectable. Do not write directly under an activated `output/`; unmanaged entries are enrolled for permanent deletion seven days after the sweeper first sees them.
- Before the final response, decide whether each delivered file has durable value. If only the knowledge matters, record the smallest justified candidate, decision, lesson, or handoff through the normal memory workflow. If the exact file matters, run `python scripts/artifactctl.py inspect <delivered-file>` and propose preservation with its exact source, byte size, reason, intended `40-documents/` destination, and privacy review.
- Preserve an exact file only after the user explicitly approves that file and destination. Use the advisory lease and the `$portable-agent-memory` preservation workflow; do not create an exemption, `keep` marker, or permanent subfolder inside `output/`. The delivery copy remains expiring even after an approved preservation copy exists.
- State the absolute delivery path, exact `expires_at` time, and permanent-deletion policy in the final response. Never make a handoff depend on an expiring file.
- Activate cleanup only on an empty outbox. If an existing checkout has pre-policy `output/` entries, do not enroll them: inspect and move the exact legacy directory outside the repository first, then install the sweeper. The hash-pinned `SessionStart` handler fails closed while unreviewed legacy entries remain.
- The hash-pinned `SessionStart` sweeper and the installed hourly local scheduler delete expired managed packages permanently. A machine that was off catches up at the next scheduler run or trusted session start. `scripts/artifactctl.py uninstall-sweeper` removes the device-local schedule when intentionally decommissioning a checkout.

## Script lifecycle

- Before creating a script, classify it as durable repository tooling or a task-local helper. Put one-off helpers in an external temporary workspace, not `scripts/` and not the delivery `output/` outbox.
- Every new or materially rewritten script intended to remain under `scripts/` must start with a concise comment or docstring recording `Purpose` (why it exists), `Retention` (why it should remain reusable), and `Remove when` (an objective condition for future deletion). Place this metadata after any required shebang or language directive.
- At a meaningful checkpoint and before the final response, review every helper script created by the current task. If it is no longer required by a checked-in workflow, hook, test, documented operator command, or clearly justified reuse case, delete it together with task-created caches and auxiliary files. Do not retain it merely because it might be useful someday.
- Self-cleanup authority extends only to files created by the current task or clearly marked as Agent-temporary. Never delete a pre-existing, user-created, tracked, or ambiguously owned script based only on age, low reference count, or apparent obsolescence; report it as a cleanup candidate and obtain approval.
- When promoting a temporary helper to durable tooling, move it into `scripts/`, add the lifecycle header, and add the smallest appropriate test or documentation reference that proves why it remains.

## At a meaningful checkpoint

- Create a unique shared handoff only when a later task would otherwise need to rediscover unfinished or non-obvious state. Use the selected lifecycle runner's `new-handoff` command and the exact current lease ID. Keep the handoff compact, and change `status` from `draft` to `current` only when it represents actively resumable state.
Expand Down
9 changes: 9 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ Run from the repository root:

```powershell
python ./tests/smoke.py
python ./tests/artifactctl_test.py
pwsh -NoProfile -File ./tests/smoke.ps1
pwsh -NoProfile -File ./tests/interop.ps1
pwsh -NoProfile -File ./tests/prepublish-smoke.ps1
Expand All @@ -23,8 +24,16 @@ Python is the preferred cross-platform lifecycle runtime. Windows PowerShell 5.1
- Do not weaken ignore rules or make the tool follow the `.agent-vault` link during publication checks.
- Keep `.codex/hooks.json` as the only tracked project source under `.codex`. Never publish Codex logs, caches, approvals, or generated state.
- When changing `scripts/agent-startup-context.py`, update all four pinned SHA-256 occurrences in `.codex/hooks.json`. The publication check rejects stale pins, and users must review the changed hook definition again.
- When changing `scripts/artifactctl.py`, update both pinned SHA-256 occurrences in the `SessionStart` hook and refresh the device-local sweeper schedule. Never commit `output/`; it is a seven-day temporary delivery outbox.
- New automatic promotion behavior must remain opt-in and reviewable. `SOUL.md`, approved lessons, and durable preferences are human-governed.

## Script lifecycle

- Keep one-off helpers outside the repository. `scripts/` is for durable operator or lifecycle tooling, not task residue.
- Begin every new or materially rewritten durable script with a concise `Purpose`, `Retention`, and `Remove when` comment or docstring so later maintainers can judge whether it still belongs.
- Delete task-created temporary scripts, caches, and auxiliary files when their work is complete. Do not delete pre-existing or ambiguously owned scripts without explicit approval.
- A script promoted into `scripts/` should have the smallest useful test or documentation reference demonstrating its durable role.

## Pull requests

Explain the behavior change, the threat or failure mode it addresses, and the checks you ran. Keep platform-specific behavior explicit.
Loading