tickmarkr 2.1.1 → 2.1.2

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.
@@ -105,7 +105,7 @@ Outside multi-agent environments, run the loop directly.
105
105
 
106
106
  ### Version preflight
107
107
 
108
- Before \`tickmarkr compile\` or \`tickmarkr run\`: run \`tickmarkr version\`, read \`package.json\` version, and if the binary is older on major.minor, stop and tell the operator to update. Never proceed on hope — stale binaries silently skip daemon gates. Also verify no run is live before starting one: \`pgrep -f "tickmarkr (run|resume)"\` must be empty — match the process, not one install path (\`dist/cli/index.js\` alone misses global and homebrew installs), and treat a held \`.tickmarkr/graph.lock\` as a live run until its holder pid is proven dead.
108
+ Before \`tickmarkr compile\` or \`tickmarkr run\`: run \`tickmarkr version\`, read \`package.json\` version, and stop if the versions differ anywhere (major, minor, or patch); the binary and repository must agree on the entire version, so binary \`2.1.0\` versus repository \`2.1.1\` is a stop. Tell the operator to update or link the correct binary. Never proceed on hope — stale binaries silently skip daemon gates. Also verify no run is live in this repository before starting one: lead with this repository's \`.tickmarkr/graph.lock\`, read its holder pid, and treat it as live until \`kill -0 <pid>\` proves that holder dead. Never require a machine-wide process pattern to be empty: a lawful run in another repository — or the probing shell's own argv — can match. If you use a process probe as secondary evidence, exclude the probing process, resolve every candidate's own cwd (for example with \`lsof -a -p <pid> -d cwd\`), and count only candidates whose cwd is this repository root.
109
109
 
110
110
  ### Tip-verify-before-green
111
111
 
@@ -104,7 +104,15 @@ export async function verify(argv, cwd = process.cwd()) {
104
104
  }
105
105
  const wantAcceptance = acceptance.length > 0 && !values["no-acceptance"];
106
106
  const wantReview = !values["no-review"];
107
- const gates = GATE_NAMES.filter((g) => (g !== "acceptance" || wantAcceptance) && (g !== "review" || wantReview));
107
+ // A gate that enforced nothing must not print a green row. `files` has exactly three sources —
108
+ // explicit --files, a compiled task's own files[], or nothing — and only the third leaves the
109
+ // allowlist empty, where scopeGate passes as "no file scope declared — unrestricted"
110
+ // (scope.ts:41). An honest `details` string is no defence: the ROW is what gets quoted, and
111
+ // quoting it launders a check that never ran. So scope filters on availability exactly as
112
+ // acceptance and review do below — the report omits the gate rather than crediting one that
113
+ // gated no allowlist. (Narrowing the empty allowlist to the changed set is the same green row by
114
+ // another mechanism, and hides the same fact.)
115
+ const gates = GATE_NAMES.filter((g) => (g !== "acceptance" || wantAcceptance) && (g !== "review" || wantReview) && (g !== "scope" || files.length > 0));
108
116
  const task = {
109
117
  id: "VERIFY", title: "standalone verification", goal, shape: "implement", complexity: 5,
110
118
  deps: [], files, context: [], acceptance: acceptance.length ? acceptance : ["(deterministic verification only)"],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "tickmarkr",
3
- "version": "2.1.1",
3
+ "version": "2.1.2",
4
4
  "description": "Spec in, verified work out.",
5
5
  "type": "module",
6
6
  "license": "MIT",
@@ -45,10 +45,27 @@ Before `tickmarkr compile` or `tickmarkr run`, compare the installed binary agai
45
45
 
46
46
  1. Run `tickmarkr version` (one line, machine-parseable).
47
47
  2. Read the `version` field from the repository's `package.json`.
48
- 3. If the binary is **older on major.minor** than the repo (e.g. binary `1.36.x` vs repo `1.38.x`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`) or link the repo binary. Do not compile, plan, or run on hope.
48
+ 3. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`) or link the repo binary. Do not compile, plan, or run on hope.
49
49
 
50
50
  A stale binary silently skips daemon gates shipped in newer releases — the v1.38 run exposed this when a global `1.36.0` binary missed the daemon tip-verify gate entirely (OBS-38). Preflight failure is always stop-and-report; never proceed-and-hope.
51
51
 
52
+ ### No run may be live in THIS repository
53
+
54
+ The version check above is only half the preflight. Before `compile` or `run`, confirm no run is already
55
+ live **in this repository**:
56
+
57
+ 1. Lead with this repository's own `.tickmarkr/graph.lock`. Read its recorded holder pid.
58
+ 2. Treat the lock as held by a LIVE run until `kill -0 <pid>` proves that holder dead.
59
+ 3. **Never require a machine-wide process pattern to be empty.** A lawful run in another repository — or
60
+ the probing shell's own argv — matches such a pattern, so an empty result is not evidence of safety
61
+ and a non-empty one is not evidence of danger.
62
+ 4. If you use a process probe as secondary evidence, exclude the probing process itself, resolve every
63
+ candidate's own working directory (for example `lsof -a -p <pid> -d cwd`), and count only candidates
64
+ whose cwd is **this repository root**.
65
+
66
+ The invariant this protects is per-repository — *never run two tickmarkr runs in the same repository
67
+ concurrently* — so a machine-wide check answers a question nobody asked and blocks work that is lawful.
68
+
52
69
  ## Verified handoffs (agent-to-agent messaging)
53
70
 
54
71
  When relaying missions between agents in a multi-agent terminal, **never use bare send-text** (`herdr agent send` / pane send-text) — it writes text without pressing Enter, so handoffs sit unsubmitted (OBS-39).
@@ -40,10 +40,27 @@ Before `tickmarkr compile` or `tickmarkr run`, compare the installed binary agai
40
40
 
41
41
  1. Run `tickmarkr version` (one line, machine-parseable).
42
42
  2. Read the `version` field from the repository's `package.json`.
43
- 3. If the binary is **older on major.minor** than the repo (e.g. binary `1.36.x` vs repo `1.38.x`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`) or link the repo binary. Do not compile, plan, or run on hope.
43
+ 3. If the binary and repository do not **agree on the entire version** (including the patch; e.g. binary `2.1.0` vs repo `2.1.1`), **stop immediately** and tell the operator to update the global install (`npm i -g tickmarkr@latest`) or link the repo binary. Do not compile, plan, or run on hope.
44
44
 
