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
1 change: 1 addition & 0 deletions docs/governance/workstreams.md
Original file line number Diff line number Diff line change
Expand Up @@ -224,6 +224,7 @@ state to its index: [`docs/adr/`](../adr/README.md), [`docs/pdr/`](../pdr/README
| D208 | **A person named verifier was the author's word** (#363). On the third project, 2026-11-04, pull request #643, opened by an agent App, said `Verifier: @napkinstack-admin` over results, evidence and commits it had written itself; that account never touched the pull request. T2 compared the name with the change's authors only: the sheet passed it, and on a project that owes no approval the merge waited on T4 alone, which the author turns green by running its own tests at the head. The project's session caught it by reading | **Done**: `nstack pr-check` reads, in one GraphQL query, whose hand is on the pull request — its description's editors, its reviewers, its commenters — and T2 refuses a person named verifier who has none — each person named, so that a second name does not carry the first; a bot named with its `[bot]` suffix is the same account. A verifier session is out of scope: it acts under the same App as the author's. Unread — no token, no answer, a field refused, more than one page —, it is said as a warning, never failed; on a workstation and under `nstack attest`, with no pull request number or recording the head, it is not read. The skeleton's `pull-request.yml` gives it `PR_NUMBER` and the job's token, with `pull-requests: read`, and runs again on a submitted review, as it does on an edit of the description; a comment is read at the next run. `RULES.md`, `05-workflow.md` §7 and `verification.md` say it. A fresh verifier's findings fixed: a field refused by the forge crashed the check, a review did not start it again, a second name carried the first, and four mutants survived. Test seen red first: the case of #643 refused, each kind of hand accepted, the query's shape and its unread answers, and the workflow's wiring |
| D209 | **An unattested head read "compliant" on the forge** (#367). On a fourth project, 2026-11-07, `checks: attested`, a pull request was opened with no record on its head: the CI job "Test sheet and cycle" said `attested — judged where nstack attest runs, not in CI` and `Pull request rules: compliant.` — `check_attested` returned before reading anything under GitHub Actions, the checkout keeping no credential to fetch the notes; the status `nstack / local checks` is posted only by a session holding a token, and that one had none. Only an observer's local `pr-check` said A1 | **Done**: the record is read from the remote's notes in CI as on a workstation — the fetch carries the job's token in git's environment (`http.extraheader`), masked for the runner, never in an argument a log shows, only towards GitHub and only under GitHub Actions — on a workstation the remote's own credential answers, an expired token in the environment would refuse it; an unattested head is refused (A1), a failed or incomplete record too (A2). A remote that cannot be read — no token, a refusal — is a warning that names why, never the silent line. The A1 action says the check reads the record again at its next run, a push or an edit of the description. `07-governance.md` §7, `RULES.md` and the workflow's `contents: read` say it. Tests seen red first: a record pushed with the notes, read by a fresh checkout under CI; no record on the remote refused; a failed record refused; no remote, and a remote that refuses, warned with git's reason; notes listed but not fetched unread; the token reaching git in CI and never on a workstation. A fresh verifier's findings fixed: the token was sent on a workstation too (an expired one refused the fetch, seen on a private repository), the refusing remote was untested, git's reason could be a fragment, the secret was not masked; three mutants killed. Probed on a private repository: an installation token in the environment reads its notes |
| D210 | **`nstack claim` took an earlier cycle's start branch, then made none.** On a project, a cycle's D5 claim answered *work continues on d5-…, which exists: no other branch is made* — the start branch of an earlier cycle's D5, another title, its one empty *Start* commit never deleted on the remote: any `d<n>-…` branch counted as the deliverable's work (D149). Once that branch was deleted, claiming again only renewed the hold, and still made no work branch | **Done**: an empty branch whose first commit off the default branch is a claim's start for another title of the same id is an earlier cycle's, never this deliverable's work — the claim makes its own; a hand-named branch, and this deliverable's own start, are still taken. A renewed hold with no work branch left gets its first one again, unless the cycle's default branch has merged the deliverable; with one, the renewal says nothing of it. `project.md` says both. Six tests: two seen red first, the merge guard's seen red without it |
| D211 | **C10 read an amendment's date as a decision's due date.** On a project, the plan warned that a record's success criterion was *due at cycle 07's closure*: its criterion cited the record it amends, with the amendment's date — and another, due before a date months later, was named due at the closure its amendment date reached. PDR-0020 §6 read any `YYYY-MM-DD` of the criterion as due, inferring nothing from prose | **Done**: PDR-0023 (proposed, option B chosen by the decider) amends PDR-0020 §1 and §6 — only a date written in bold, `**YYYY-MM-DD**`, alone between the markers, is due; a plain date, an amendment's or a reference's, is not read; still nothing inferred from prose. The forms' comment, `06-decisions.md` §5, `framing.md`'s closure, `RULES.md` and the glossary say it. This repository's own records are outside C10 and keep their plain dates. Tests seen red first: an amendment date alone, an amendment date reached beside a bold date not reached, a plain date, a bold run holding words; the existing cases' due dates written in bold |
| v0.6.0 | **What is announced refuses, and the documents say what is true**: D51 to D54, and the documentary lot of the internal audit | **Done (2026-09-20)**: five workstreams merged (#83, #84, #85, #86, #87), **v0.6.0 published**, tag on `8ae544c`, verified on published artefacts — including an update of `tool-library` from v0.5.0, 9 files and no conflict, still compliant. Found doing it: D55. Next: the pilot, and M11 beside it |
| Pilot | **`NapkinStack/flet`, the first real project**, run by a session that knew nothing of this repository, with the maintainer as decider and this repository's agent as observer — public on the Free plan, where the whole checklist is available, rather than private on Team where G5 is paid and G6 does not exist | **Set aside by the maintainer, 2026-10-15**, at its cycle boundary; the real product followed since is `summit`. **Ran from 2026-09-20**: discovery decided *go* after three rounds and two independent challenges, charter accepted; cycle 01 closed `stopped/reframed`, **cycle 02 closed** with two deliverables accepted and one deferred (its #27), **cycle 03 framed and approved** (#28, #30), which starts its first `critical` module; the framework taken to v0.6.1 by itself at the boundary (#29), v0.6.2 proposed (#31). It has produced D56 to D63, D68, D70 to D77, replicated D26 and D55, and measured D49 — the first figures on what an independent verifier finds (PDR-0003) |
| M11 | **Ceremony follows consequence** (PDR-0011): the framework charges what a failure costs, per module and per project, and states each price before it is paid; with PDR-0004's routing, and PDR-0007, PDR-0008 and PDR-0010 — through to v0.7.0 | **Published as v0.7.0 (2026-10-12).** Planned 2026-09-23 (#98). Merged: **M11f** (#101), **M11a** (#102), **M11b** (#103), **M11d** (#104), **M11e** (#105), **M11c** (#106), **M11h** (#107), **M11g** (#108) closed by its probe — not clean, returned to M13; **M11m** (D68, D74, D75); **M11l** (PDR-0010, D59, D60); **M11n** (D79); **M11o** (PDR-0010 extended: every document has its moment); **M11p** (PDR-0002's three levels given a place: `docs/project/rules/`); **M11j** (PDR-0007: the unframed stage); **M11q** (no folder created empty; PDR-0012 proposed: a project's tree is the project's). **M11k** (PDR-0008's print mode: `nstack settings`; D77, D78). **M11s** (PDR-0012 accepted; the framework's files under `.nstack/`; an update carries what a project adapted); **M11u** (an update rewrites the old addresses a project wrote in its own files, D85). **M11t** (`nstack new-contract`: the contracts module is created by the first contract; PDR-0012 built in full); **M11v** (what the third project found: D81, D82, D83, D86, D87). **M11i**: v0.7.0 published 2026-10-12 (#125, D88). To do: **M11r** PDR-0008's apply mode and D57, after a probe on the real forge; the two earlier projects take v0.7.0 themselves, observed |
Expand Down
136 changes: 136 additions & 0 deletions docs/pdr/0023-a-criterion-s-due-date-is-the-one-written-in-bold.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,136 @@
# PDR-0023 — A criterion's due date is the one written in bold

- **Status**: Proposed (2026-11-10), option B chosen by the decider who assigned the defect;
accepted when a maintainer approves the pull request that carries it. It amends PDR-0020 §1
and §6 on one point, what a date of the criterion is; the rest of PDR-0020 stands
- **Date**: 2026-11-10
- **Decision makers**: NapkinStack maintainers (`@NapkinStack/maintainers`)
- **Modules affected**: the engine (the plan's rule C10), the decision forms, the framing
playbook's closure, the decisions handbook, `RULES.md`, the glossary

> The PDR describes **what the product must do and why**, never its implementation.
> The how belongs to an ADR.

---

## User problem

PDR-0020 makes a closure read the decisions whose criterion is due, and reads as due *any* date
written `YYYY-MM-DD` in the success criterion section (§1, §6). A criterion carries other dates
than the one it is due by. On a project, C10 warned that a record was *due at cycle 07's closure*
because its criterion cited the record it amends, with the amendment's date; another, due
*before* a date months later, was named due at the closure its amendment date reached.

The decider is then told to read a decision that is not due, writes a reading line that answers
nothing, and learns to dismiss the warning — the one thing the rule exists to prevent. The only
way out today is to move the amendment out of the criterion, which is where a reader looks for
it.

## Goal

A closure is told a decision is due only by the date the criterion says it is due by.

## Out of scope

- **Reading the words around a date.** *Before*, *by*, *amended*, *see*: PDR-0020 §6 infers
nothing from prose, and this keeps it so.
- **Rewriting existing records.** A record whose due date is plain stops being read as due;
the closure that reaches it says so (Decision, 4), and its author bolds it.
- **Cycles named in the criterion.** A cycle named by its file or as *cycle 03* stays due as
PDR-0020 says.
- **The reading line.** Its date stays plain: it records a reading, not a deadline.

---

## Prior art

| Product / reference | Solution chosen | What we keep from it |
|---|---|---|
| PDR-0020's own forms | The sentence *observed before **\<YYYY-MM-DD\>***, its date already in bold | **The form already marks the due date**: the rule reads the mark the form shows |
| Front matter of static sites (Jekyll, Hugo: `date`, `expiryDate`) | A date that drives behaviour is in a field of its own, never found in prose | **A date that acts is marked as such**; another date is text |
| GOV.UK Tech Docs Template (`review_in`) | The review date is a named field | The same: the deadline is where the tool is told to look |
| iCalendar, `DUE` (RFC 5545) | A due date is its own property, apart from `DTSTAMP`, `LAST-MODIFIED` | **A due date and an edit date are never the same field** |

**The convention the user already knows:** a deadline set apart from the text around it. A
field would be the strongest; the forms are prose sentences, and their due date is already bold.

---

## Options considered

| Option | What the user experiences | Cost | Chosen? |
|---|---|---|---|
| Do nothing | Amendment and reference dates warned as due; the warning dismissed | 0 | No |
| A. Only a date after *before* or *by* | Most criteria read as written — but prose inferred, which PDR-0020 §6 refuses; *on*, *until*, *at the latest* missed | Low | No |
| **B. Only a date written in bold, `**YYYY-MM-DD**`** | The form's date is due, as it is shown; any other date is text. A plain due date in an older record stops being named until bolded | Low | **Chosen** |
| C. Keep the rule; amendments moved out of the criterion | Nothing changes in the engine; every author must know to keep the section clean | 0, paid by every author | No |

## Decision

**Option B.** It amends PDR-0020 §1 and §6 on what a date of the criterion is:

1. **A date of the criterion** is a date written in bold, alone between the markers:
`**2027-01-31**`. A plain date — an amendment's, a reference's, a remark's — is not read.
A bold run holding other words, `**before 2027-01-31**`, is not one either: the rule stays
exact.
2. **Everything else in PDR-0020 stands**: the criterion section only, a cycle named by its file
or as *cycle 03*, comments not read, a reading line per closure, a warning and never a
refusal.
3. **The forms, the handbook, the playbook, `RULES.md` and the glossary say it**, where they
state the rule.
4. **A plain due date is reported, not read.** A criterion with no bold date and no cycle named,
holding a plain date a closure reached with no reading after it, gets a C10 warning of its
own: no due date C10 can read, put it in bold. A date written *amended YYYY-MM-DD* is left
out — the one phrase read, only to keep an amendment quiet; it never makes a date due.
A criterion left with no due date C10 can read is reported; a plain date beside a bold due
date is no longer read, by design.

---

## Expected behaviour

**Nominal journey.** Cycle 07 closes on 2026-11-07. A record's criterion reads *(PDR-0004,
amended 2026-11-02) … before **2027-01-31***. `nstack plan` names nothing for it; the closure
whose end reaches 2027-01-31 names it.

**Edge cases.** A criterion with only an amendment date, or only plain dates, is never due by a
date. A criterion with a bold date and a named cycle is due at both, as before. A plain due date
in a record written before this decision stops being read as due: the closure that reaches it
warns that the criterion has no due date C10 can read, and says to bold it.

**Acceptance criteria** *(testable)*:

- [ ] Given a closed cycle and a criterion with *amended YYYY-MM-DD* the closure reached and a
bold date it did not reach, then no warning.
- [ ] Given a criterion with only an amendment date the closure reached, then no warning.
- [ ] Given a criterion with a bold date the closure reached, then C10 warns, as PDR-0020 says.
- [ ] Given a plain date, or a bold run holding words, then no warning that it is due.
- [ ] Given a plain date the closure reached, with no bold date and no cycle named, then C10
warns that the criterion has no due date it can read; with an amendment date only, nothing.

---

## Success criterion

We will consider this was the right call if, on the project that met it and one other, no
closure warns of a decision for a date that was not its due date, and no due decision goes
unread for a date left plain, before **2027-03-31**.

How it is observed: the C10 warnings of their closures, against the records' criteria.

If the criterion is not met: supersede — a due date as a field of its own.

## Removal condition

This decision is removed with PDR-0020's rule C10, or superseded if a due date gets a field of
its own.

## Impacts

- **Projects**: a criterion whose due date is plain stops being read as due; the closure that
reaches it says to bold it; amendment and reference dates stop being named. No check turns red: C10 is a warning.
- **The engine**: the plan's rule C10 reads only bold dates.
- **The skeleton**: the PDR and ADR forms' comment, `06-decisions.md` §5, `framing.md`'s
closure, `RULES.md` and the glossary say it.
- **This repository**: its own records are outside C10 (PDR-0020, *Out of scope*); their plain
dates are left as they are.
3 changes: 2 additions & 1 deletion docs/pdr/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ The PDR describes **what the product must do and why**, never its implementation
| [0017](./0017-a-change-s-runs-show-what-their-logs-said.md) | A change's runs show what their logs said | **Accepted (2026-10-21)**, from #224; the how in ADR-0009; clarified 2026-10-24 (#245, #246) | 2027-03-31, the third project's end-to-end runs |
| [0018](./0018-git-is-required-the-forge-is-the-project-s-choice.md) | Git is required; the forge is the project's choice | **Accepted (2026-10-21)**, from #223; the how in ADR-0010 | 2027-06-30, a first project off GitHub |
| [0019](./0019-the-project-s-success-is-recorded-where-a-cycle-closes.md) | The project's success is recorded where a cycle closes | **Accepted (2026-10-25)**, the second step of D155's view of the project; built as D159 | 2027-03-31, two closures of the third project |
| [0020](./0020-a-closure-reads-the-decisions-whose-criterion-is-due.md) | A closure reads the decisions whose criterion is due | **Accepted (2026-10-26)**, from #279; built as D168 | 2027-03-31, two closures of the third project |
| [0020](./0020-a-closure-reads-the-decisions-whose-criterion-is-due.md) | A closure reads the decisions whose criterion is due | **Accepted (2026-10-26)**, from #279; built as D168; §1 and §6 amended by PDR-0023 (a due date is written in bold) | 2027-03-31, two closures of the third project |
| [0021](./0021-an-agent-may-approve-another-s-change.md) | An agent may approve another's change, where the project says so | **Accepted (2026-10-28)**, as proposed; built as D175; clarified 2026-10-29 (D181): the identity's independence, not the person's | 2027-04-30, 40 pull requests of the third project approved by another contributor's agent |
| [0022](./0022-agents-deliver-alone-a-person-is-asked-only-where-it-matters.md) | Agents deliver alone; a person is asked only where it matters | **Accepted (2026-11-01)**; built as D193, D194 and D196 | 2027-03-31, the third project and one new project |
| [0023](./0023-a-criterion-s-due-date-is-the-one-written-in-bold.md) | A criterion's due date is the one written in bold | Proposed (2026-11-10), option B chosen by the decider; amends PDR-0020 §1 and §6; built as D211 | 2027-03-31, the project that met it and one other |
Loading
Loading