4 Commits
8.1.1 ... main

Author SHA1 Message Date
4dad213752 devdocs - CLAUDE.md pointing to the AyaNova name map
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:39:26 -07:00
03ff2a2626 devdocs - keep devdocs scripts in LF
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:39:26 -07:00
712d0c891f devdocs - AyaNova/Sockeye name map and compare script
NAME-MAP.md lists the names Sockeye changed from AyaNova (types, properties,
database columns, config keys, client files), the names left alone, and the
files that exist in only one codebase. compare-with-raven.sh maps the names
and diffs the shared server files so only real code differences show.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-27 10:37:16 -07:00
e28dc48bd0 case 4682 - automatic backup runs at the chosen time on the server's clock all year
Same fix as AyaNova: the backup ran at the stored UTC hour, so with daylight
saving it moved an hour on the server's clock for part of the year.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-10-26 09:10:00 -07:00
5 changed files with 146 additions and 4 deletions

1
.gitattributes vendored Normal file
View File

@@ -0,0 +1 @@
devdocs/*.sh text eol=lf

8
CLAUDE.md Normal file
View File

@@ -0,0 +1,8 @@
# Sockeye — instructions for Claude Code
Sockeye is the internal licensing server for AyaNova. Its server began as a copy of AyaNova's
(`..\raven`), with some names changed: `devdocs/NAME-MAP.md` lists them, and
`devdocs/compare-with-raven.sh` compares the shared files with the names mapped. For a low-level
server fix in either codebase, check whether the other needs it too, and record the answer.
The working rules in `..\raven\CLAUDE.md` apply here as well.

81
devdocs/NAME-MAP.md Normal file
View File

@@ -0,0 +1,81 @@
# AyaNova ↔ Sockeye name map
Sockeye's server began as a copy of AyaNova's (`raven/server/AyaNova`). Some names were
changed for Sockeye; most code is otherwise the same. Use this map to carry a fix from one
codebase to the other. Mapped 2026-10-27, AyaNova `main` (8.3.0) against Sockeye `main` (8.1.x).
`compare-with-raven.sh` (beside this file) applies the map and compares the shared files, so
only real code differences show:
```bash
bash compare-with-raven.sh # every shared file: differing lines, AyaNova lines, path
bash compare-with-raven.sh util/DbUtil.cs # the diff for one file (< AyaNova mapped, > Sockeye)
```
## Renamed
| AyaNova | Sockeye | Where |
|---|---|---|
| namespace `AyaNova.*`, `AyaNova` in text | `Sockeye.*`, `Sockeye` | everywhere |
| `AyaType` (enum) | `SockType` | `biz/AyaType.cs` → `biz/SockType.cs` |
| `AyaTypeId` | `SockTypeId` | `biz/` |
| `AyaEvent` (enum) | `SockEvent` | `biz/` |
| `AyaDaysOfWeek` (enum) | `SockDaysOfWeek` | `biz/` |
| `AyaNovaVersion` | `SockeyeVersion` | `util/` |
| `AyaNovaConnectionString`, `AyaNovaServerURL`, `logRuleAyaNovaItems` | `SockeyeConnectionString`, `SockeyeServerURL`, `logRuleSockeyeItems` | `util/` |
| `AyaTypeTranslated`, `ayaTypeColumnName` | `SockTypeTranslated`, `sockTypeColumnName` | DataList, PickList |
| stored type property `AType` (JSON `aType`) | `SockType` (JSON `sockType`) | `models/`: Attachment, Event, SearchKey, OpsJob, Report, Review, IntegrationItem, DataListSelectedRequest… |
| computed `public AyaType AyaType { get => AyaType.X; }` (JSON `ayaType`) | `public SockType SType { get => SockType.X; }` (JSON `sType`) | business models: Customer, User, Memo, Reminder, Review… |
| locals and parameters `ayaType`, `atype`, `ayId`, `ayaEvent` | `sockType`, `sockId`, `sockEvent` (a few `stype`, `sock`) | everywhere |
| database columns `atype`, `ayatype`, `ayid`, `ayevent` | `socktype`, `sockid`, `sockevent`, and `stype` on business tables | `util/AySchema.cs`, raw SQL in DataList/PickList/biz |
| config keys `AYANOVA_*` | `SOCKEYE_*`, same suffix | `util/ServerBootConfig.cs`, `config.json`, environment |
| client `src/api/ayatype.js`, `ayanova-version.js` | `src/api/socktype.js`, `sockeye-version.js` | `raven-client` ↔ `sockeye-client` |
The shared `AyaType`/`SockType` members have the same numbers (Customer 8, User 3, Report 57…),
and `AyaEvent`/`SockEvent` are identical, so stored values mean the same thing in both.
## Not renamed (same in both)
`AyContext`, `AySchema`, `AyaPickListFieldDefinition`, `AyaPickList`, `AyaField`, `AyImportData`,
`AyFileInfo`, `AyaNovaLicenseKey`; table names (`aevent`, `asearchkey`, `auser`…); routes
(`api/v{version}/…`); the folder layout (`biz`, `ControllerHelpers`, `Controllers`, `DataList`,
`generator`, `kpi`, `models`, `PickList`, `resource`, `util`).
## Only in one codebase
- **AyaNova only:** the service objects (work orders, quotes, PM, parts, inventory, units,
contracts, projects, rates, CSR, loan units, purchase orders, task groups, tax codes), their
KPIs, `License.cs` (licence check, client side), `Seeder.cs`, `ReportApiRelay.cs`,
`CoreJobLicense`, `CoreJobVersionCheck`, `CoreJobPM*`, `SuperUserDefaultPasswordRoutes`,
`CustomerContactRoutes`, and newer config keys (`AYANOVA_TRUSTED_PROXIES`,
`AYANOVA_SMTP_ACCEPT_ANY_CERTIFICATE`, `AYANOVA_REMOVE_LICENSE_FROM_DB`,
`AYANOVA_REPORT_RENDER_API_URL_OVERRIDE`). Also `AyaDayOfWeek` and `AyaUnitsOfTime` (PM).
- **Sockeye only:** licensing and sales (`KeyFactory`, `RavenKeyFactory`, `LicenseBiz`,
products, purchases, subscriptions, subscription servers and their states, trial licence
requests, vendor notifications, `GZCase`), `SockBot*` jobs, `FetchController`,
`RvfController`, `RvrController`, `AutoOrderProcessingUtil`.
## How close the shared files are (2026-10-27)
288 server files exist in both. After mapping, 156 are identical, about 55 differ by 1–10
lines, and the rest differ more, mostly because one codebase has objects the other lacks.
Low-level files that differ by more than the names, worth checking when a fix touches them:
| File | Differing lines | Main reason |
|---|---|---|
| `util/RunProgram.cs` | most | AyaNova has `RunNoShell` (backup without a shell, 8.3.0); Sockeye still has the old `Run` |
| `generator/CoreJobBackup.cs` | ~50 | same: Sockeye still passes the password in the `pg_dump` URI through a shell |
| `util/DbUtil.cs`, `util/AySchema.cs` | many | different tables; check `AdminConnectionString` and drop-and-recreate by hand |
| `DataList/DataListSqlFilterCriteriaBuilder.cs` | ~155 | filter fixes went into each separately; compare before changing either |
| `util/ServerBootConfig.cs`, `Program.cs`, `Startup.cs` | many | config keys and service wiring differ |
| `util/FileUtil.cs`, `util/StringUtil.cs`, `util/DateUtil.cs`, `generator/CoreJobNotify.cs`, `generator/CoreJobSweeper.cs`, `util/Mailer.cs` | 15–90 | partly real drift; check with the script |
Rerun the script for current numbers; this table is a snapshot.
## Carrying a fix across
1. Make and test the fix in one codebase.
2. Run `bash compare-with-raven.sh <file>` on each file touched: if the file matches apart from
names, the same change applies with the names above.
3. Record in both repos whether the fix applies (done, n/a, or deferred). In AyaNova that is
`raven/devdocs/modernization/NET10-PORT.md`.

View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
# Compare Sockeye server source with AyaNova's (raven) after mapping AyaNova's names to
# Sockeye's (see NAME-MAP.md), so only real code differences show.
#
# bash compare-with-raven.sh one line per shared file: differing lines, raven lines, path
# bash compare-with-raven.sh util/DbUtil.cs the diff for one file (raven mapped < > sockeye)
#
# Read-only. Expects the repos side by side under one folder (C:\data\code).
set -euo pipefail
here="$(cd "$(dirname "$0")" && pwd)"
R="$here/../../raven/server/AyaNova"
S="$here/../server"
# AyaNova name -> Sockeye name; order matters (longer names first)
norm() {
sed -E \
-e 's/AyaNovaVersion/SockeyeVersion/g' \
-e 's/AyaNova/Sockeye/g; s/ayanova/sockeye/g; s/AYANOVA_/SOCKEYE_/g' \
-e 's/public AyaType AyaType/public SockType SType/g' \
-e 's/AyaTypeId/SockTypeId/g; s/AyaType/SockType/g; s/ayaType/sockType/g; s/ayatype/socktype/g' \
-e 's/\bAType\b/SockType/g; s/\batype\b/socktype/g' \
-e 's/AyaEvent/SockEvent/g; s/ayaEvent/sockEvent/g; s/ayevent/sockevent/g' \
-e 's/AyaDaysOfWeek/SockDaysOfWeek/g' \
-e 's/\bAyId\b/SockId/g; s/\bayId\b/sockId/g; s/\bayid\b/sockid/g' \
"$1" | tr -d '\r'
}
if [ $# -gt 0 ]; then
diff <(norm "$R/$1") <(tr -d '\r' < "$S/$1") || true
exit 0
fi
(cd "$R" && find . -name '*.cs' -not -path './obj/*' -not -path './bin/*') | sort | while read -r f; do
[ -f "$S/$f" ] || continue
d=$(diff <(norm "$R/$f") <(tr -d '\r' < "$S/$f") | grep -c '^[<>]' || true)
echo "$d $(wc -l < "$R/$f") ${f#./}"
done

View File

@@ -51,22 +51,36 @@ namespace Sockeye.Util
internal static void SetNextBackup() internal static void SetNextBackup()
{ {
DateTime utcNow = DateTime.UtcNow; DateTime utcNow = DateTime.UtcNow;
//case 4682: the backup time is a time on the server's clock; days are the server's local days
DateTime localToday = TimeZoneInfo.ConvertTimeFromUtc(utcNow, TimeZoneInfo.Local).Date;
//Has last backup run more than 24 hours ago? //Has last backup run more than 24 hours ago?
if (NextBackup < utcNow.AddHours(-24)) if (NextBackup < utcNow.AddHours(-24))
{ {
//set it to today then //set it to today then
NextBackup = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, Backup.BackupTime.Hour, Backup.BackupTime.Minute, 0, DateTimeKind.Utc); NextBackup = BackupTimeOnLocalDay(localToday);
//Make sure next backup is in the future //Make sure next backup is in the future
if (NextBackup < utcNow) if (NextBackup < utcNow)
NextBackup = NextBackup.AddDays(1); NextBackup = BackupTimeOnLocalDay(localToday.AddDays(1));
} }
else else
{ {
//less than 24 hours, set it to tomorrow //less than 24 hours, set it to tomorrow
NextBackup = new DateTime(utcNow.Year, utcNow.Month, utcNow.Day, Backup.BackupTime.Hour, Backup.BackupTime.Minute, 0, DateTimeKind.Utc); NextBackup = BackupTimeOnLocalDay(localToday.AddDays(1));
NextBackup = NextBackup.AddDays(1);
} }
} }
//case 4682: the backup time is stored as the UTC moment it meant on the day it was set, and was run at that UTC hour
//every day, so it moved an hour on the server's clock with each daylight saving change. The server's clock time is
//recovered with that day's offset and the backup is run at that clock time on the given local day (UTC returned)
private static DateTime BackupTimeOnLocalDay(DateTime localDay)
{
var chosen = TimeZoneInfo.ConvertTimeFromUtc(DateTime.SpecifyKind(Backup.BackupTime, DateTimeKind.Utc), TimeZoneInfo.Local);
var local = DateTime.SpecifyKind(localDay.Date + new TimeSpan(chosen.Hour, chosen.Minute, 0), DateTimeKind.Unspecified);
//a time skipped when the clocks go forward (e.g. 2:30 AM that night) runs an hour later
if (TimeZoneInfo.Local.IsInvalidTime(local))
local = local.AddHours(1);
return TimeZoneInfo.ConvertTimeToUtc(local, TimeZoneInfo.Local);
}
}//eoc }//eoc
}//eons }//eons