45
45
  A stale binary silently skips daemon gates shipped in newer releases — the v1.38 run exposed this when a global `1.36.0` binary missed the daemon tip-verify gate entirely (OBS-38). Preflight failure is always stop-and-report; never proceed-and-hope.
46
46
 
47
+ ### No run may be live in THIS repository
48
+
49
+ The version check above is only half the preflight. Before `compile` or `run`, confirm no run is already
50
+ live **in this repository**:
51
+
52
+ 1. Lead with this repository's own `.tickmarkr/graph.lock`. Read its recorded holder pid.
53
+ 2. Treat the lock as held by a LIVE run until `kill -0 <pid>` proves that holder dead.
54
+ 3. **Never require a machine-wide process pattern to be empty.** A lawful run in another repository — or
55
+ the probing shell's own argv — matches such a pattern, so an empty result is not evidence of safety
56
+ and a non-empty one is not evidence of danger.
57
+ 4. If you use a process probe as secondary evidence, exclude the probing process itself, resolve every
58
+ candidate's own working directory (for example `lsof -a -p <pid> -d cwd`), and count only candidates
59
+ whose cwd is **this repository root**.
60
+
61
+ The invariant this protects is per-repository — *never run two tickmarkr runs in the same repository
62
+ concurrently* — so a machine-wide check answers a question nobody asked and blocks work that is lawful.
63
+
47
64
  ## Verified handoffs (agent-to-agent messaging)
48
65
 
49
66
  When relaying missions between agents in a multi-agent terminal, **never use bare send-text** (`herdr agent send` / pane send-text) — it writes text without pressing Enter, so handoffs sit unsubmitted (OBS-39).
@@ -35,6 +35,16 @@ through brief lineage. **An executor choice nobody made is still an executor cho
35
35
  status, and either ADOPT the
36
36
  existing orchestrator (updated brief, re-armed watchers) or, if the old hierarchy is dead, archive the
37
37
  stale brief and build fresh.
