@debonet/idd 0.0.0-stage → 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,42 @@
1
+ # idd-merge-hook
2
+
3
+ Statement ids: complete
4
+
5
+ ## Purpose
6
+
7
+ The Git hook command that makes agents reconcile project questionnaire
8
+ branches before completing a code-branch merge.
9
+
10
+ ## API
11
+
12
+ * {#s-e4kj5b <arch} `npm exec -- idd-merge-hook install` writes executable IDD
13
+ hooks to `.githooks/` and sets the clone's `core.hooksPath` to that
14
+ directory. The hooks invoke the package through `npm exec -- idd-merge-hook`.
15
+ In a Concord project, its pre-commit hook also runs the Concord spec gate.
16
+ It is repeatable and refuses a different configured hook directory or
17
+ existing default hooks that it cannot preserve.
18
+ * {#s-t0r5nn <arch} `idd-merge-hook check` exits 0 only when the configured hook
19
+ directory and all required executable hook files and commands are present.
20
+ * {#s-sjb7rh <arch} `idd-merge-hook reviewed` can run only during a code merge;
21
+ it fetches `origin` and records a review only when the source IDD branch is
22
+ an ancestor of the target IDD branch.
23
+ * {#s-02z196 <arch} A review record binds the source and target code commits,
24
+ source and target code branch names, and both IDD branch commits. A later
25
+ change to any bound commit invalidates that review.
26
+ * {#s-sj7f9n <arch} During an active code merge, `pre-commit` and
27
+ `pre-merge-commit` fetch `origin` and exit 0 only when no project
28
+ questionnaire branches exist or a matching review record covers an IDD
29
+ source history already merged into the target and the local IDD branches
30
+ have no unpublished commits. Otherwise they print the source and target
31
+ branches and the required IDD reconciliation steps, then exit 1. Outside a
32
+ code merge they exit 0 without fetching.
33
+ * {#s-wjofsg <arch} After a code merge, `post-merge` fetches `origin`, verifies
34
+ that the reviewed source IDD history remains in the target IDD branch, and
35
+ removes the review record. It exits unsuccessfully if the review or
36
+ reconciled history no longer matches.
37
+
38
+ ## Dependencies
39
+
40
+ * {#p-cqaul6 >specs/guidance/agent-merge.md} The agent's semantic reconciliation
41
+ process for duplicate, obsolete, or conflicting questions.
42
+ * {#p-gpf7kp >specs/build.md} The package command and repository hook setup.
@@ -0,0 +1,367 @@
1
+ # idd
2
+
3
+ Statement ids: complete
4
+
5
+ ## Purpose
6
+
7
+ The npm-installed command that prints IDD's workflow, creates a project's
8
+ page file and question file, checks them, builds the questions page from
9
+ them, adds questions with branch-scoped hash ids, records and clears answers
10
+ in the project's answer and pending answer files, and imports questions
11
+ from a page made from the older template; it also reports session status,
12
+ claims work items between agent sessions, prints a hosted viewer link, and
13
+ counts questions on an IDD branch.
14
+
15
+ ## His words
16
+
17
+ > {#m-f2iwx9} From his instructions file `~/.claude/CLAUDE.md`, section
18
+ > "Questions for me go on a web questions page", as read 2026-11-04: "Every
19
+ > time a question is added: insert the question object before the closing
20
+ > `];` of `vaQuestion`, republish to the same URL, and open the page in my
21
+ > local Chrome"
22
+
23
+ > {#m-xoqfn9} From this session, 2026-11-11: “we should offer the agents tools to
24
+ > quickly get a count of conflicts/unanswered/answered/suspended questions.
25
+ > It should by default pull the current idd-<branch> to make sure its synched.
26
+ > It can have a --nosync option to turn that off. And flags for returning just
27
+ > one of the counts, e.g. --type {c,u,a,s,conflicts,unanswered,answered,suspended}.”
28
+
29
+ > {#m-xhv3ci} In answer to the IDD question “What should the suspended count
30
+ > measure?”, he wrote: “I meant \"superseded\" not \"suspended\"”
31
+
32
+ ## API
33
+
34
+ * {#s-4lt6mo <m-f2iwx9@98efad1} **One command.** The command is `idd
35
+ <subcommand> [arguments]`, installed by the public `@debonet/idd` npm
36
+ package and run from the IDD data worktree; {#s-s1eg2z <arch} it reads and
37
+ writes every project path relative to that worktree, never writes outside
38
+ it, and finds its own resources relative to its installed package.
39
+ * {#s-hsle36 <arch} **`guide`** prints the package's current canonical
40
+ adoption and session instructions to stdout and exits 0 without requiring
41
+ a project page or changing files.
42
+ * {#s-7wfrt9 <arch} **`viewer-url <owner/repository> <code-branch>`** prints
43
+ a direct link to that repository branch's hosted questionnaire. It URL
44
+ encodes both values and uses `IDD_VIEWER_URL` when set, otherwise the
45
+ package's configured public viewer address. It does not read a GitHub
46
+ credential or change files.
47
+ > {#m-umjnwb} BUG REPORT (CEO), 2026-11-13: “Somehow the new agent got this for a
48
+ > login page: https://idd-service-778162731919.us-east4.run.app/?repo=AblifyAI%2Fablify&branch=main
49
+ > It should be using interrogation.dev everywhere (that does not work with
50
+ > github sign in!)”
51
+ * {#s-q3kge1 <m-umjnwb@4a707e9} When `IDD_VIEWER_URL` is unset, both `viewer-url` and `wait` use
52
+ `https://interrogation.dev` as their origin. They never use the Cloud Run
53
+ `run.app` address as a viewer link or event endpoint, so GitHub sign-in stays
54
+ on the registered custom domain.
55
+ * {#s-dal2u7 <arch} **`wait <question-id> [interval-seconds]`** subscribes to
56
+ the hosted viewer's event stream and checks the matching remote IDD branch
57
+ through the current worktree's configured Git remote when the wait starts
58
+ and when an invalidation arrives. It defaults to a 30-second interval and
59
+ accepts intervals from 1 to 3600 seconds; it checks the remote branch at
60
+ that interval while push delivery is unavailable or the event stream is
61
+ disconnected. When the question has an accepted answer, it fast-forwards
62
+ the clean local data worktree, records that already-accepted answer in
63
+ `idd/pending.json` without rewriting `idd/answers.json`, prints the answer
64
+ as JSON and exits 0.
65
+ * {#s-854kw7 <arch} `wait` refuses to run from a dirty worktree, does not
66
+ commit or push, and uses the repository's configured Git credentials
67
+ without storing or displaying them. It exits 1 if the remote cannot be
68
+ fetched or fast-forwarded, and exits 130 when interrupted.
69
+ * {#s-dvurdo <arch} **Usage errors.** With no subcommand, an unknown one, or the
70
+ wrong number of arguments, it prints a usage line to stderr, writes
71
+ nothing and exits 2.
72
+
73
+ ## branch-hash
74
+
75
+ * {#s-2bfbql <arch} **`branch-hash <repository-id> <full-ref>`** prints the lowercase
76
+ SHA-256 digest of the UTF-8 JSON serialization of `[String(repository-id),
77
+ full-ref]` and exits 0; `<full-ref>` is a full Git ref such as
78
+ `refs/heads/main`.
79
+ * {#s-0ys3dr <arch} **Refused input.** When the repository id is not a positive
80
+ decimal integer or the full ref does not begin with `refs/heads/` and name
81
+ a non-empty branch, the command prints the problem to stderr and exits 2.
82
+
83
+ ## status
84
+
85
+ * {#s-zqk718 <arch} **`status <session-id> <blocked|working|clear>`** updates
86
+ `idd/status.json`: `blocked` and `working` store the selected boolean
87
+ and the current ISO 8601 `updatedAt` under the session id; `clear` removes
88
+ that entry and leaves an empty object when it was the last one.
89
+ * {#s-qvff9k <arch} **Session id and file validation.** A session id is a non-empty
90
+ string of at most 128 ASCII letters, digits, periods, underscores, colons or
91
+ hyphens. The command refuses malformed status data without changing the
92
+ file with exit 1; an invalid session id or state exits 2. `check` validates
93
+ every entry by `specs/hosted-questionnaire.md`. Clearing an absent entry
94
+ succeeds without writing the file.
95
+
96
+ ## Work claims
97
+
98
+ * {#s-2jdc1d} **`claim <kind> <item-id> <session-id>`** records that one active
99
+ session owns an answer, thought or conflict work item in `idd/claims.json`.
100
+ It refuses to replace a claim held by another session and reports the
101
+ current owner; repeating a claim by its owner succeeds without changing its
102
+ timestamp.
103
+ * {#s-ky6iv4} **`release <kind> <item-id> <session-id>`** removes a claim only
104
+ when the named session owns it. Releasing an absent claim succeeds; a
105
+ different owner is reported and the file is unchanged.
106
+ * {#s-7pdsig} A claim kind is `answer`, `thought` or `conflict`; an
107
+ item id is a lowercase 64-character hexadecimal id; and a session id follows
108
+ the status command's validation rule. Invalid arguments exit 2. Malformed
109
+ claim data is refused without changing the file and exits 1.
110
+ * {#s-7uz5pq} Claiming authorizes work only after the claim has been
111
+ committed and pushed to the matching IDD branch. A rejected push means the
112
+ session must sync and inspect the remote claim before it starts work.
113
+
114
+ ## count
115
+
116
+ * {#s-vwase3 <m-xoqfn9@0df3826} **`count`** reports the counts of conflicting, unanswered,
117
+ answered and superseded questions in the current IDD data branch. With no
118
+ `--type` option, it prints one `<type>: <count>` line for each, in that order;
119
+ with `--type <type>`, it prints only that count as a decimal integer.
120
+ * {#s-4w8iuj <m-xoqfn9@0df3826,m-xhv3ci@566158e} **Count types.** `--type` accepts `c`, `u`, `a`, `s` and their
121
+ full names `conflicts`, `unanswered`, `answered`, and `superseded`. `s` is
122
+ the alias for superseded questions.
123
+ * {#s-83u8i8 <arch} **Conflicting questions.** Count each active question once when
124
+ at least one rejected answer has a different choice or note from its
125
+ accepted answer, or when it has a rejected answer and no accepted answer.
126
+ * {#s-1mid3a <arch} **Unanswered questions.** Count each active question once when
127
+ it has neither an accepted answer with a choice or note nor a conflicting
128
+ rejected answer.
129
+ * {#s-6uvlo6 <arch} **Answered questions.** Count each active question once when
130
+ it has an accepted answer with a choice or a non-empty note and no
131
+ conflicting rejected answer.
132
+ * {#s-t8xsan <m-xhv3ci@566158e} **Superseded questions.** Count the entries in the IDD branch's
133
+ question history file; each entry is one superseded question.
134
+ * {#s-jpjbwh <m-xoqfn9@0df3826} **Sync by default.** Unless `--nosync` is supplied, `count`
135
+ requires a clean Git worktree checked out on an `idd-<branch>` branch,
136
+ fetches `refs/heads/<current-branch>` from `origin`, and fast-forwards the
137
+ worktree before reading question data. It never commits or pushes.
138
+ * {#s-yufw52 <m-xoqfn9@0df3826} **No sync.** `--nosync` skips the fetch and fast-forward and
139
+ counts the current worktree contents without requiring it to be clean.
140
+ * {#s-14wrxl <arch} **Usage errors.** `--type` requires one supported type, may be
141
+ supplied once, and `--nosync` may be supplied once. Unknown options,
142
+ unsupported types or repeated flags print usage to stderr and exit 2.
143
+ * {#s-amd1ad <arch} **Sync errors.** With `idd/page.json` present, a non-IDD
144
+ branch, dirty worktree during sync,
145
+ fetch failure or non-fast-forward update prints an error to stderr and exits
146
+ 1 without changing tracked questionnaire files. Invalid question data exits
147
+ 1 with the validation problems on stderr and prints no counts.
148
+
149
+ ## thoughts
150
+
151
+ * {#s-8hvj7z <arch} **`thoughts [--nosync]`** lists the project's thoughts from
152
+ `idd/thoughts.json` in append order, printing each thought's id, timestamp
153
+ and plain text. It prints `idd: no thoughts` when the file is absent or
154
+ empty. It does not change files.
155
+ * {#s-wapj39 <arch} By default, `thoughts` requires a clean worktree checked out on
156
+ an `idd-<branch>` branch, fetches the matching remote branch from `origin`
157
+ and fast-forwards before reading. `--nosync` skips synchronization and
158
+ reads the current worktree contents. It never commits or pushes.
159
+ * {#s-twjecx <arch} `thoughts` validates each record against
160
+ `specs/project-files.md`; invalid data prints problems to stderr, prints no
161
+ thoughts and exits 1. A non-IDD branch, dirty worktree during sync, fetch
162
+ failure or non-fast-forward update also exits 1 without changing tracked
163
+ questionnaire files.
164
+ * {#s-g5gxep <arch} `check` validates `idd/thoughts.json` when present, including
165
+ exact members, non-empty text, valid branch hash, ISO 8601 timestamp,
166
+ unique ids, and each id's digest.
167
+
168
+ ## init
169
+
170
+ * {#s-51ncq9 <arch} **`init <title> <branch-hash>`** writes `idd/page.json`
171
+ holding `title` set to `<title>`, `url` set to `null`, and `branchHash`
172
+ set to `<branch-hash>`, and `idd/questions.json` holding an empty array,
173
+ then exits 0.
174
+ * {#s-cd2jke <arch} **It never overwrites.** When either file exists, `init` writes
175
+ nothing, prints a line naming the existing file to stderr and exits 2.
176
+
177
+ ## check
178
+
179
+ * {#s-hcsr5l <m-f2iwx9@98efad1} **`check`** reads both files and prints to stdout one line per
180
+ way they differ from `specs/project-files.md`, each `<file>: <problem>`
181
+ naming the question id where the problem is in a question, and exits 1
182
+ when it printed any; {#s-9exkpo <arch} with no problem it prints `idd: ok` and
183
+ exits 0.
184
+ * {#s-2b5w6i <arch} **What it catches.** It reports a file that is not JSON, a
185
+ member missing, of the wrong form or not named by
186
+ `specs/project-files.md`, a branch hash that is not 64 lowercase
187
+ hexadecimal characters, a question id that differs from its hash of the
188
+ question's own `branchHash` and content, an id two questions share,
189
+ an options array of fewer than two or more than four options, and a key
190
+ two options of one question share, or a `createdAt` that is not an ISO
191
+ 8601 time; it also checks question snapshots and replacement links in
192
+ `idd/history.json`, and accepts answers and pending answers for active or
193
+ historical question ids; when `idd/conflicts.json` exists, it validates
194
+ every rejected answer against its question and options.
195
+ * {#s-pmnqlm <arch} **The answer file.** When `idd/answers.json` exists, `check`
196
+ also reports each way it differs from `specs/project-files.md`, The
197
+ answer file: a file that is not JSON or not an object, a member whose
198
+ name is no question's id, a value missing a member, holding another or
199
+ holding one of the wrong form, and a `choice` that is neither `""` nor a
200
+ key of its question's options. {#s-a1zj4t <arch} An absent answer file is not a
201
+ problem.
202
+ * {#s-ikua3t <arch} **The pending answer file.** When `idd/pending.json` exists, `check`
203
+ reports each way it differs from `specs/project-files.md`, The pending
204
+ answer file, including an answer that differs from the answer file.
205
+ * {#s-sse100 <arch} **No page file.** When `idd/page.json` does not exist, every
206
+ subcommand but `init`, `guide`, `viewer-url` and `branch-hash` exits 2 and
207
+ names the missing page file on stderr. `import`, when the question file does
208
+ not already hold questions, reports the missing page through its own import
209
+ error because it needs the page's branch hash to create question ids.
210
+
211
+ ## build
212
+
213
+ * {#s-qzz6xu <m-f2iwx9@98efad1} **`build`** runs the check; when the check reports
214
+ any problem it prints the problems, writes nothing and exits 1;
215
+ otherwise it writes `tmp/idd/page.html`, creating `tmp/idd/` when absent,
216
+ prints that path and exits 0.
217
+ * {#s-gcsqgg <m-f2iwx9@98efad1} **The built page.** The built page is the questions page
218
+ (`specs/questions-page.md`) drawing exactly the question file's questions
219
+ in its order, titled with the page file's title.
220
+ * {#s-t2pr4c <arch} **Any text survives.** A title, body, option text or tag holding
221
+ any characters, `</script>`, quotes, backslashes and non-ASCII text
222
+ among them, is drawn by the built page exactly as the question file holds
223
+ it.
224
+
225
+ ## add
226
+
227
+ * {#s-mvtsgu <m-f2iwx9@98efad1,m-edvy4l@54198de} **`add`** reads from stdin one JSON object
228
+ holding exactly the members `title`, `body` and `options`, each in the
229
+ form `specs/project-files.md` gives a question's (The question file),
230
+ appends the reserved X option from `specs/project-files.md` when absent,
231
+ computes its id as the lowercase SHA-256 digest of the UTF-8 JSON
232
+ serialization of `[branchHash, title, body, options]`, with each option,
233
+ including reserved X, represented as `[key, text, tag-or-empty-string]`, where `branchHash` is
234
+ the current page file's branch hash; stores that hash in the question,
235
+ adds the current ISO 8601 time as `createdAt`, appends it to
236
+ `idd/questions.json`, prints the id to stdout and exits 0. A legacy
237
+ question whose id predates X being included retains its id; the checker
238
+ accepts that prior formula, and duplicate detection compares a legacy
239
+ question after adding X for display so the same question cannot be added
240
+ again under a different id.
241
+ * {#s-hbdizs <arch} **Only the end changes.** `add` leaves every question the file
242
+ held, and their order, as it was.
243
+ * {#s-zopa6h <arch} **Refused input.** When stdin does not hold such an object,
244
+ `add` writes nothing, prints one line per problem to stderr, each naming
245
+ the member at fault, and exits 2. When the computed id is already in the
246
+ question file, including after normalizing a legacy question by adding the
247
+ reserved X option, it writes nothing, shows the existing question, and exits 2.
248
+ * {#s-yx8tuh <arch} **Files that fail the check.** After stdin passes `add`'s
249
+ input validation, when the check reports any problem in the project's files
250
+ as they are, `add` prints the problems, writes nothing and exits 1.
251
+ * {#s-xrkyna <arch} **No id-only subcommand.** No subcommand prints a question id
252
+ without adding the question: `next-id` is an unknown subcommand.
253
+
254
+ ## archive
255
+
256
+ * {#s-9oyy6g <arch} **`archive <question-id> <replacement-id>`** moves the active
257
+ question named by `question-id` to `idd/history.json` with its complete
258
+ question snapshot, the replacement id and the current ISO 8601 time; it
259
+ removes the question from `idd/questions.json` and leaves its answer and
260
+ pending answer unchanged.
261
+ * {#s-f9mmtf <arch} **Refused input.** It writes nothing and exits 2 when either id
262
+ is invalid, the question is not active, the replacement is not active, both
263
+ ids are equal, or the question already appears in history. It writes nothing
264
+ and exits 1 when the current project files fail `check`.
265
+
266
+ ## answer
267
+
268
+ * {#s-2s3q95 <arch} **`answer <id>`** reads from stdin one JSON object holding
269
+ `sChoice`, `sNote` and `dtm`, in the form of the page's answer document
270
+ (`specs/questions-page.md`, API), and records it in `idd/answers.json` as
271
+ the member `<id>` whose `choice`, `note` and `time` are their values
272
+ (`specs/project-files.md`, The answer file), then exits 0. {#s-akhsa6 <arch} It
273
+ creates the file when it is absent and leaves every other member as it
274
+ was. {#s-19wno0 <arch} It also records the same answer under `<id>` in
275
+ `idd/pending.json`, creating that file when absent, and leaves every other
276
+ pending member as it was. {#s-pos9d5 <arch} Any other
277
+ member of the object read is ignored.
278
+ * {#s-y1y75p <arch} **An accepted answer cannot be replaced.** With the page
279
+ file present, when the answer file
280
+ already holds `<id>`, `answer` writes nothing, prints the accepted answer
281
+ to stderr and exits 2.
282
+ * {#s-o5jdcy <arch} **Refused input.** When `<id>` is the id of no question of the
283
+ question file, stdin does not hold such an object, `sChoice` is neither
284
+ `""` nor a key of that question's options, `sNote` is not a string, or
285
+ `dtm` is not an ISO 8601 time, `answer` writes nothing, prints a line
286
+ naming the problem to stderr and exits 2.
287
+
288
+ ## clear-pending
289
+
290
+ * {#s-09iime <arch} **`clear-pending <id> <time>`** removes `<id>` from
291
+ `idd/pending.json` only when its recorded `time` exactly equals `<time>`;
292
+ when the id is absent or its time differs, it writes nothing and exits 0;
293
+ when the last member is removed, it leaves the file as an empty object.
294
+ * {#s-p66ewo <arch} **Refused input.** When `<id>` is the id of no question or `<time>` is
295
+ not an ISO 8601 time, `clear-pending` writes nothing, prints the problem
296
+ to stderr and exits 2.
297
+
298
+ ## import
299
+
300
+ * {#s-hnxl58 <arch} **`import <page>`** reads `<page>`, an HTML page made from the
301
+ older template, which holds its questions as a JavaScript array literal
302
+ assigned to `const vaQuestion`, and writes them, in the same order, to
303
+ `idd/questions.json` in the form `specs/project-files.md` gives.
304
+ * {#s-vhstia <arch} **Keys.** Each question's `sTitle` and `sBody` become
305
+ `title` and `body`, its `vaOpt` becomes `options`, and each option's
306
+ `sKey`, `sText` and `sTag` become `key`, `text` and `tag`; its id is
307
+ computed from the page file's `branchHash` and converted question fields,
308
+ and `createdAt` is the time the import runs.
309
+ * {#s-tasgnp <arch} **Bodies become text.** An imported body is
310
+ converted from HTML to text: each `<code>…</code>` becomes the same text
311
+ between backticks, each `<br>` and each paragraph break becomes a blank
312
+ line, every other tag is removed and its text kept, and every character
313
+ reference, as `&lt;`, becomes its character.
314
+ * {#s-yl6bcx <arch} **Titles, option texts and tags stay text.** An imported title,
315
+ option text or tag is not converted from HTML: no tag in it is removed and
316
+ no backtick or blank line is added, so a title `<analyze> reading a
317
+ pop-up` imports as `<analyze> reading a pop-up`.
318
+ * {#s-irxy7e <arch} **Their character references are decoded.** In an imported
319
+ title, option text or tag, each `&lt;`, `&gt;`, `&quot;` and `&amp;`, and
320
+ each numeric character reference, decimal (`&#60;`) or hexadecimal
321
+ (`&#x3c;`), becomes its character, decoded once, so `&amp;lt;` becomes
322
+ `&lt;`; every other character, other named references among them, is kept
323
+ as written.
324
+ * {#s-zmh8z6 <arch} **It never overwrites questions.** When `idd/questions.json`
325
+ exists and holds any question, `import` writes nothing, prints a line
326
+ naming the file to stderr and exits 2.
327
+ * {#s-51kf4i <arch} **An unreadable page.** When `<page>` cannot be read or holds
328
+ no `vaQuestion` array literal it can read, `import` writes nothing, prints
329
+ a line naming the page and the problem to stderr and exits 2.
330
+
331
+ ## Concurrent runs
332
+
333
+ * {#s-lzdklu <m-edvy4l@54198de} **Concurrent adds preserve distinct questions.** Any number of
334
+ `add` runs started at once on one project preserve every distinct
335
+ question they add, each under its computed id; identical questions share
336
+ an id and only one is stored.
337
+ * {#s-vhcbdr <m-edvy4l@54198de} **No answer is lost.** Any number of `answer` runs started at
338
+ once on one project for different ids leave every one of their answers in
339
+ the answer file.
340
+ * {#s-z8braj <arch} **One step from read to write.** Every subcommand that writes a
341
+ project file makes its decision on the files as they are at the moment it
342
+ writes: no other run writes a project file between the read it decides on
343
+ and its write.
344
+ * {#s-cals8z <arch} **No half-written file.** A run that reads a project file while
345
+ another run writes it reads the whole file as it was either before or
346
+ after that write.
347
+ * {#s-4y820y <arch} **Consistent checks and builds.** `check` and `build` take the project
348
+ lock while reading the project files, so they do not see the interval
349
+ between `answer` writing the answer file and its pending answer file.
350
+ * {#s-mm6l60 <arch} **Waiting for another run.** A run that must wait for another
351
+ run's write waits for it; when it has waited 10 seconds, it writes
352
+ nothing, prints a line to stderr saying the project's files are held by
353
+ another run, and exits 2. {#s-qt9vvj <arch} A run that ended, however it ended,
354
+ never makes a later run wait.
355
+ * {#s-zb5ycj <arch} **Nothing left behind.** Once a run has exited, the only files it
356
+ has created or changed are the project files it wrote and files under
357
+ `tmp/idd/`.
358
+ * {#s-rueqkl <arch} **One written form.** Every project file a subcommand writes is
359
+ JSON indented two spaces per level, a member written `"name": value`, and
360
+ ends with a newline, so that the full question digest appears as its `id`
361
+ in `idd/questions.json`.
362
+
363
+ ## Dependencies
364
+
365
+ * {#p-tc5ji4 >specs/project-files.md} The files it creates, checks, reads and
366
+ records answers in.
367
+ * {#p-88dl9g >specs/questions-page.md} The page it builds.
@@ -0,0 +1,198 @@
1
+ # Project files
2
+
3
+ Statement ids: complete
4
+
5
+ ## Purpose
6
+
7
+ The files on an adopting project's matching IDD branch that hold its shared
8
+ questions, answers, pending answers and work claims, so sessions of any agent
9
+ tool work from the same question list and recorded results, while each tool
10
+ presents its own questionnaire.
11
+
12
+ ## His words
13
+
14
+ > {#m-ec324x} RULING (CEO), 2026-11-10: “There should always be an
15
+ > answer (the last one) which is: X. None of the above are right. Let me
16
+ > explain: Also, by default, none of the answers should be selected.”
17
+
18
+ > {#m-q2caxw} RULING (CEO), 2026-11-12, IDD question
19
+ > `08a76cb8364300fab2aaf8345ba237d56cb66cafded6dc4143a7978193dc0fd7`:
20
+ > “Should the question id hash include the reserved X option? A) Exclude it,
21
+ > because it is added to older questions. B) Include it, because it can be
22
+ > chosen.” His answer: “B”. His note: “Since it can be chosen, it should be
23
+ > in the hash.”
24
+
25
+ > {#m-tu5fn2} From his instructions file `~/.claude/CLAUDE.md`, section
26
+ > "Questions for me go on a web questions page", as read 2026-11-04: "One
27
+ > page per session or project. Start from
28
+ > `~/.claude/templates/questions-page.html`. Copy it to the scratchpad,
29
+ > replace `__TITLE__` with "<Project> Open Questions", and replace the
30
+ > placeholder entry in `vaQuestion` with real questions."
31
+
32
+ > {#m-90lcy5} From the same section: "Each question is complete on its own.
33
+ > Give a short title, then a body that says what I see, where and when, with
34
+ > a concrete example, then 2–4 options as `{ sKey, sText }`."
35
+
36
+ > {#m-polrzx} RULING (CEO) 2026-11-02 (Concord), on `concord.json`: "That is
37
+ > weird. Concord.json should not use semantic hungarian tags. SH is really
38
+ > limited to code."
39
+
40
+ > {#m-edvy4l} RULING (CEO) 2026-11-04, q197. The question: "IDD: what do agent
41
+ > tools other than Claude Code do? The new IDD package (interrogation-driven
42
+ > development, in ~/@idd) puts every decision on a questions page like this
43
+ > one. The page is a claude.ai artifact, so a Codex session cannot publish it
44
+ > or read your answers. Example: a Codex session in AIOS needs to know
45
+ > whether a new button saves on click or on Enter.
46
+ > A) Codex writes the question into the project's question file and also
47
+ > asks it in full in the terminal; it appears on the page the next time a
48
+ > Claude Code session publishes it.
49
+ > B) Codex asks in the terminal only, by the same writing rules, and records
50
+ > your answer in the specs.
51
+ > C) IDD gets a local page any tool can serve, with answers saved to a file
52
+ > in the project; the claude.ai page is retired.
53
+ > D) IDD is for Claude Code sessions only, for now." His answer, choosing no
54
+ > option, with the note: "Each agent gets its own tool for interactive
55
+ > questionnaires. The results should be captured in a local file so that
56
+ > multiple agents can expand off of the same question list. For now, define
57
+ > the claude approach, and leave open how other tools will solve it for
58
+ > themselves (e.g. Codex, Gemini, etc.)"
59
+
60
+ ## Terms
61
+
62
+ * {#d-wczec5 <m-tu5fn2@79447f9} **Page file** — `idd/page.json` at the root of an
63
+ adopting project's data worktree: the page's title and branch hash.
64
+ * {#d-q6pb2e <m-tu5fn2@79447f9} **Question file** — `idd/questions.json` at the root
65
+ of that data worktree: every question presented with an agent tool's
66
+ questionnaire.
67
+ * {#d-b4np1k <m-edvy4l@54198de} **Answer file** — `idd/answers.json` at the root of
68
+ that data worktree: the human's recorded answers.
69
+ * {#d-z044ah <arch} **Pending answer file** — `idd/pending.json` at the root of
70
+ that data worktree: answers a session has not yet incorporated.
71
+ ## Shared by every agent tool
72
+
73
+ * {#g-f1rxwm <m-edvy4l@54198de for=main,architect via=prompt} **One question list.** A
74
+ session of any agent tool takes its question ids from, and adds its
75
+ questions to, the project's one question file, and records and reads the
76
+ human's answers in the project's answer file; no agent tool keeps a
77
+ question list or an answer record of its own.
78
+ * {#g-7pv35v <m-edvy4l@54198de,m-zbztp6@c44613a for=main,architect via=prompt} **Each tool its own
79
+ questionnaire.** How an agent tool puts the questions before the human and
80
+ collects his answers is that tool's own, and is specified in that tool's
81
+ guidance spec under `specs/guidance/`. IDD specifies Claude Code's
82
+ (`specs/guidance/claude-code.md`) and Codex's
83
+ (`specs/guidance/codex.md`).
84
+
85
+ ## The page file
86
+
87
+ * {#s-f6ji64 <m-tu5fn2@79447f9,m-polrzx@d3b1d54} **Its members.** The page file holds one JSON
88
+ object with exactly three members: `title`, a non-empty string, as in
89
+ `"title": "AIOS Open Questions"`; `url`, the address the page is
90
+ published at, a non-empty string, or `null` while the page has never been
91
+ published; and `branchHash`, a lowercase, 64-character SHA-256 digest
92
+ identifying the source code branch whose questionnaire it holds.
93
+
94
+ ## The question file
95
+
96
+ * {#s-rhzch5 <m-tu5fn2@79447f9,m-polrzx@d3b1d54} **An array.** The question file holds one JSON
97
+ array of questions, in the order they were presented to the human.
98
+ * {#s-0yj34e <m-90lcy5@5b4a86e,m-polrzx@d3b1d54} **A question.** Each question is an object with
99
+ exactly the members `id`, `branchHash`, `title`, `body`, `options` and
100
+ `createdAt`. {#s-bjouc5 <arch,m-ec324x@c9504b3,m-q2caxw@e07030c} `id` is the lowercase,
101
+ 64-character SHA-256 digest of the UTF-8 JSON serialization of
102
+ `[branchHash, title, body, options]`, with every option represented as
103
+ `[key, text, tag-or-empty-string]`, including the reserved final X option;
104
+ `branchHash` is the hash of the code branch where the question was first
105
+ created. A legacy question whose id predates including X retains its stored
106
+ id and is accepted under the prior formula; adding X for display does not
107
+ rewrite its identity. New questions with the same canonical content and
108
+ branch hash share one id, preventing duplicate questions. `createdAt` is
109
+ the ISO 8601 time the question was added.
110
+ {#s-s0obkh <m-90lcy5@5b4a86e} `title` and
111
+ `body` are non-empty strings. {#s-twjma0 <m-90lcy5@5b4a86e} `options` is an array of
112
+ two to four substantive options, optionally followed by the reserved final
113
+ option `{"key":"X","text":"None of the above are right. Let me explain:"}`.
114
+ {#s-wznig4 <m-ec324x@c9504b3,m-q2caxw@e07030c} A newly added question stores this reserved option
115
+ last; a legacy question without it remains valid and the viewer supplies it
116
+ when displaying the question. No other option may use key `X`.
117
+ * {#s-0yp4n2 <m-90lcy5@5b4a86e,m-polrzx@d3b1d54} **An option.** Each option is an object with the
118
+ members `key`, a non-empty string no other option of its question holds,
119
+ and `text`, a non-empty string, and optionally `tag`, a non-empty string
120
+ drawn under the option's text; it has no other member.
121
+ * {#s-fvhi3x <arch} **Bodies are text.** A body is plain text: backticks mark code
122
+ spans and a blank line separates paragraphs; HTML in a body is text, never
123
+ markup (`specs/questions-page.md`, What the page draws).
124
+
125
+ ## The answer file
126
+
127
+ * {#s-hri503 <arch} **Its members.** The answer file holds one JSON object. Each
128
+ member's name is the id of a question in the active question file or
129
+ question history, and its value
130
+ is an object with exactly the members `choice`, the key of the option he
131
+ chose, a string, or `""` when he chose none; `note`, his note, a string,
132
+ `""` when he wrote none; and `time`, the time he saved the answer, an ISO
133
+ 8601 string.
134
+ * {#s-zrui8r <arch} **One accepted answer per question.** The answer file holds at
135
+ most one accepted answer per question. An accepted answer is never
136
+ replaced; a later submission is rejected and its submitter is shown the
137
+ accepted answer (`specs/idd-page.md`, answer; `specs/hosted-questionnaire.md`).
138
+ * {#s-r40lfj <arch} **Absent means none recorded.** A project with no answer file
139
+ has no recorded answer; the first answer recorded creates it.
140
+
141
+ ## The question history file
142
+
143
+ * {#s-g7mf6n <arch} `idd/history.json` holds a JSON array. Each entry has exactly
144
+ `questionId`, a full `question` snapshot of the removed question,
145
+ `replacedBy`, the id of its replacement, and `time`, the ISO 8601 time
146
+ it was removed from the active list. The snapshot's `id` equals
147
+ `questionId`.
148
+ * {#s-0fiha8 <arch} An answer to a question in history remains in
149
+ `idd/answers.json` under its original `questionId`; it is not deleted or
150
+ rewritten during reconciliation.
151
+
152
+ ## The pending answer file
153
+
154
+ * {#s-0ra8q5 <arch} **Its members.** The pending answer file holds one JSON object. Each
155
+ member's name is the id of a question in the active question file or
156
+ question history, and its value
157
+ has the same `choice`, `note` and `time` members and forms as an answer
158
+ file entry.
159
+ * {#s-ejxfi4 <arch} **A pending answer is recorded.** Each member of the pending answer file
160
+ has an entry with the same id, choice, note and time in the answer file.
161
+ * {#s-u52jq6 <arch} **Absent means none pending.** A project with no pending answer file has
162
+ no locally pending answer; recording the first answer creates it.
163
+
164
+ ## Work claims
165
+
166
+ * {#s-8ffele <arch} `idd/claims.json` holds one JSON object keyed by
167
+ `<kind>:<item-id>`. Each value has exactly `sessionId`, the owning agent
168
+ session id, and `claimedAt`, an ISO 8601 time. Kinds are `answer`, `thought`
169
+ and `conflict`; the item id is the stable 64-character id from its source
170
+ file.
171
+ * {#s-upxu0m <arch} If `idd/claims.json` is absent, no work item is claimed;
172
+ the first claim creates it.
173
+
174
+ ## Thoughts and observations
175
+
176
+ * {#s-61lmfb <arch} `idd/thoughts.json` holds one JSON array of thoughts in append
177
+ order. A thought has exactly `id`, `branchHash`, `text`, and `createdAt`.
178
+ `id` is a lowercase, 64-character SHA-256 digest of the UTF-8 JSON
179
+ serialization of `[branchHash, createdAt, text]`. `branchHash` identifies
180
+ the code branch, `text` is the non-empty plain-text statement, and
181
+ `createdAt` is its unique ISO 8601 submission time. Thoughts are separate from questions,
182
+ answers, conflicts and their counts.
183
+ * {#s-bv05e2 <arch} A thought is immutable after submission. Repeating identical text
184
+ creates a distinct thought with a distinct id; appending never replaces or
185
+ merges an existing thought.
186
+ * {#s-axz9ts <arch} If `idd/thoughts.json` is absent, the project has no submitted
187
+ thoughts. The first submission creates it.
188
+
189
+ ## Where the built page goes
190
+
191
+ * {#s-9e2f7d <arch} **Built under `tmp/`.** `idd build` writes the page to
192
+ `tmp/idd/page.html` under the data worktree root; it is the file the session
193
+ publishes, and is never committed.
194
+
195
+ ## Dependencies
196
+
197
+ * {#p-8khzfb >specs/idd-page.md} The command that creates, checks and builds from
198
+ these files.