How should I work here?
The loop, the change table, who may approve what, the project commands, and the definition of done. The only place commands are written.
Portable Agent Guide System: a small set of records and rules for repositories where AI agents do much of the work.
Agents forget between sessions. They add scope nobody asked for, change the architecture quietly, and make up reasons for old code. PAGS gives every project one written answer to each question an agent should ask before changing anything, plus a loop that keeps those answers current.
Each session starts with no memory of the last one. Without written records, small, reasonable-looking changes add up to a project nobody planned.
An agent adds a feature because it seemed useful. Nobody approved it, and now it has to be maintained.
PAGS: new capabilities need an approved roadmap item first.
A refactor swaps a pattern or adds a dependency. The next session cannot tell whether it was intended.
PAGS: hard-to-reverse choices get a decision record.
In old code, agents guess why something was done and state the guess as fact.
PAGS: facts are marked observed, intended, or unknown.
Records that change at the same pace live together. Intent (the charter) and reality (the architecture) stay apart, because keeping them apart is what makes PAGS honest about existing code. IDs like R-003 T-014 D-012 A-001 let entries point at each other.
The loop, the change table, who may approve what, the project commands, and the definition of done. The only place commands are written.
Mission, goals, non-goals, principles, constraints, and the maintainer's name. One page. Only the maintainer edits it.
The Now block, then outcomes with their tasks indented underneath, then unplanned fixes and ideas for later.
Decisions, dependency reasons, and exceptions. An exception is a decision with an end date and an exit condition.
Structure, flows, interfaces, invariants, and known debt. Unconfirmed facts are marked observed or unknown.
The user-facing contract. Any change to visible behavior updates it in the same change.
Terms, states, visual language, and accessibility rules, for projects with an interface.
Done tasks, finished outcomes, and replaced decisions move here, so the live files stay short.
Every entry starts with a headline line: ID, status in brackets, a summary, and links. Indented key: value lines add detail only where it earns its place. The headlines are the index, so there are no summary tables to keep in sync.
- R-003 [approved] Offline edits sync when the network returns
approved-by: sam 2026-09-20
- T-014 [doing] Queue writes while offline -> D-012
done-when: queued writes survive an app restart
- T-015 [todo] Resolve conflicts on reconnect
- D-012 [accepted 2026-09-20] Use SQLite for the write queue -> R-003 why: writes must survive restarts on mobile cost: adds a native build step replaces: D-004 - D-015 [exception until 2026-12-01] Skip e2e tests on Windows CI exit: runner image ships WebView2
$ grep -nE '^\s*- [RTDA]-[0-9]+' .pags/*.md
flowchart RL T["T-014 · task"] -->|indented under| R["R-003 · outcome"] T -->|follows| D["D-012 · decision"] D2["D-019 · newer decision"] -->|replaces| D R -->|must fit within| C["CHARTER"] D -->|reshapes| A["ARCHITECTURE"] E["D-015 · exception"] -->|bends a rule in| AG["AGENTS.md"]
[replaced] before moving to the archive.Each session goes through the same steps, from reading the handoff to writing the next one. The step that matters most is the scope check: work outside approved scope becomes a proposal and is not implemented.
flowchart TD
S([Session starts]) --> N[Read the Now block in WORK.md]
N --> K[Classify the change]
K --> L[Look up its row in the change table]
L --> Q{Inside approved scope?}
Q -- No --> P[Write a proposal]
P --> H{Maintainer approves?}
H -- Not yet --> W([Record it in the Now block and stop])
H -- Yes --> REC[Record who approved and when]
REC --> T
Q -- Yes --> T[Create or update the task]
T --> DQ{Hard to reverse?}
DQ -- Yes --> D[Record a decision]
DQ -- No --> I
D --> I[Make the smallest coherent change]
I --> V[Run tests and pags check]
V --> VQ{All green?}
VQ -- No --> I
VQ -- Yes --> U[Update records and archive finished entries]
U --> NB[Rewrite the Now block]
NB --> END([Session ends])
One table in AGENTS.md replaces separate rules for classifying, reading, and recording. An agent finds its row, reads only what that row lists, and updates only what it names.
| Change | Read first | Then update |
|---|---|---|
| New capability | Charter, Work | Outcome approval, then a task |
| Scope or non-goal change | Charter, Work | Proposal approval, then Charter, Work, and a decision |
| Architecture or public interface | Architecture, Decisions | Decision, Architecture |
| New or removed dependency | Decisions | Decision starting “Add dependency:”, lockfile |
| Refactor | Architecture, relevant decisions | Task; Architecture if the structure changes |
| Security change | Decisions, Architecture | Task; a decision if a policy changes |
| Bug fix | Architecture for the affected area | Task under Unplanned, regression test |
| User-visible behavior | README | README, in the same change |
| Temporary rule deviation | The rule being bent | Exception in Decisions, with end date and exit approval |
| Maintenance, formatting, doc fixes | Nothing extra | Nothing new, as long as scope is unchanged |
If a row names a record the project does not have yet, the agent creates it from the template. A minimal install never leaves an agent pointing at a file that does not exist.
Each record type has a short, fixed list of statuses written in brackets. pags check rejects any status that is not on the list. Finished entries move to an archive file.
stateDiagram-v2 [*] --> proposed proposed --> approved: maintainer approves proposed --> dropped approved --> doing doing --> done: its tasks are done approved --> dropped done --> [*]: moved to archive
stateDiagram-v2 [*] --> todo todo --> doing doing --> blocked blocked --> doing doing --> done: evidence recorded todo --> dropped done --> [*]: moved to archive
stateDiagram-v2 [*] --> proposed proposed --> accepted: maintainer accepts proposed --> rejected accepted --> replaced: a newer decision replaces it rejected --> [*] replaced --> [*]: moved to archive
stateDiagram-v2 state "exception until date" as exception [*] --> exception: maintainer grants it exception --> resolved: exit condition met exception --> exception: maintainer extends the date resolved --> [*]: moved to archive
A new project starts with a one-page charter and grows its records as it grows. An existing project starts by describing what is there, without pretending anyone planned it.
A new repository with little or no code.
An existing codebase with history and habits.
Seen in the code or its behavior today. Goes in ARCHITECTURE, marked (observed).
Someone with authority decided it. Goes in CHARTER, WORK, or DECISIONS.
Nobody knows why. Written as why: unknown and never guessed.
flowchart LR
X["Existing code that breaks the new rules"] --> Q{"What is it?"}
Q -->|We accept it| L["Accepted legacy"] --> A1["ARCHITECTURE · A- accepted"]
Q -->|We plan to change it| M["Planned migration"] --> A2["WORK · R- outcome"]
Q -->|Allowed for now| E["Temporary exception"] --> A3["DECISIONS · exception until date"]
Q -->|It is simply wrong| B["Defect"] --> A4["WORK · T- under Unplanned"]
One Python script with no dependencies sets PAGS up in any repository. It never overwrites your work silently: the default at every prompt is the safe choice, and it previews every change before writing.
flowchart TD
A(["Run scripts/install-pags.py"]) --> B["Detect from commit count and non-doc files"]
B --> C{"New or existing project?"}
C -->|New| G["Greenfield starter set"]
C -->|Existing| BF["Brownfield set, including ARCHITECTURE"]
G --> P["Preview every file it will touch"]
BF --> P
P --> F{"AGENTS.md or README.md already there?"}
F -->|Yes| MS["Insert a marked PAGS section, keep the rest"]
F -->|No| NW["Create from template"]
MS --> AP["Apply after you confirm"]
NW --> AP
AP --> CK["Install .pags/check.py"]
CK --> MF["Write .pags/manifest with version and template hashes"]
MF --> CL["Add CLAUDE.md that imports AGENTS.md"]
CL --> NX(["Print next steps for this project type"])
Agents follow written advice less reliably as their context fills up. pags check is a small script, installed as .pags/check.py, that runs before each commit and in CI. It fails the build when the records stop agreeing with each other.
flowchart LR E["Edit code and records"] --> C["git commit"] C --> H["pre-commit runs pags check"] H --> CI["CI runs pags check and tests"] CI --> M(["Merge"]) H -. fails .-> E CI -. fails .-> E
.pags/DECISIONS.md:9: error: D-015 exception ended 2026-09-01; resolve it or ask the maintainer .pags/WORK.md:14: error: T-022 links to R-009, which does not exist .pags/WORK.md:31: warning: T-018 is [done]; move it to WORK-ARCHIVE.md FAIL 41 entries, 2 error(s), 1 warning(s)
The Now block sits at the top of WORK.md. It is rewritten at the end of each session, not appended to, so it stays short enough for the next agent to read first.
## Now - Focus: R-003 offline sync. T-014 is in progress. - Last session: Added the retry queue. Tests pass. D-012 accepted. - Next: Handle conflicts on reconnect (T-015). - Waiting on: Maintainer approval for the R-004 proposal. <!-- Rewrite this block at the end of every session. -->
Spec-driven tools help write a spec for each feature. PAGS governs the whole project over time, so the two work well together.
These parts of the design have options on the table but no decision yet. Each links to its issue.
A hidden .pags/ folder is skipped by ripgrep and many agent search tools. A visible docs/pags/ folder avoids that.
Two branches that each add a decision will both pick the next number. The archive keeps files short, but the ID scheme still needs a fix, or pags check needs to catch duplicates at merge.
The rule in §6 is settled in principle. The open question is whether the name goes in the charter, AGENTS.md, or a CODEOWNERS-style line.