38
+ ⚠ **VERIFY EVERY INHERITED WATCHER FROM THE PROCESS TABLE BEFORE YOU TRUST IT — re-arming your own
39
+ watchers is NOT enough, and a seat told only to re-arm its own is told the wrong thing.** An inherited
40
+ *"watcher armed"* line is a claim, not a watcher: it is a report by a seat that no longer exists, which
41
+ is strictly WEAKER than the live seat's report rule 11 already forbids trusting — and it reads as
42
+ settled fact. So at every adopt, walk the predecessor's watchers by class — **journal watchers,
43
+ artifact watchers, dialog watchers and beat loops, which is the closed set a session owns** — probe
44
+ each from the process table yourself (`pgrep -f <token>`, discriminated per rule 11), and re-arm every
45
+ one the table does not show. Earned 2026-08-25 (OBS-622): a handoff recorded *"artifact watcher armed"*
46
+ over two live consult verdicts; at adopt the only `watch-artifacts.sh` on the machine belonged to a
47
+ different repository, and nothing had been watching either file.
38
48
  **An adopted seat ANNOUNCES itself, in the same act as re-arming:** tell the adopted orchestrator the
39
49
  fresh seat is live (verified send: probe token + read-back). Through the gap its view of your tier read
40
50
  STALE, and a tier that believes it is unsupervised escalates into a file nobody is reading. Earned
@@ -550,6 +560,15 @@ that hang is byte-identical to a seat still working. The script prints each unfi
550
560
  last line on every timeout heartbeat — read it there, and when in doubt `tail -1` the artifact, never
551
561
  the transcript's claim about it.
552
562
 
563
+ **An ABSENT artifact ALONE cannot discriminate a working producer from a dead watcher.** A producer still
564
+ working and a watcher that died with its seat write byte-identical evidence — nothing — so a missing file
565
+ is one signal carrying at least three meanings (still working, watcher dead, producer dead), and it is
566
+ **never** evidence that the watcher is still waiting. Reading it that way infers an instrument's liveness
567
+ from the silence it was built to sit through. Settle it with two probes that do not share a failure:
568
+ the watcher from the process table, the producer from its pane or seat status. Measured 2026-08-25
569
+ (OBS-622): both consultants were live and had written nothing, so the artifact side could not see that
570
+ nothing was watching them.
571
+
553
572
  **Arm it in the same call as the spawn, not the next one.** A watcher armed "after I finish this step"
554
573
  leaves a gap exactly as wide as however long you stay busy, and you will be busy — you just spawned work.
555
574
  **Measured 2026-08-06 (OBS-369): two consult verdicts, 30KB and 12.8KB, sat COMPLETE with their markers
@@ -677,6 +696,50 @@ orchestrator turn boundary.
677
696
  fix helps ONE operator and leaves every other user with the defect. If an overlay is the interim, it
678
697
  says so in writing and names its removal condition.
679
698
 
699
+ 8. **A SHIPPED VERSION IS NOT DONE UNTIL THE STATE IT LEAVES BEHIND IS CLEAN.** Publishing is the loud
700
+ half; the quiet half is that the NEXT seat inherits either the truth or a confident lie. Run this
701
+ before you stand down from any release — **operator instruction, 2026-08-25: *"overseer should always
702
+ leave clean state after a version is shipped"***. Every line below is a defect that actually happened
703
+ on the release that produced this rule.
704
+
705
+ - **REWRITE THE MEMORY INDEX FIRST, and read it back.** Minutes after `2.1.1` hit npm, the index line
706
+ a fresh session loads still read *"⛔ 2.1.1 CANNOT ship from run …2011"* — true when written, and by
707
+ then the exact opposite of the truth. **The index is what everyone loads and the body is what nobody
708
+ opens** (Evidence rule 25), so a stale index is not a cosmetic lag; it is the most-read wrong
709
+ sentence in the project. State what shipped, what did NOT, and the first three things the next seat
710
+ should do.
711
+ - **VERIFY EVERY ID THE CODE NOW CITES ACTUALLY EXISTS.** A release lands source comments citing
712
+ ledger ids. One of that release's entries was written by a heredoc in a command that then timed out
713
+ — the entry survived, but nothing had checked. `grep -c '^## OBS-<id>'` for each id the diff
714
+ introduced. A citation pointing at nothing is the defect the ledger itself files (OBS-604), shipped
715
+ into `src/`.
716
+ - **KILL THE BEAT LOOP *AND* RUN `--stand-down`.** Either alone is worse than neither: the loop
717
+ without the stand-down re-arms a tier you retired within 10s, and the stand-down without the loop
718
+ is undone by the next tick. Verify `status` reads `DISARMED` — which means *handed off*, distinct
719
+ from `STALE` (armed then died) and `ABSENT` (never armed).
720
+ - **RECORD YOUR WATCHERS AS DYING WITH THIS SESSION — never as "armed".** A written stand-down or
721
+ handoff may NOT carry the bare wording *"watcher armed"* for anything this seat owns: that form
722
+ states an act and lets the successor read a fact, and it survived into a handoff exactly once before
723
+ costing two unwatched consult verdicts (OBS-622). The admissible form names the lifetime and the
724
+ work it leaves the successor — *"watchers armed by this session (journal, artifact, dialog, beat);
725
+ they die with it — re-arm on adopt"* — and, per rule 11, says which tier's watchers were NOT armed.
726
+ A detached watcher is the one exception and must be labelled as such, with its heartbeat file, since
727
+ it outlives the seat instead.
728
+ - **SWEEP THE PANES THE RUN LEFT.** A daemon killed by a signal flushes its journal and releases its
729
+ lock but **does not clean up its worker panes or its board**. Two orphaned worker panes and a dead
730
+ board pane sat in the operator's tab bar until he screenshotted them. Verify each is inert first
731
+ (no agent, nothing running in its worktree) and confirm the WORK is on its branch — then close.
732
+ Emptied tabs disappear on their own.
733
+ - **CORRECT EVERY TAB LABEL.** `ORCH · 2.1.1 T1 regate` was still on screen hours after that regate
734
+ ended. Tab labels are how the operator reads fleet state; a stale one is a false status report.
735
+ - **LEAVE THE TREE CLEAN AND SAY WHAT IS UNMERGED.** Name the branches that hold real but ungated
736
+ work, so the next seat neither discards nor trusts them. *"Zero merges, T1/T3 branches ungated, T5
737
+ never dispatched"* is a handoff; *"the run ended"* is not.
738
+
739
+ ⚠ **The half of this that is NOT operator discipline must be QUEUED, not absorbed:** a daemon that
740
+ orphans its panes on SIGTERM is a PRODUCT defect and belongs in `src/**`. Sweeping by hand every time
741
+ is the local remedy, and per rule 7 it says so in writing and names its removal condition.
742
+
680
743
  ---
681
744
 
682
745
  ## Briefing a seat to audit a security-shaped check — phrasing matters
@@ -787,6 +850,14 @@ twice.** They are mission-independent on purpose: nothing here names a task, a l
787
850
  owns it** — "watchers alive" is the one claim a seat cannot verify about itself. Measured 2026-08-06:
788
851
  an orchestrator sat `idle` through three merges and two dispatches with no journal watcher in the
789
852
  process table, while its own last report read *"daemon, board, sweeper, watcher all alive"* (OBS-366).
853
+ **STATE THE LIFETIME, because an unstated one is read as the mission's: a session-scoped watcher DIES
854
+ WITH THE SEAT THAT ARMED IT.** Every watcher a seat arms — journal, artifact, dialog, beat loop — is
855
+ session-scoped unless it was deliberately detached (`ppid 1`, the heartbeat form below), so `/clear`,
856
+ a crash, an adopt or a stand-down ends it, and **a handoff is the one moment the arming seat stops
857
+ existing** — which is exactly when its watchers are most likely to be believed. The inverse failure is
858
+ the same root read the other way: a DETACHED loop outlives its seat and holds a tier `ARMED` with
859
+ nobody home (OBS-583). Neither direction may be assumed; the lifetime is a property of how the watcher
860
+ was launched, and it belongs in writing next to every claim that one is armed.
790
861
  **And the process-table probe has a standard idiom that DEFEATS it, so the rule above needs one more
791
862
  line to be usable.** Never probe for a watcher with `ps … | grep <token> | grep -v grep`: a poll-grep
792
863
  watcher carries the word `grep` in its own argv, so the filter whose job is removing the *probing* grep